1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 #include "tool_setup.h" 23 24 #include "rawstr.h" 25 26 #define ENABLE_CURLX_PRINTF 27 /* use our own printf() functions */ 28 #include "curlx.h" 29 30 #include "tool_binmode.h" 31 #include "tool_cfgable.h" 32 #include "tool_cb_prg.h" 33 #include "tool_formparse.h" 34 #include "tool_getparam.h" 35 #include "tool_helpers.h" 36 #include "tool_libinfo.h" 37 #include "tool_metalink.h" 38 #include "tool_msgs.h" 39 #include "tool_paramhlp.h" 40 #include "tool_parsecfg.h" 41 42 #include "memdebug.h" /* keep this as LAST include */ 43 44 #ifdef MSDOS 45 # define USE_WATT32 46 #endif 47 48 #define GetStr(str,val) do { \ 49 if(*(str)) { \ 50 free(*(str)); \ 51 *(str) = NULL; \ 52 } \ 53 if((val)) { \ 54 *(str) = strdup((val)); \ 55 if(!(*(str))) \ 56 return PARAM_NO_MEM; \ 57 } \ 58 } WHILE_FALSE 59 60 struct LongShort { 61 const char *letter; /* short name option */ 62 const char *lname; /* long name option */ 63 bool extraparam; /* whether it takes an additional argument */ 64 }; 65 66 static const struct LongShort aliases[]= { 67 /* 'letter' strings with more than one character have *no* short option to 68 mention. */ 69 {"*@", "url", TRUE}, 70 {"*4", "dns-ipv4-addr", TRUE}, 71 {"*6", "dns-ipv6-addr", TRUE}, 72 {"*a", "random-file", TRUE}, 73 {"*b", "egd-file", TRUE}, 74 {"*B", "oauth2-bearer", TRUE}, 75 {"*c", "connect-timeout", TRUE}, 76 {"*d", "ciphers", TRUE}, 77 {"*D", "dns-interface", TRUE}, 78 {"*e", "disable-epsv", FALSE}, 79 {"*E", "epsv", FALSE}, 80 /* 'epsv' made like this to make --no-epsv and --epsv to work 81 although --disable-epsv is the documented option */ 82 #ifdef USE_ENVIRONMENT 83 {"*f", "environment", FALSE}, 84 #endif 85 {"*F", "dns-servers", TRUE}, 86 {"*g", "trace", TRUE}, 87 {"*G", "npn", FALSE}, 88 {"*h", "trace-ascii", TRUE}, 89 {"*H", "alpn", FALSE}, 90 {"*i", "limit-rate", TRUE}, 91 {"*j", "compressed", FALSE}, 92 {"*J", "tr-encoding", FALSE}, 93 {"*k", "digest", FALSE}, 94 {"*l", "negotiate", FALSE}, 95 {"*m", "ntlm", FALSE}, 96 {"*M", "ntlm-wb", FALSE}, 97 {"*n", "basic", FALSE}, 98 {"*o", "anyauth", FALSE}, 99 #ifdef USE_WATT32 100 {"*p", "wdebug", FALSE}, 101 #endif 102 {"*q", "ftp-create-dirs", FALSE}, 103 {"*r", "create-dirs", FALSE}, 104 {"*s", "max-redirs", TRUE}, 105 {"*t", "proxy-ntlm", FALSE}, 106 {"*u", "crlf", FALSE}, 107 {"*v", "stderr", TRUE}, 108 {"*w", "interface", TRUE}, 109 {"*x", "krb", TRUE}, 110 {"*x", "krb4", TRUE}, 111 /* 'krb4' is the previous name */ 112 {"*y", "max-filesize", TRUE}, 113 {"*z", "disable-eprt", FALSE}, 114 {"*Z", "eprt", FALSE}, 115 /* 'eprt' made like this to make --no-eprt and --eprt to work 116 although --disable-eprt is the documented option */ 117 {"*~", "xattr", FALSE}, 118 {"$a", "ftp-ssl", FALSE}, 119 /* 'ftp-ssl' deprecated name since 7.20.0 */ 120 {"$a", "ssl", FALSE}, 121 /* 'ssl' new option name in 7.20.0, previously this was ftp-ssl */ 122 {"$b", "ftp-pasv", FALSE}, 123 {"$c", "socks5", TRUE}, 124 {"$d", "tcp-nodelay", FALSE}, 125 {"$e", "proxy-digest", FALSE}, 126 {"$f", "proxy-basic", FALSE}, 127 {"$g", "retry", TRUE}, 128 {"$h", "retry-delay", TRUE}, 129 {"$i", "retry-max-time", TRUE}, 130 {"$k", "proxy-negotiate", FALSE}, 131 {"$m", "ftp-account", TRUE}, 132 {"$n", "proxy-anyauth", FALSE}, 133 {"$o", "trace-time", FALSE}, 134 {"$p", "ignore-content-length", FALSE}, 135 {"$q", "ftp-skip-pasv-ip", FALSE}, 136 {"$r", "ftp-method", TRUE}, 137 {"$s", "local-port", TRUE}, 138 {"$t", "socks4", TRUE}, 139 {"$T", "socks4a", TRUE}, 140 {"$u", "ftp-alternative-to-user", TRUE}, 141 {"$v", "ftp-ssl-reqd", FALSE}, 142 /* 'ftp-ssl-reqd' deprecated name since 7.20.0 */ 143 {"$v", "ssl-reqd", FALSE}, 144 /* 'ssl-reqd' new in 7.20.0, previously this was ftp-ssl-reqd */ 145 {"$w", "sessionid", FALSE}, 146 /* 'sessionid' listed as --no-sessionid in the help */ 147 {"$x", "ftp-ssl-control", FALSE}, 148 {"$y", "ftp-ssl-ccc", FALSE}, 149 {"$j", "ftp-ssl-ccc-mode", TRUE}, 150 {"$z", "libcurl", TRUE}, 151 {"$#", "raw", FALSE}, 152 {"$0", "post301", FALSE}, 153 {"$1", "keepalive", FALSE}, 154 /* 'keepalive' listed as --no-keepalive in the help */ 155 {"$2", "socks5-hostname", TRUE}, 156 {"$3", "keepalive-time", TRUE}, 157 {"$4", "post302", FALSE}, 158 {"$5", "noproxy", TRUE}, 159 {"$7", "socks5-gssapi-nec", FALSE}, 160 {"$8", "proxy1.0", TRUE}, 161 {"$9", "tftp-blksize", TRUE}, 162 {"$A", "mail-from", TRUE}, 163 {"$B", "mail-rcpt", TRUE}, 164 {"$C", "ftp-pret", FALSE}, 165 {"$D", "proto", TRUE}, 166 {"$E", "proto-redir", TRUE}, 167 {"$F", "resolve", TRUE}, 168 {"$G", "delegation", TRUE}, 169 {"$H", "mail-auth", TRUE}, 170 {"$I", "post303", FALSE}, 171 {"$J", "metalink", FALSE}, 172 {"$K", "sasl-ir", FALSE}, 173 {"$L", "test-event", FALSE}, 174 {"$M", "unix-socket", TRUE}, 175 {"$N", "path-as-is", FALSE}, 176 {"$O", "socks5-gssapi-service", TRUE}, 177 /* 'socks5-gssapi-service' merged with'proxy-service-name' and 178 deprecated since 7.49.0 */ 179 {"$O", "proxy-service-name", TRUE}, 180 {"$P", "service-name", TRUE}, 181 {"$Q", "proto-default", TRUE}, 182 {"$R", "expect100-timeout", TRUE}, 183 {"$S", "tftp-no-options", FALSE}, 184 {"$U", "connect-to", TRUE}, 185 {"0", "http1.0", FALSE}, 186 {"01", "http1.1", FALSE}, 187 {"02", "http2", FALSE}, 188 {"03", "http2-prior-knowledge", FALSE}, 189 {"1", "tlsv1", FALSE}, 190 {"10", "tlsv1.0", FALSE}, 191 {"11", "tlsv1.1", FALSE}, 192 {"12", "tlsv1.2", FALSE}, 193 {"2", "sslv2", FALSE}, 194 {"3", "sslv3", FALSE}, 195 {"4", "ipv4", FALSE}, 196 {"6", "ipv6", FALSE}, 197 {"a", "append", FALSE}, 198 {"A", "user-agent", TRUE}, 199 {"b", "cookie", TRUE}, 200 {"B", "use-ascii", FALSE}, 201 {"c", "cookie-jar", TRUE}, 202 {"C", "continue-at", TRUE}, 203 {"d", "data", TRUE}, 204 {"dr", "data-raw", TRUE}, 205 {"da", "data-ascii", TRUE}, 206 {"db", "data-binary", TRUE}, 207 {"de", "data-urlencode", TRUE}, 208 {"D", "dump-header", TRUE}, 209 {"e", "referer", TRUE}, 210 {"E", "cert", TRUE}, 211 {"Ea", "cacert", TRUE}, 212 {"Eb", "cert-type", TRUE}, 213 {"Ec", "key", TRUE}, 214 {"Ed", "key-type", TRUE}, 215 {"Ee", "pass", TRUE}, 216 {"Ef", "engine", TRUE}, 217 {"Eg", "capath", TRUE}, 218 {"Eh", "pubkey", TRUE}, 219 {"Ei", "hostpubmd5", TRUE}, 220 {"Ej", "crlfile", TRUE}, 221 {"Ek", "tlsuser", TRUE}, 222 {"El", "tlspassword", TRUE}, 223 {"Em", "tlsauthtype", TRUE}, 224 {"En", "ssl-allow-beast", FALSE}, 225 {"Eo", "login-options", TRUE}, 226 {"Ep", "pinnedpubkey", TRUE}, 227 {"Eq", "cert-status", FALSE}, 228 {"Er", "false-start", FALSE}, 229 {"Es", "ssl-no-revoke", FALSE}, 230 {"Et", "tcp-fastopen", FALSE}, 231 {"f", "fail", FALSE}, 232 {"F", "form", TRUE}, 233 {"Fs", "form-string", TRUE}, 234 {"g", "globoff", FALSE}, 235 {"G", "get", FALSE}, 236 {"h", "help", FALSE}, 237 {"H", "header", TRUE}, 238 {"Hp", "proxy-header", TRUE}, 239 {"i", "include", FALSE}, 240 {"I", "head", FALSE}, 241 {"j", "junk-session-cookies", FALSE}, 242 {"J", "remote-header-name", FALSE}, 243 {"k", "insecure", FALSE}, 244 {"K", "config", TRUE}, 245 {"l", "list-only", FALSE}, 246 {"L", "location", FALSE}, 247 {"Lt", "location-trusted", FALSE}, 248 {"m", "max-time", TRUE}, 249 {"M", "manual", FALSE}, 250 {"n", "netrc", FALSE}, 251 {"no", "netrc-optional", FALSE}, 252 {"ne", "netrc-file", TRUE}, 253 {"N", "buffer", FALSE}, 254 /* 'buffer' listed as --no-buffer in the help */ 255 {"o", "output", TRUE}, 256 {"O", "remote-name", FALSE}, 257 {"Oa", "remote-name-all", FALSE}, 258 {"p", "proxytunnel", FALSE}, 259 {"P", "ftp-port", TRUE}, 260 {"q", "disable", FALSE}, 261 {"Q", "quote", TRUE}, 262 {"r", "range", TRUE}, 263 {"R", "remote-time", FALSE}, 264 {"s", "silent", FALSE}, 265 {"S", "show-error", FALSE}, 266 {"t", "telnet-option", TRUE}, 267 {"T", "upload-file", TRUE}, 268 {"u", "user", TRUE}, 269 {"U", "proxy-user", TRUE}, 270 {"v", "verbose", FALSE}, 271 {"V", "version", FALSE}, 272 {"w", "write-out", TRUE}, 273 {"x", "proxy", TRUE}, 274 {"X", "request", TRUE}, 275 {"Y", "speed-limit", TRUE}, 276 {"y", "speed-time", TRUE}, 277 {"z", "time-cond", TRUE}, 278 {"#", "progress-bar", FALSE}, 279 {":", "next", FALSE}, 280 }; 281 282 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon. 283 * We allow ':' and '\' to be escaped by '\' so that we can use certificate 284 * nicknames containing ':'. See <https://sourceforge.net/p/curl/bugs/1196/> 285 * for details. */ 286 #ifndef UNITTESTS 287 static 288 #endif 289 void parse_cert_parameter(const char *cert_parameter, 290 char **certname, 291 char **passphrase) 292 { 293 size_t param_length = strlen(cert_parameter); 294 size_t span; 295 const char *param_place = NULL; 296 char *certname_place = NULL; 297 *certname = NULL; 298 *passphrase = NULL; 299 300 /* most trivial assumption: cert_parameter is empty */ 301 if(param_length == 0) 302 return; 303 304 /* next less trivial: cert_parameter contains no colon nor backslash; this 305 * means no passphrase was given and no characters escaped */ 306 if(!strpbrk(cert_parameter, ":\\")) { 307 *certname = strdup(cert_parameter); 308 return; 309 } 310 /* deal with escaped chars; find unescaped colon if it exists */ 311 certname_place = malloc(param_length + 1); 312 if(!certname_place) 313 return; 314 315 *certname = certname_place; 316 param_place = cert_parameter; 317 while(*param_place) { 318 span = strcspn(param_place, ":\\"); 319 strncpy(certname_place, param_place, span); 320 param_place += span; 321 certname_place += span; 322 /* we just ate all the non-special chars. now we're on either a special 323 * char or the end of the string. */ 324 switch(*param_place) { 325 case '\0': 326 break; 327 case '\\': 328 param_place++; 329 switch(*param_place) { 330 case '\0': 331 *certname_place++ = '\\'; 332 break; 333 case '\\': 334 *certname_place++ = '\\'; 335 param_place++; 336 break; 337 case ':': 338 *certname_place++ = ':'; 339 param_place++; 340 break; 341 default: 342 *certname_place++ = '\\'; 343 *certname_place++ = *param_place; 344 param_place++; 345 break; 346 } 347 break; 348 case ':': 349 /* Since we live in a world of weirdness and confusion, the win32 350 dudes can use : when using drive letters and thus c:\file:password 351 needs to work. In order not to break compatibility, we still use : as 352 separator, but we try to detect when it is used for a file name! On 353 windows. */ 354 #ifdef WIN32 355 if(param_place && 356 (param_place == &cert_parameter[1]) && 357 (cert_parameter[2] == '\\' || cert_parameter[2] == '/') && 358 (ISALPHA(cert_parameter[0])) ) { 359 /* colon in the second column, followed by a backslash, and the 360 first character is an alphabetic letter: 361 362 this is a drive letter colon */ 363 *certname_place++ = ':'; 364 param_place++; 365 break; 366 } 367 #endif 368 /* escaped colons and Windows drive letter colons were handled 369 * above; if we're still here, this is a separating colon */ 370 param_place++; 371 if(strlen(param_place) > 0) { 372 *passphrase = strdup(param_place); 373 } 374 goto done; 375 } 376 } 377 done: 378 *certname_place = '\0'; 379 } 380 381 ParameterError getparameter(char *flag, /* f or -long-flag */ 382 char *nextarg, /* NULL if unset */ 383 bool *usedarg, /* set to TRUE if the arg 384 has been used */ 385 struct GlobalConfig *global, 386 struct OperationConfig *config) 387 { 388 char letter; 389 char subletter = '\0'; /* subletters can only occur on long options */ 390 int rc; 391 const char *parse = NULL; 392 unsigned int j; 393 time_t now; 394 int hit = -1; 395 bool longopt = FALSE; 396 bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */ 397 ParameterError err; 398 bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled 399 by using --OPTION or --no-OPTION */ 400 401 402 if(('-' != flag[0]) || 403 (('-' == flag[0]) && ('-' == flag[1]))) { 404 /* this should be a long name */ 405 char *word = ('-' == flag[0]) ? flag+2 : flag; 406 size_t fnam = strlen(word); 407 int numhits = 0; 408 409 if(!strncmp(word, "no-", 3)) { 410 /* disable this option but ignore the "no-" part when looking for it */ 411 word += 3; 412 toggle = FALSE; 413 } 414 415 for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) { 416 if(curlx_strnequal(aliases[j].lname, word, fnam)) { 417 longopt = TRUE; 418 numhits++; 419 if(curlx_raw_equal(aliases[j].lname, word)) { 420 parse = aliases[j].letter; 421 hit = j; 422 numhits = 1; /* a single unique hit */ 423 break; 424 } 425 parse = aliases[j].letter; 426 hit = j; 427 } 428 } 429 if(numhits > 1) { 430 /* this is at least the second match! */ 431 return PARAM_OPTION_AMBIGUOUS; 432 } 433 if(hit < 0) { 434 return PARAM_OPTION_UNKNOWN; 435 } 436 } 437 else { 438 flag++; /* prefixed with one dash, pass it */ 439 hit = -1; 440 parse = flag; 441 } 442 443 do { 444 /* we can loop here if we have multiple single-letters */ 445 446 if(!longopt) { 447 letter = (char)*parse; 448 subletter='\0'; 449 } 450 else { 451 letter = parse[0]; 452 subletter = parse[1]; 453 } 454 *usedarg = FALSE; /* default is that we don't use the arg */ 455 456 if(hit < 0) { 457 for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) { 458 if(letter == aliases[j].letter[0]) { 459 hit = j; 460 break; 461 } 462 } 463 if(hit < 0) { 464 return PARAM_OPTION_UNKNOWN; 465 } 466 } 467 468 if(aliases[hit].extraparam) { 469 /* this option requires an extra parameter */ 470 if(!longopt && parse[1]) { 471 nextarg = (char *)&parse[1]; /* this is the actual extra parameter */ 472 singleopt = TRUE; /* don't loop anymore after this */ 473 } 474 else if(!nextarg) 475 return PARAM_REQUIRES_PARAMETER; 476 else 477 *usedarg = TRUE; /* mark it as used */ 478 } 479 480 switch(letter) { 481 case '*': /* options without a short option */ 482 switch(subletter) { 483 case '4': /* --dns-ipv4-addr */ 484 /* addr in dot notation */ 485 GetStr(&config->dns_ipv4_addr, nextarg); 486 break; 487 case '6': /* --dns-ipv6-addr */ 488 /* addr in dot notation */ 489 GetStr(&config->dns_ipv6_addr, nextarg); 490 break; 491 case 'a': /* random-file */ 492 GetStr(&config->random_file, nextarg); 493 break; 494 case 'b': /* egd-file */ 495 GetStr(&config->egd_file, nextarg); 496 break; 497 case 'B': /* OAuth 2.0 bearer token */ 498 GetStr(&config->oauth_bearer, nextarg); 499 break; 500 case 'c': /* connect-timeout */ 501 err = str2udouble(&config->connecttimeout, nextarg); 502 if(err) 503 return err; 504 break; 505 case 'd': /* ciphers */ 506 GetStr(&config->cipher_list, nextarg); 507 break; 508 case 'D': /* --dns-interface */ 509 /* interface name */ 510 GetStr(&config->dns_interface, nextarg); 511 break; 512 case 'e': /* --disable-epsv */ 513 config->disable_epsv = toggle; 514 break; 515 case 'E': /* --epsv */ 516 config->disable_epsv = (!toggle)?TRUE:FALSE; 517 break; 518 #ifdef USE_ENVIRONMENT 519 case 'f': 520 config->writeenv = toggle; 521 break; 522 #endif 523 case 'F': /* --dns-servers */ 524 /* IP addrs of DNS servers */ 525 GetStr(&config->dns_servers, nextarg); 526 break; 527 case 'g': /* --trace */ 528 GetStr(&global->trace_dump, nextarg); 529 if(global->tracetype && (global->tracetype != TRACE_BIN)) 530 warnf(global, "--trace overrides an earlier trace/verbose option\n"); 531 global->tracetype = TRACE_BIN; 532 break; 533 case 'G': /* --npn */ 534 config->nonpn = (!toggle)?TRUE:FALSE; 535 break; 536 case 'h': /* --trace-ascii */ 537 GetStr(&global->trace_dump, nextarg); 538 if(global->tracetype && (global->tracetype != TRACE_ASCII)) 539 warnf(global, 540 "--trace-ascii overrides an earlier trace/verbose option\n"); 541 global->tracetype = TRACE_ASCII; 542 break; 543 case 'H': /* --alpn */ 544 config->noalpn = (!toggle)?TRUE:FALSE; 545 break; 546 case 'i': /* --limit-rate */ 547 { 548 /* We support G, M, K too */ 549 char *unit; 550 curl_off_t value = curlx_strtoofft(nextarg, &unit, 0); 551 552 if(!*unit) 553 unit = (char *)"b"; 554 else if(strlen(unit) > 1) 555 unit = (char *)"w"; /* unsupported */ 556 557 switch(*unit) { 558 case 'G': 559 case 'g': 560 value *= 1024*1024*1024; 561 break; 562 case 'M': 563 case 'm': 564 value *= 1024*1024; 565 break; 566 case 'K': 567 case 'k': 568 value *= 1024; 569 break; 570 case 'b': 571 case 'B': 572 /* for plain bytes, leave as-is */ 573 break; 574 default: 575 warnf(global, "unsupported rate unit. Use G, M, K or B!\n"); 576 return PARAM_BAD_USE; 577 } 578 config->recvpersecond = value; 579 config->sendpersecond = value; 580 } 581 break; 582 583 case 'j': /* --compressed */ 584 if(toggle && !(curlinfo->features & CURL_VERSION_LIBZ)) 585 return PARAM_LIBCURL_DOESNT_SUPPORT; 586 config->encoding = toggle; 587 break; 588 589 case 'J': /* --tr-encoding */ 590 config->tr_encoding = toggle; 591 break; 592 593 case 'k': /* --digest */ 594 if(toggle) 595 config->authtype |= CURLAUTH_DIGEST; 596 else 597 config->authtype &= ~CURLAUTH_DIGEST; 598 break; 599 600 case 'l': /* --negotiate */ 601 if(toggle) { 602 if(curlinfo->features & CURL_VERSION_SPNEGO) 603 config->authtype |= CURLAUTH_NEGOTIATE; 604 else 605 return PARAM_LIBCURL_DOESNT_SUPPORT; 606 } 607 else 608 config->authtype &= ~CURLAUTH_NEGOTIATE; 609 break; 610 611 case 'm': /* --ntlm */ 612 if(toggle) { 613 if(curlinfo->features & CURL_VERSION_NTLM) 614 config->authtype |= CURLAUTH_NTLM; 615 else 616 return PARAM_LIBCURL_DOESNT_SUPPORT; 617 } 618 else 619 config->authtype &= ~CURLAUTH_NTLM; 620 break; 621 622 case 'M': /* --ntlm-wb */ 623 if(toggle) { 624 if(curlinfo->features & CURL_VERSION_NTLM_WB) 625 config->authtype |= CURLAUTH_NTLM_WB; 626 else 627 return PARAM_LIBCURL_DOESNT_SUPPORT; 628 } 629 else 630 config->authtype &= ~CURLAUTH_NTLM_WB; 631 break; 632 633 case 'n': /* --basic for completeness */ 634 if(toggle) 635 config->authtype |= CURLAUTH_BASIC; 636 else 637 config->authtype &= ~CURLAUTH_BASIC; 638 break; 639 640 case 'o': /* --anyauth, let libcurl pick it */ 641 if(toggle) 642 config->authtype = CURLAUTH_ANY; 643 /* --no-anyauth simply doesn't touch it */ 644 break; 645 646 #ifdef USE_WATT32 647 case 'p': /* --wdebug */ 648 dbug_init(); 649 break; 650 #endif 651 case 'q': /* --ftp-create-dirs */ 652 config->ftp_create_dirs = toggle; 653 break; 654 655 case 'r': /* --create-dirs */ 656 config->create_dirs = toggle; 657 break; 658 659 case 's': /* --max-redirs */ 660 /* specified max no of redirects (http(s)), this accepts -1 as a 661 special condition */ 662 err = str2num(&config->maxredirs, nextarg); 663 if(err) 664 return err; 665 if(config->maxredirs < -1) 666 return PARAM_BAD_NUMERIC; 667 break; 668 669 case 't': /* --proxy-ntlm */ 670 if(curlinfo->features & CURL_VERSION_NTLM) 671 config->proxyntlm = toggle; 672 else 673 return PARAM_LIBCURL_DOESNT_SUPPORT; 674 break; 675 676 case 'u': /* --crlf */ 677 /* LF -> CRLF conversion? */ 678 config->crlf = toggle; 679 break; 680 681 case 'v': /* --stderr */ 682 if(strcmp(nextarg, "-")) { 683 FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT); 684 if(!newfile) 685 warnf(global, "Failed to open %s!\n", nextarg); 686 else { 687 if(global->errors_fopened) 688 fclose(global->errors); 689 global->errors = newfile; 690 global->errors_fopened = TRUE; 691 } 692 } 693 else 694 global->errors = stdout; 695 break; 696 case 'w': /* --interface */ 697 /* interface */ 698 GetStr(&config->iface, nextarg); 699 break; 700 case 'x': /* --krb */ 701 /* kerberos level string */ 702 if(curlinfo->features & CURL_VERSION_KERBEROS4) 703 GetStr(&config->krblevel, nextarg); 704 else 705 return PARAM_LIBCURL_DOESNT_SUPPORT; 706 break; 707 case 'y': /* --max-filesize */ 708 err = str2offset(&config->max_filesize, nextarg); 709 if(err) 710 return err; 711 break; 712 case 'z': /* --disable-eprt */ 713 config->disable_eprt = toggle; 714 break; 715 case 'Z': /* --eprt */ 716 config->disable_eprt = (!toggle)?TRUE:FALSE; 717 break; 718 case '~': /* --xattr */ 719 config->xattr = toggle; 720 break; 721 case '@': /* the URL! */ 722 { 723 struct getout *url; 724 if(config->url_get || ((config->url_get = config->url_list) != NULL)) { 725 /* there's a node here, if it already is filled-in continue to find 726 an "empty" node */ 727 while(config->url_get && (config->url_get->flags & GETOUT_URL)) 728 config->url_get = config->url_get->next; 729 } 730 731 /* now there might or might not be an available node to fill in! */ 732 733 if(config->url_get) 734 /* existing node */ 735 url = config->url_get; 736 else 737 /* there was no free node, create one! */ 738 url = new_getout(config); 739 740 if(!url) 741 return PARAM_NO_MEM; 742 else { 743 /* fill in the URL */ 744 GetStr(&url->url, nextarg); 745 url->flags |= GETOUT_URL; 746 } 747 } 748 } 749 break; 750 case '$': /* more options without a short option */ 751 switch(subletter) { 752 case 'a': /* --ftp-ssl */ 753 if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) 754 return PARAM_LIBCURL_DOESNT_SUPPORT; 755 config->ftp_ssl = toggle; 756 break; 757 case 'b': /* --ftp-pasv */ 758 Curl_safefree(config->ftpport); 759 break; 760 case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves 761 the name locally and passes on the resolved address */ 762 GetStr(&config->socksproxy, nextarg); 763 config->socksver = CURLPROXY_SOCKS5; 764 break; 765 case 't': /* --socks4 specifies a socks4 proxy to use */ 766 GetStr(&config->socksproxy, nextarg); 767 config->socksver = CURLPROXY_SOCKS4; 768 break; 769 case 'T': /* --socks4a specifies a socks4a proxy to use */ 770 GetStr(&config->socksproxy, nextarg); 771 config->socksver = CURLPROXY_SOCKS4A; 772 break; 773 case '2': /* --socks5-hostname specifies a socks5 proxy and enables name 774 resolving with the proxy */ 775 GetStr(&config->socksproxy, nextarg); 776 config->socksver = CURLPROXY_SOCKS5_HOSTNAME; 777 break; 778 case 'd': /* --tcp-nodelay option */ 779 config->tcp_nodelay = toggle; 780 break; 781 case 'e': /* --proxy-digest */ 782 config->proxydigest = toggle; 783 break; 784 case 'f': /* --proxy-basic */ 785 config->proxybasic = toggle; 786 break; 787 case 'g': /* --retry */ 788 err = str2unum(&config->req_retry, nextarg); 789 if(err) 790 return err; 791 break; 792 case 'h': /* --retry-delay */ 793 err = str2unum(&config->retry_delay, nextarg); 794 if(err) 795 return err; 796 break; 797 case 'i': /* --retry-max-time */ 798 err = str2unum(&config->retry_maxtime, nextarg); 799 if(err) 800 return err; 801 break; 802 803 case 'k': /* --proxy-negotiate */ 804 if(curlinfo->features & CURL_VERSION_SPNEGO) 805 config->proxynegotiate = toggle; 806 else 807 return PARAM_LIBCURL_DOESNT_SUPPORT; 808 break; 809 810 case 'm': /* --ftp-account */ 811 GetStr(&config->ftp_account, nextarg); 812 break; 813 case 'n': /* --proxy-anyauth */ 814 config->proxyanyauth = toggle; 815 break; 816 case 'o': /* --trace-time */ 817 global->tracetime = toggle; 818 break; 819 case 'p': /* --ignore-content-length */ 820 config->ignorecl = toggle; 821 break; 822 case 'q': /* --ftp-skip-pasv-ip */ 823 config->ftp_skip_ip = toggle; 824 break; 825 case 'r': /* --ftp-method (undocumented at this point) */ 826 config->ftp_filemethod = ftpfilemethod(config, nextarg); 827 break; 828 case 's': /* --local-port */ 829 rc = sscanf(nextarg, "%d - %d", 830 &config->localport, 831 &config->localportrange); 832 if(!rc) 833 return PARAM_BAD_USE; 834 else if(rc == 1) 835 config->localportrange = 1; /* default number of ports to try */ 836 else { 837 config->localportrange -= config->localport; 838 if(config->localportrange < 1) { 839 warnf(global, "bad range input\n"); 840 return PARAM_BAD_USE; 841 } 842 } 843 break; 844 case 'u': /* --ftp-alternative-to-user */ 845 GetStr(&config->ftp_alternative_to_user, nextarg); 846 break; 847 case 'v': /* --ftp-ssl-reqd */ 848 if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) 849 return PARAM_LIBCURL_DOESNT_SUPPORT; 850 config->ftp_ssl_reqd = toggle; 851 break; 852 case 'w': /* --no-sessionid */ 853 config->disable_sessionid = (!toggle)?TRUE:FALSE; 854 break; 855 case 'x': /* --ftp-ssl-control */ 856 if(toggle && !(curlinfo->features & CURL_VERSION_SSL)) 857 return PARAM_LIBCURL_DOESNT_SUPPORT; 858 config->ftp_ssl_control = toggle; 859 break; 860 case 'y': /* --ftp-ssl-ccc */ 861 config->ftp_ssl_ccc = toggle; 862 if(!config->ftp_ssl_ccc_mode) 863 config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE; 864 break; 865 case 'j': /* --ftp-ssl-ccc-mode */ 866 config->ftp_ssl_ccc = TRUE; 867 config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg); 868 break; 869 case 'z': /* --libcurl */ 870 #ifdef CURL_DISABLE_LIBCURL_OPTION 871 warnf(global, 872 "--libcurl option was disabled at build-time!\n"); 873 return PARAM_OPTION_UNKNOWN; 874 #else 875 GetStr(&global->libcurl, nextarg); 876 break; 877 #endif 878 case '#': /* --raw */ 879 config->raw = toggle; 880 break; 881 case '0': /* --post301 */ 882 config->post301 = toggle; 883 break; 884 case '1': /* --no-keepalive */ 885 config->nokeepalive = (!toggle)?TRUE:FALSE; 886 break; 887 case '3': /* --keepalive-time */ 888 err = str2unum(&config->alivetime, nextarg); 889 if(err) 890 return err; 891 break; 892 case '4': /* --post302 */ 893 config->post302 = toggle; 894 break; 895 case 'I': /* --post303 */ 896 config->post303 = toggle; 897 break; 898 case '5': /* --noproxy */ 899 /* This specifies the noproxy list */ 900 GetStr(&config->noproxy, nextarg); 901 break; 902 case '7': /* --socks5-gssapi-nec*/ 903 config->socks5_gssapi_nec = toggle; 904 break; 905 case '8': /* --proxy1.0 */ 906 /* http 1.0 proxy */ 907 GetStr(&config->proxy, nextarg); 908 config->proxyver = CURLPROXY_HTTP_1_0; 909 break; 910 case '9': /* --tftp-blksize */ 911 err = str2unum(&config->tftp_blksize, nextarg); 912 if(err) 913 return err; 914 break; 915 case 'A': /* --mail-from */ 916 GetStr(&config->mail_from, nextarg); 917 break; 918 case 'B': /* --mail-rcpt */ 919 /* append receiver to a list */ 920 err = add2list(&config->mail_rcpt, nextarg); 921 if(err) 922 return err; 923 break; 924 case 'C': /* --ftp-pret */ 925 config->ftp_pret = toggle; 926 break; 927 case 'D': /* --proto */ 928 config->proto_present = TRUE; 929 if(proto2num(config, &config->proto, nextarg)) 930 return PARAM_BAD_USE; 931 break; 932 case 'E': /* --proto-redir */ 933 config->proto_redir_present = TRUE; 934 if(proto2num(config, &config->proto_redir, nextarg)) 935 return PARAM_BAD_USE; 936 break; 937 case 'F': /* --resolve */ 938 err = add2list(&config->resolve, nextarg); 939 if(err) 940 return err; 941 break; 942 case 'G': /* --delegation LEVEL */ 943 config->gssapi_delegation = delegation(config, nextarg); 944 break; 945 case 'H': /* --mail-auth */ 946 GetStr(&config->mail_auth, nextarg); 947 break; 948 case 'J': /* --metalink */ 949 { 950 #ifdef USE_METALINK 951 int mlmaj, mlmin, mlpatch; 952 metalink_get_version(&mlmaj, &mlmin, &mlpatch); 953 if((mlmaj*10000)+(mlmin*100)+mlpatch < CURL_REQ_LIBMETALINK_VERS) { 954 warnf(global, 955 "--metalink option cannot be used because the version of " 956 "the linked libmetalink library is too old. " 957 "Required: %d.%d.%d, found %d.%d.%d\n", 958 CURL_REQ_LIBMETALINK_MAJOR, 959 CURL_REQ_LIBMETALINK_MINOR, 960 CURL_REQ_LIBMETALINK_PATCH, 961 mlmaj, mlmin, mlpatch); 962 return PARAM_BAD_USE; 963 } 964 else 965 config->use_metalink = toggle; 966 #else 967 warnf(global, "--metalink option is ignored because the binary is " 968 "built without the Metalink support.\n"); 969 #endif 970 break; 971 } 972 case 'K': /* --sasl-ir */ 973 config->sasl_ir = toggle; 974 break; 975 case 'L': /* --test-event */ 976 #ifdef CURLDEBUG 977 config->test_event_based = toggle; 978 #else 979 warnf(global, "--test-event is ignored unless a debug build!\n"); 980 #endif 981 break; 982 case 'M': /* --unix-socket */ 983 GetStr(&config->unix_socket_path, nextarg); 984 break; 985 case 'N': /* --path-as-is */ 986 config->path_as_is = toggle; 987 break; 988 case 'O': /* --proxy-service-name */ 989 GetStr(&config->proxy_service_name, nextarg); 990 break; 991 case 'P': /* --service-name */ 992 GetStr(&config->service_name, nextarg); 993 break; 994 case 'Q': /* --proto-default */ 995 GetStr(&config->proto_default, nextarg); 996 err = check_protocol(config->proto_default); 997 if(err) 998 return err; 999 break; 1000 case 'R': /* --expect100-timeout */ 1001 err = str2udouble(&config->expect100timeout, nextarg); 1002 if(err) 1003 return err; 1004 break; 1005 case 'S': /* --tftp-no-options */ 1006 config->tftp_no_options = toggle; 1007 break; 1008 case 'U': /* --connect-to */ 1009 err = add2list(&config->connect_to, nextarg); 1010 if(err) 1011 return err; 1012 break; 1013 } 1014 break; 1015 case '#': /* --progress-bar */ 1016 if(toggle) 1017 global->progressmode = CURL_PROGRESS_BAR; 1018 else 1019 global->progressmode = CURL_PROGRESS_STATS; 1020 break; 1021 case ':': /* --next */ 1022 return PARAM_NEXT_OPERATION; 1023 case '0': /* --http* options */ 1024 switch(subletter) { 1025 case '\0': 1026 /* HTTP version 1.0 */ 1027 config->httpversion = CURL_HTTP_VERSION_1_0; 1028 break; 1029 case '1': 1030 /* HTTP version 1.1 */ 1031 config->httpversion = CURL_HTTP_VERSION_1_1; 1032 break; 1033 case '2': 1034 /* HTTP version 2.0 */ 1035 config->httpversion = CURL_HTTP_VERSION_2_0; 1036 break; 1037 case '3': 1038 /* HTTP version 2.0 over clean TCP*/ 1039 config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE; 1040 break; 1041 } 1042 break; 1043 case '1': /* --tlsv1* options */ 1044 switch(subletter) { 1045 case '\0': 1046 /* TLS version 1.x */ 1047 config->ssl_version = CURL_SSLVERSION_TLSv1; 1048 break; 1049 case '0': 1050 /* TLS version 1.0 */ 1051 config->ssl_version = CURL_SSLVERSION_TLSv1_0; 1052 break; 1053 case '1': 1054 /* TLS version 1.1 */ 1055 config->ssl_version = CURL_SSLVERSION_TLSv1_1; 1056 break; 1057 case '2': 1058 /* TLS version 1.2 */ 1059 config->ssl_version = CURL_SSLVERSION_TLSv1_2; 1060 break; 1061 } 1062 break; 1063 case '2': 1064 /* SSL version 2 */ 1065 config->ssl_version = CURL_SSLVERSION_SSLv2; 1066 break; 1067 case '3': 1068 /* SSL version 3 */ 1069 config->ssl_version = CURL_SSLVERSION_SSLv3; 1070 break; 1071 case '4': 1072 /* IPv4 */ 1073 config->ip_version = 4; 1074 break; 1075 case '6': 1076 /* IPv6 */ 1077 config->ip_version = 6; 1078 break; 1079 case 'a': 1080 /* This makes the FTP sessions use APPE instead of STOR */ 1081 config->ftp_append = toggle; 1082 break; 1083 case 'A': 1084 /* This specifies the User-Agent name */ 1085 GetStr(&config->useragent, nextarg); 1086 break; 1087 case 'b': /* cookie string coming up: */ 1088 if(nextarg[0] == '@') { 1089 nextarg++; 1090 } 1091 else if(strchr(nextarg, '=')) { 1092 /* A cookie string must have a =-letter */ 1093 GetStr(&config->cookie, nextarg); 1094 break; 1095 } 1096 /* We have a cookie file to read from! */ 1097 GetStr(&config->cookiefile, nextarg); 1098 break; 1099 case 'B': 1100 /* use ASCII/text when transferring */ 1101 config->use_ascii = toggle; 1102 break; 1103 case 'c': 1104 /* get the file name to dump all cookies in */ 1105 GetStr(&config->cookiejar, nextarg); 1106 break; 1107 case 'C': 1108 /* This makes us continue an ftp transfer at given position */ 1109 if(!curlx_strequal(nextarg, "-")) { 1110 err = str2offset(&config->resume_from, nextarg); 1111 if(err) 1112 return err; 1113 config->resume_from_current = FALSE; 1114 } 1115 else { 1116 config->resume_from_current = TRUE; 1117 config->resume_from = 0; 1118 } 1119 config->use_resume=TRUE; 1120 break; 1121 case 'd': 1122 /* postfield data */ 1123 { 1124 char *postdata = NULL; 1125 FILE *file; 1126 size_t size = 0; 1127 bool raw_mode = (subletter == 'r'); 1128 1129 if(subletter == 'e') { /* --data-urlencode*/ 1130 /* [name]=[content], we encode the content part only 1131 * [name]@[file name] 1132 * 1133 * Case 2: we first load the file using that name and then encode 1134 * the content. 1135 */ 1136 const char *p = strchr(nextarg, '='); 1137 size_t nlen; 1138 char is_file; 1139 if(!p) 1140 /* there was no '=' letter, check for a '@' instead */ 1141 p = strchr(nextarg, '@'); 1142 if(p) { 1143 nlen = p - nextarg; /* length of the name part */ 1144 is_file = *p++; /* pass the separator */ 1145 } 1146 else { 1147 /* neither @ nor =, so no name and it isn't a file */ 1148 nlen = is_file = 0; 1149 p = nextarg; 1150 } 1151 if('@' == is_file) { 1152 /* a '@' letter, it means that a file name or - (stdin) follows */ 1153 if(curlx_strequal("-", p)) { 1154 file = stdin; 1155 set_binmode(stdin); 1156 } 1157 else { 1158 file = fopen(p, "rb"); 1159 if(!file) 1160 warnf(global, 1161 "Couldn't read data from file \"%s\", this makes " 1162 "an empty POST.\n", nextarg); 1163 } 1164 1165 err = file2memory(&postdata, &size, file); 1166 1167 if(file && (file != stdin)) 1168 fclose(file); 1169 if(err) 1170 return err; 1171 } 1172 else { 1173 GetStr(&postdata, p); 1174 if(postdata) 1175 size = strlen(postdata); 1176 } 1177 1178 if(!postdata) { 1179 /* no data from the file, point to a zero byte string to make this 1180 get sent as a POST anyway */ 1181 postdata = strdup(""); 1182 if(!postdata) 1183 return PARAM_NO_MEM; 1184 size = 0; 1185 } 1186 else { 1187 char *enc = curl_easy_escape(config->easy, postdata, (int)size); 1188 Curl_safefree(postdata); /* no matter if it worked or not */ 1189 if(enc) { 1190 /* now make a string with the name from above and append the 1191 encoded string */ 1192 size_t outlen = nlen + strlen(enc) + 2; 1193 char *n = malloc(outlen); 1194 if(!n) { 1195 curl_free(enc); 1196 return PARAM_NO_MEM; 1197 } 1198 if(nlen > 0) { /* only append '=' if we have a name */ 1199 snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc); 1200 size = outlen-1; 1201 } 1202 else { 1203 strcpy(n, enc); 1204 size = outlen-2; /* since no '=' was inserted */ 1205 } 1206 curl_free(enc); 1207 postdata = n; 1208 } 1209 else 1210 return PARAM_NO_MEM; 1211 } 1212 } 1213 else if('@' == *nextarg && !raw_mode) { 1214 /* the data begins with a '@' letter, it means that a file name 1215 or - (stdin) follows */ 1216 nextarg++; /* pass the @ */ 1217 1218 if(curlx_strequal("-", nextarg)) { 1219 file = stdin; 1220 if(subletter == 'b') /* forced data-binary */ 1221 set_binmode(stdin); 1222 } 1223 else { 1224 file = fopen(nextarg, "rb"); 1225 if(!file) 1226 warnf(global, "Couldn't read data from file \"%s\", this makes " 1227 "an empty POST.\n", nextarg); 1228 } 1229 1230 if(subletter == 'b') 1231 /* forced binary */ 1232 err = file2memory(&postdata, &size, file); 1233 else { 1234 err = file2string(&postdata, file); 1235 if(postdata) 1236 size = strlen(postdata); 1237 } 1238 1239 if(file && (file != stdin)) 1240 fclose(file); 1241 if(err) 1242 return err; 1243 1244 if(!postdata) { 1245 /* no data from the file, point to a zero byte string to make this 1246 get sent as a POST anyway */ 1247 postdata = strdup(""); 1248 if(!postdata) 1249 return PARAM_NO_MEM; 1250 } 1251 } 1252 else { 1253 GetStr(&postdata, nextarg); 1254 if(postdata) 1255 size = strlen(postdata); 1256 } 1257 1258 #ifdef CURL_DOES_CONVERSIONS 1259 if(subletter != 'b') { 1260 /* NOT forced binary, convert to ASCII */ 1261 if(convert_to_network(postdata, strlen(postdata))) { 1262 Curl_safefree(postdata); 1263 return PARAM_NO_MEM; 1264 } 1265 } 1266 #endif 1267 1268 if(config->postfields) { 1269 /* we already have a string, we append this one with a separating 1270 &-letter */ 1271 char *oldpost = config->postfields; 1272 curl_off_t oldlen = config->postfieldsize; 1273 curl_off_t newlen = oldlen + curlx_uztoso(size) + 2; 1274 config->postfields = malloc((size_t)newlen); 1275 if(!config->postfields) { 1276 Curl_safefree(oldpost); 1277 Curl_safefree(postdata); 1278 return PARAM_NO_MEM; 1279 } 1280 memcpy(config->postfields, oldpost, (size_t)oldlen); 1281 /* use byte value 0x26 for '&' to accommodate non-ASCII platforms */ 1282 config->postfields[oldlen] = '\x26'; 1283 memcpy(&config->postfields[oldlen+1], postdata, size); 1284 config->postfields[oldlen+1+size] = '\0'; 1285 Curl_safefree(oldpost); 1286 Curl_safefree(postdata); 1287 config->postfieldsize += size+1; 1288 } 1289 else { 1290 config->postfields = postdata; 1291 config->postfieldsize = curlx_uztoso(size); 1292 } 1293 } 1294 /* 1295 We can't set the request type here, as this data might be used in 1296 a simple GET if -G is used. Already or soon. 1297 1298 if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) { 1299 Curl_safefree(postdata); 1300 return PARAM_BAD_USE; 1301 } 1302 */ 1303 break; 1304 case 'D': 1305 /* dump-header to given file name */ 1306 GetStr(&config->headerfile, nextarg); 1307 break; 1308 case 'e': 1309 { 1310 char *ptr = strstr(nextarg, ";auto"); 1311 if(ptr) { 1312 /* Automatic referer requested, this may be combined with a 1313 set initial one */ 1314 config->autoreferer = TRUE; 1315 *ptr = 0; /* zero terminate here */ 1316 } 1317 else 1318 config->autoreferer = FALSE; 1319 GetStr(&config->referer, nextarg); 1320 } 1321 break; 1322 case 'E': 1323 switch(subletter) { 1324 case 'a': /* CA info PEM file */ 1325 /* CA info PEM file */ 1326 GetStr(&config->cacert, nextarg); 1327 break; 1328 case 'b': /* cert file type */ 1329 GetStr(&config->cert_type, nextarg); 1330 break; 1331 case 'c': /* private key file */ 1332 GetStr(&config->key, nextarg); 1333 break; 1334 case 'd': /* private key file type */ 1335 GetStr(&config->key_type, nextarg); 1336 break; 1337 case 'e': /* private key passphrase */ 1338 GetStr(&config->key_passwd, nextarg); 1339 cleanarg(nextarg); 1340 break; 1341 case 'f': /* crypto engine */ 1342 GetStr(&config->engine, nextarg); 1343 if(config->engine && curlx_raw_equal(config->engine, "list")) 1344 return PARAM_ENGINES_REQUESTED; 1345 break; 1346 case 'g': /* CA info PEM file */ 1347 /* CA cert directory */ 1348 GetStr(&config->capath, nextarg); 1349 break; 1350 case 'h': /* --pubkey public key file */ 1351 GetStr(&config->pubkey, nextarg); 1352 break; 1353 case 'i': /* --hostpubmd5 md5 of the host public key */ 1354 GetStr(&config->hostpubmd5, nextarg); 1355 if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32) 1356 return PARAM_BAD_USE; 1357 break; 1358 case 'j': /* CRL info PEM file */ 1359 /* CRL file */ 1360 GetStr(&config->crlfile, nextarg); 1361 break; 1362 case 'k': /* TLS username */ 1363 if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) 1364 GetStr(&config->tls_username, nextarg); 1365 else 1366 return PARAM_LIBCURL_DOESNT_SUPPORT; 1367 break; 1368 case 'l': /* TLS password */ 1369 if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) 1370 GetStr(&config->tls_password, nextarg); 1371 else 1372 return PARAM_LIBCURL_DOESNT_SUPPORT; 1373 break; 1374 case 'm': /* TLS authentication type */ 1375 if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) { 1376 GetStr(&config->tls_authtype, nextarg); 1377 if(!strequal(config->tls_authtype, "SRP")) 1378 return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ 1379 } 1380 else 1381 return PARAM_LIBCURL_DOESNT_SUPPORT; 1382 break; 1383 case 'n': /* no empty SSL fragments, --ssl-allow-beast */ 1384 if(curlinfo->features & CURL_VERSION_SSL) 1385 config->ssl_allow_beast = toggle; 1386 break; 1387 1388 case 'o': /* --login-options */ 1389 GetStr(&config->login_options, nextarg); 1390 break; 1391 1392 case 'p': /* Pinned public key DER file */ 1393 /* Pinned public key DER file */ 1394 GetStr(&config->pinnedpubkey, nextarg); 1395 break; 1396 1397 case 'q': /* --cert-status */ 1398 config->verifystatus = TRUE; 1399 break; 1400 1401 case 'r': /* --false-start */ 1402 config->falsestart = TRUE; 1403 break; 1404 1405 case 's': /* --ssl-no-revoke */ 1406 if(curlinfo->features & CURL_VERSION_SSL) 1407 config->ssl_no_revoke = TRUE; 1408 break; 1409 1410 case 't': /* --tcp-fastopen */ 1411 config->tcp_fastopen = TRUE; 1412 break; 1413 1414 default: /* certificate file */ 1415 { 1416 char *certname, *passphrase; 1417 parse_cert_parameter(nextarg, &certname, &passphrase); 1418 Curl_safefree(config->cert); 1419 config->cert = certname; 1420 if(passphrase) { 1421 Curl_safefree(config->key_passwd); 1422 config->key_passwd = passphrase; 1423 } 1424 cleanarg(nextarg); 1425 } 1426 } 1427 break; 1428 case 'f': 1429 /* fail hard on errors */ 1430 config->failonerror = toggle; 1431 break; 1432 case 'F': 1433 /* "form data" simulation, this is a little advanced so lets do our best 1434 to sort this out slowly and carefully */ 1435 if(formparse(config, 1436 nextarg, 1437 &config->httppost, 1438 &config->last_post, 1439 (subletter=='s')?TRUE:FALSE)) /* 's' means literal string */ 1440 return PARAM_BAD_USE; 1441 if(SetHTTPrequest(config, HTTPREQ_FORMPOST, &config->httpreq)) 1442 return PARAM_BAD_USE; 1443 break; 1444 1445 case 'g': /* g disables URLglobbing */ 1446 config->globoff = toggle; 1447 break; 1448 1449 case 'G': /* HTTP GET */ 1450 config->use_httpget = TRUE; 1451 break; 1452 1453 case 'h': /* h for help */ 1454 if(toggle) { 1455 return PARAM_HELP_REQUESTED; 1456 } 1457 /* we now actually support --no-help too! */ 1458 break; 1459 case 'H': 1460 /* A custom header to append to a list */ 1461 if(subletter == 'p') /* --proxy-header */ 1462 err = add2list(&config->proxyheaders, nextarg); 1463 else 1464 err = add2list(&config->headers, nextarg); 1465 if(err) 1466 return err; 1467 break; 1468 case 'i': 1469 config->include_headers = toggle; /* include the headers as well in the 1470 general output stream */ 1471 break; 1472 case 'j': 1473 config->cookiesession = toggle; 1474 break; 1475 case 'I': 1476 /* 1477 * no_body will imply include_headers later on 1478 */ 1479 config->no_body = toggle; 1480 if(SetHTTPrequest(config, 1481 (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET, 1482 &config->httpreq)) 1483 return PARAM_BAD_USE; 1484 break; 1485 case 'J': /* --remote-header-name */ 1486 if(config->include_headers) { 1487 warnf(global, 1488 "--include and --remote-header-name cannot be combined.\n"); 1489 return PARAM_BAD_USE; 1490 } 1491 config->content_disposition = toggle; 1492 break; 1493 case 'k': /* allow insecure SSL connects */ 1494 config->insecure_ok = toggle; 1495 break; 1496 case 'K': /* parse config file */ 1497 if(parseconfig(nextarg, global)) 1498 warnf(global, "error trying read config from the '%s' file\n", 1499 nextarg); 1500 break; 1501 case 'l': 1502 config->dirlistonly = toggle; /* only list the names of the FTP dir */ 1503 break; 1504 case 'L': 1505 config->followlocation = toggle; /* Follow Location: HTTP headers */ 1506 switch (subletter) { 1507 case 't': 1508 /* Continue to send authentication (user+password) when following 1509 * locations, even when hostname changed */ 1510 config->unrestricted_auth = toggle; 1511 break; 1512 } 1513 break; 1514 case 'm': 1515 /* specified max time */ 1516 err = str2udouble(&config->timeout, nextarg); 1517 if(err) 1518 return err; 1519 break; 1520 case 'M': /* M for manual, huge help */ 1521 if(toggle) { /* --no-manual shows no manual... */ 1522 #ifdef USE_MANUAL 1523 return PARAM_MANUAL_REQUESTED; 1524 #else 1525 warnf(global, 1526 "built-in manual was disabled at build-time!\n"); 1527 return PARAM_OPTION_UNKNOWN; 1528 #endif 1529 } 1530 break; 1531 case 'n': 1532 switch(subletter) { 1533 case 'o': /* CA info PEM file */ 1534 /* use .netrc or URL */ 1535 config->netrc_opt = toggle; 1536 break; 1537 case 'e': /* netrc-file */ 1538 GetStr(&config->netrc_file, nextarg); 1539 break; 1540 default: 1541 /* pick info from .netrc, if this is used for http, curl will 1542 automatically enfore user+password with the request */ 1543 config->netrc = toggle; 1544 break; 1545 } 1546 break; 1547 case 'N': 1548 /* disable the output I/O buffering. note that the option is called 1549 --buffer but is mostly used in the negative form: --no-buffer */ 1550 if(longopt) 1551 config->nobuffer = (!toggle)?TRUE:FALSE; 1552 else 1553 config->nobuffer = toggle; 1554 break; 1555 case 'O': /* --remote-name */ 1556 if(subletter == 'a') { /* --remote-name-all */ 1557 config->default_node_flags = toggle?GETOUT_USEREMOTE:0; 1558 break; 1559 } 1560 /* fall-through! */ 1561 case 'o': /* --output */ 1562 /* output file */ 1563 { 1564 struct getout *url; 1565 if(config->url_out || ((config->url_out = config->url_list) != NULL)) { 1566 /* there's a node here, if it already is filled-in continue to find 1567 an "empty" node */ 1568 while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE)) 1569 config->url_out = config->url_out->next; 1570 } 1571 1572 /* now there might or might not be an available node to fill in! */ 1573 1574 if(config->url_out) 1575 /* existing node */ 1576 url = config->url_out; 1577 else 1578 /* there was no free node, create one! */ 1579 url = new_getout(config); 1580 1581 if(!url) 1582 return PARAM_NO_MEM; 1583 else { 1584 /* fill in the outfile */ 1585 if('o' == letter) { 1586 GetStr(&url->outfile, nextarg); 1587 url->flags &= ~GETOUT_USEREMOTE; /* switch off */ 1588 } 1589 else { 1590 url->outfile = NULL; /* leave it */ 1591 if(toggle) 1592 url->flags |= GETOUT_USEREMOTE; /* switch on */ 1593 else 1594 url->flags &= ~GETOUT_USEREMOTE; /* switch off */ 1595 } 1596 url->flags |= GETOUT_OUTFILE; 1597 } 1598 } 1599 break; 1600 case 'P': 1601 /* This makes the FTP sessions use PORT instead of PASV */ 1602 /* use <eth0> or <192.168.10.10> style addresses. Anything except 1603 this will make us try to get the "default" address. 1604 NOTE: this is a changed behaviour since the released 4.1! 1605 */ 1606 GetStr(&config->ftpport, nextarg); 1607 break; 1608 case 'p': 1609 /* proxy tunnel for non-http protocols */ 1610 config->proxytunnel = toggle; 1611 break; 1612 1613 case 'q': /* if used first, already taken care of, we do it like 1614 this so we don't cause an error! */ 1615 break; 1616 case 'Q': 1617 /* QUOTE command to send to FTP server */ 1618 switch(nextarg[0]) { 1619 case '-': 1620 /* prefixed with a dash makes it a POST TRANSFER one */ 1621 nextarg++; 1622 err = add2list(&config->postquote, nextarg); 1623 break; 1624 case '+': 1625 /* prefixed with a plus makes it a just-before-transfer one */ 1626 nextarg++; 1627 err = add2list(&config->prequote, nextarg); 1628 break; 1629 default: 1630 err = add2list(&config->quote, nextarg); 1631 break; 1632 } 1633 if(err) 1634 return err; 1635 break; 1636 case 'r': 1637 /* Specifying a range WITHOUT A DASH will create an illegal HTTP range 1638 (and won't actually be range by definition). The man page previously 1639 claimed that to be a good way, why this code is added to work-around 1640 it. */ 1641 if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) { 1642 char buffer[32]; 1643 curl_off_t off; 1644 warnf(global, 1645 "A specified range MUST include at least one dash (-). " 1646 "Appending one for you!\n"); 1647 off = curlx_strtoofft(nextarg, NULL, 10); 1648 snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off); 1649 Curl_safefree(config->range); 1650 config->range = strdup(buffer); 1651 if(!config->range) 1652 return PARAM_NO_MEM; 1653 } 1654 { 1655 /* byte range requested */ 1656 char *tmp_range; 1657 tmp_range = nextarg; 1658 while(*tmp_range != '\0') { 1659 if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') { 1660 warnf(global, "Invalid character is found in given range. " 1661 "A specified range MUST have only digits in " 1662 "\'start\'-\'stop\'. The server's response to this " 1663 "request is uncertain.\n"); 1664 break; 1665 } 1666 tmp_range++; 1667 } 1668 /* byte range requested */ 1669 GetStr(&config->range, nextarg); 1670 } 1671 break; 1672 case 'R': 1673 /* use remote file's time */ 1674 config->remote_time = toggle; 1675 break; 1676 case 's': 1677 /* don't show progress meter, don't show errors : */ 1678 if(toggle) 1679 global->mute = global->noprogress = TRUE; 1680 else 1681 global->mute = global->noprogress = FALSE; 1682 if(global->showerror < 0) 1683 /* if still on the default value, set showerror to the reverse of 1684 toggle. This is to allow -S and -s to be used in an independent 1685 order but still have the same effect. */ 1686 global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */ 1687 break; 1688 case 'S': 1689 /* show errors */ 1690 global->showerror = toggle?1:0; /* toggle on if used with -s */ 1691 break; 1692 case 't': 1693 /* Telnet options */ 1694 err = add2list(&config->telnet_options, nextarg); 1695 if(err) 1696 return err; 1697 break; 1698 case 'T': 1699 /* we are uploading */ 1700 { 1701 struct getout *url; 1702 if(config->url_out || ((config->url_out = config->url_list) != NULL)) { 1703 /* there's a node here, if it already is filled-in continue to find 1704 an "empty" node */ 1705 while(config->url_out && (config->url_out->flags & GETOUT_UPLOAD)) 1706 config->url_out = config->url_out->next; 1707 } 1708 1709 /* now there might or might not be an available node to fill in! */ 1710 1711 if(config->url_out) 1712 /* existing node */ 1713 url = config->url_out; 1714 else 1715 /* there was no free node, create one! */ 1716 url = new_getout(config); 1717 1718 if(!url) 1719 return PARAM_NO_MEM; 1720 else { 1721 url->flags |= GETOUT_UPLOAD; /* mark -T used */ 1722 if(!*nextarg) 1723 url->flags |= GETOUT_NOUPLOAD; 1724 else { 1725 /* "-" equals stdin, but keep the string around for now */ 1726 GetStr(&url->infile, nextarg); 1727 } 1728 } 1729 } 1730 break; 1731 case 'u': 1732 /* user:password */ 1733 GetStr(&config->userpwd, nextarg); 1734 cleanarg(nextarg); 1735 break; 1736 case 'U': 1737 /* Proxy user:password */ 1738 GetStr(&config->proxyuserpwd, nextarg); 1739 cleanarg(nextarg); 1740 break; 1741 case 'v': 1742 if(toggle) { 1743 /* the '%' thing here will cause the trace get sent to stderr */ 1744 Curl_safefree(global->trace_dump); 1745 global->trace_dump = strdup("%"); 1746 if(!global->trace_dump) 1747 return PARAM_NO_MEM; 1748 if(global->tracetype && (global->tracetype != TRACE_PLAIN)) 1749 warnf(global, 1750 "-v, --verbose overrides an earlier trace/verbose option\n"); 1751 global->tracetype = TRACE_PLAIN; 1752 } 1753 else 1754 /* verbose is disabled here */ 1755 global->tracetype = TRACE_NONE; 1756 break; 1757 case 'V': 1758 if(toggle) /* --no-version yields no output! */ 1759 return PARAM_VERSION_INFO_REQUESTED; 1760 break; 1761 1762 case 'w': 1763 /* get the output string */ 1764 if('@' == *nextarg) { 1765 /* the data begins with a '@' letter, it means that a file name 1766 or - (stdin) follows */ 1767 FILE *file; 1768 const char *fname; 1769 nextarg++; /* pass the @ */ 1770 if(curlx_strequal("-", nextarg)) { 1771 fname = "<stdin>"; 1772 file = stdin; 1773 } 1774 else { 1775 fname = nextarg; 1776 file = fopen(nextarg, FOPEN_READTEXT); 1777 } 1778 err = file2string(&config->writeout, file); 1779 if(file && (file != stdin)) 1780 fclose(file); 1781 if(err) 1782 return err; 1783 if(!config->writeout) 1784 warnf(global, "Failed to read %s", fname); 1785 } 1786 else 1787 GetStr(&config->writeout, nextarg); 1788 break; 1789 case 'x': 1790 /* proxy */ 1791 GetStr(&config->proxy, nextarg); 1792 config->proxyver = CURLPROXY_HTTP; 1793 break; 1794 case 'X': 1795 /* set custom request */ 1796 GetStr(&config->customrequest, nextarg); 1797 break; 1798 case 'y': 1799 /* low speed time */ 1800 err = str2unum(&config->low_speed_time, nextarg); 1801 if(err) 1802 return err; 1803 if(!config->low_speed_limit) 1804 config->low_speed_limit = 1; 1805 break; 1806 case 'Y': 1807 /* low speed limit */ 1808 err = str2unum(&config->low_speed_limit, nextarg); 1809 if(err) 1810 return err; 1811 if(!config->low_speed_time) 1812 config->low_speed_time = 30; 1813 break; 1814 case 'z': /* time condition coming up */ 1815 switch(*nextarg) { 1816 case '+': 1817 nextarg++; 1818 /* FALLTHROUGH */ 1819 default: 1820 /* If-Modified-Since: (section 14.28 in RFC2068) */ 1821 config->timecond = CURL_TIMECOND_IFMODSINCE; 1822 break; 1823 case '-': 1824 /* If-Unmodified-Since: (section 14.24 in RFC2068) */ 1825 config->timecond = CURL_TIMECOND_IFUNMODSINCE; 1826 nextarg++; 1827 break; 1828 case '=': 1829 /* Last-Modified: (section 14.29 in RFC2068) */ 1830 config->timecond = CURL_TIMECOND_LASTMOD; 1831 nextarg++; 1832 break; 1833 } 1834 now = time(NULL); 1835 config->condtime=curl_getdate(nextarg, &now); 1836 if(-1 == (int)config->condtime) { 1837 /* now let's see if it is a file name to get the time from instead! */ 1838 struct_stat statbuf; 1839 if(-1 == stat(nextarg, &statbuf)) { 1840 /* failed, remove time condition */ 1841 config->timecond = CURL_TIMECOND_NONE; 1842 warnf(global, 1843 "Illegal date format for -z, --timecond (and not " 1844 "a file name). Disabling time condition. " 1845 "See curl_getdate(3) for valid date syntax.\n"); 1846 } 1847 else { 1848 /* pull the time out from the file */ 1849 config->condtime = statbuf.st_mtime; 1850 } 1851 } 1852 break; 1853 default: /* unknown flag */ 1854 return PARAM_OPTION_UNKNOWN; 1855 } 1856 hit = -1; 1857 1858 } while(!longopt && !singleopt && *++parse && !*usedarg); 1859 1860 return PARAM_OK; 1861 } 1862 1863 ParameterError parse_args(struct GlobalConfig *config, int argc, 1864 argv_item_t argv[]) 1865 { 1866 int i; 1867 bool stillflags; 1868 char *orig_opt = NULL; 1869 ParameterError result = PARAM_OK; 1870 struct OperationConfig *operation = config->first; 1871 1872 for(i = 1, stillflags = TRUE; i < argc && !result; i++) { 1873 orig_opt = argv[i]; 1874 1875 if(stillflags && ('-' == argv[i][0])) { 1876 char *nextarg; 1877 bool passarg; 1878 char *flag = argv[i]; 1879 1880 if(curlx_strequal("--", argv[i])) 1881 /* This indicates the end of the flags and thus enables the 1882 following (URL) argument to start with -. */ 1883 stillflags = FALSE; 1884 else { 1885 nextarg = (i < (argc - 1)) ? argv[i + 1] : NULL; 1886 1887 result = getparameter(flag, nextarg, &passarg, config, operation); 1888 if(result == PARAM_NEXT_OPERATION) { 1889 /* Reset result as PARAM_NEXT_OPERATION is only used here and not 1890 returned from this function */ 1891 result = PARAM_OK; 1892 1893 if(operation->url_list && operation->url_list->url) { 1894 /* Allocate the next config */ 1895 operation->next = malloc(sizeof(struct OperationConfig)); 1896 if(operation->next) { 1897 /* Initialise the newly created config */ 1898 config_init(operation->next); 1899 1900 /* Copy the easy handle */ 1901 operation->next->easy = config->easy; 1902 1903 /* Set the global config pointer */ 1904 operation->next->global = config; 1905 1906 /* Update the last operation pointer */ 1907 config->last = operation->next; 1908 1909 /* Move onto the new config */ 1910 operation->next->prev = operation; 1911 operation = operation->next; 1912 } 1913 else 1914 result = PARAM_NO_MEM; 1915 } 1916 } 1917 else if(!result && passarg) 1918 i++; /* we're supposed to skip this */ 1919 } 1920 } 1921 else { 1922 bool used; 1923 1924 /* Just add the URL please */ 1925 result = getparameter((char *)"--url", argv[i], &used, config, 1926 operation); 1927 } 1928 } 1929 1930 if(result && result != PARAM_HELP_REQUESTED && 1931 result != PARAM_MANUAL_REQUESTED && 1932 result != PARAM_VERSION_INFO_REQUESTED && 1933 result != PARAM_ENGINES_REQUESTED) { 1934 const char *reason = param2text(result); 1935 1936 if(orig_opt && !curlx_strequal(":", orig_opt)) 1937 helpf(config->errors, "option %s: %s\n", orig_opt, reason); 1938 else 1939 helpf(config->errors, "%s\n", reason); 1940 } 1941 1942 return result; 1943 } 1944