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