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