1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@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
parse_cert_parameter(const char * cert_parameter,char ** certname,char ** passphrase)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
getparameter(char * flag,char * nextarg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)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
parse_args(struct GlobalConfig * config,int argc,argv_item_t argv[])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