Imported Upstream version 7.48.0
[platform/upstream/curl.git] / src / tool_getparam.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, 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   /* all these ones, starting with "*" or "$" as a short-option have *no*
68      short option to mention. */
69   {"*",  "url",                      TRUE},
70   {"*4", "dns-ipv4-addr",            TRUE},
71   {"*6", "dns-ipv6-addr",            TRUE},
72   {"*a", "random-file",              TRUE},
73   {"*b", "egd-file",                 TRUE},
74   {"*B", "oauth2-bearer",             TRUE},
75   {"*c", "connect-timeout",          TRUE},
76   {"*d", "ciphers",                  TRUE},
77   {"*D", "dns-interface",            TRUE},
78   {"*e", "disable-epsv",             FALSE},
79   {"*E", "epsv",                     FALSE},
80          /* 'epsv' made like this to make --no-epsv and --epsv to work
81              although --disable-epsv is the documented option */
82 #ifdef USE_ENVIRONMENT
83   {"*f", "environment",              FALSE},
84 #endif
85   {"*F", "dns-servers",              TRUE},
86   {"*g", "trace",                    TRUE},
87   {"*G", "npn",                      FALSE},
88   {"*h", "trace-ascii",              TRUE},
89   {"*H", "alpn",                     FALSE},
90   {"*i", "limit-rate",               TRUE},
91   {"*j", "compressed",               FALSE},
92   {"*J", "tr-encoding",              FALSE},
93   {"*k", "digest",                   FALSE},
94   {"*l", "negotiate",                FALSE},
95   {"*m", "ntlm",                     FALSE},
96   {"*M", "ntlm-wb",                  FALSE},
97   {"*n", "basic",                    FALSE},
98   {"*o", "anyauth",                  FALSE},
99 #ifdef USE_WATT32
100   {"*p", "wdebug",                   FALSE},
101 #endif
102   {"*q", "ftp-create-dirs",          FALSE},
103   {"*r", "create-dirs",              FALSE},
104   {"*s", "max-redirs",               TRUE},
105   {"*t", "proxy-ntlm",               FALSE},
106   {"*u", "crlf",                     FALSE},
107   {"*v", "stderr",                   TRUE},
108   {"*w", "interface",                TRUE},
109   {"*x", "krb" ,                     TRUE},
110   {"*x", "krb4" ,                    TRUE},
111          /* 'krb4' is the previous name */
112   {"*y", "max-filesize",             TRUE},
113   {"*z", "disable-eprt",             FALSE},
114   {"*Z", "eprt",                     FALSE},
115          /* 'eprt' made like this to make --no-eprt and --eprt to work
116              although --disable-eprt is the documented option */
117   {"$a", "ftp-ssl",                  FALSE},
118          /* 'ftp-ssl' deprecated name since 7.20.0 */
119   {"$a", "ssl",                      FALSE},
120          /* 'ssl' new option name in 7.20.0, previously this was ftp-ssl */
121   {"$b", "ftp-pasv",                 FALSE},
122   {"$c", "socks5",                   TRUE},
123   {"$c", "socks",                    TRUE},
124          /* 'socks' is how the option once was documented but we prefer
125             the --socks5 version for explicit version */
126   {"$d", "tcp-nodelay",              FALSE},
127   {"$e", "proxy-digest",             FALSE},
128   {"$f", "proxy-basic",              FALSE},
129   {"$g", "retry",                    TRUE},
130   {"$h", "retry-delay",              TRUE},
131   {"$i", "retry-max-time",           TRUE},
132   {"$k", "proxy-negotiate",          FALSE},
133   {"$m", "ftp-account",              TRUE},
134   {"$n", "proxy-anyauth",            FALSE},
135   {"$o", "trace-time",               FALSE},
136   {"$p", "ignore-content-length",    FALSE},
137   {"$q", "ftp-skip-pasv-ip",         FALSE},
138   {"$r", "ftp-method",               TRUE},
139   {"$s", "local-port",               TRUE},
140   {"$t", "socks4",                   TRUE},
141   {"$T", "socks4a",                  TRUE},
142   {"$u", "ftp-alternative-to-user",  TRUE},
143   {"$v", "ftp-ssl-reqd",             FALSE},
144          /* 'ftp-ssl-reqd' deprecated name since 7.20.0 */
145   {"$v", "ssl-reqd",                 FALSE},
146          /* 'ssl-reqd' new in 7.20.0, previously this was ftp-ssl-reqd */
147   {"$w", "sessionid",                FALSE},
148          /* 'sessionid' listed as --no-sessionid in the help */
149   {"$x", "ftp-ssl-control",          FALSE},
150   {"$y", "ftp-ssl-ccc",              FALSE},
151   {"$j", "ftp-ssl-ccc-mode",         TRUE},
152   {"$z", "libcurl",                  TRUE},
153   {"$#", "raw",                      FALSE},
154   {"$0", "post301",                  FALSE},
155   {"$1", "keepalive",                FALSE},
156          /* 'keepalive' listed as --no-keepalive in the help */
157   {"$2", "socks5-hostname",          TRUE},
158   {"$3", "keepalive-time",           TRUE},
159   {"$4", "post302",                  FALSE},
160   {"$5", "noproxy",                  TRUE},
161   {"$6", "socks5-gssapi-service",    TRUE},
162   {"$7", "socks5-gssapi-nec",        FALSE},
163   {"$8", "proxy1.0",                 TRUE},
164   {"$9", "tftp-blksize",             TRUE},
165   {"$A", "mail-from",                TRUE},
166   {"$B", "mail-rcpt",                TRUE},
167   {"$C", "ftp-pret",                 FALSE},
168   {"$D", "proto",                    TRUE},
169   {"$E", "proto-redir",              TRUE},
170   {"$F", "resolve",                  TRUE},
171   {"$G", "delegation",               TRUE},
172   {"$H", "mail-auth",                TRUE},
173   {"$I", "post303",                  FALSE},
174   {"$J", "metalink",                 FALSE},
175   {"$K", "sasl-ir",                  FALSE},
176   {"$L", "test-event",               FALSE},
177   {"$M", "unix-socket",              TRUE},
178   {"$N", "path-as-is",               FALSE},
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   {"0",   "http1.0",                 FALSE},
185   {"01",  "http1.1",                 FALSE},
186   {"02",  "http2",                   FALSE},
187   {"1",  "tlsv1",                    FALSE},
188   {"10",  "tlsv1.0",                 FALSE},
189   {"11",  "tlsv1.1",                 FALSE},
190   {"12",  "tlsv1.2",                 FALSE},
191   {"2",  "sslv2",                    FALSE},
192   {"3",  "sslv3",                    FALSE},
193   {"4",  "ipv4",                     FALSE},
194   {"6",  "ipv6",                     FALSE},
195   {"a",  "append",                   FALSE},
196   {"A",  "user-agent",               TRUE},
197   {"b",  "cookie",                   TRUE},
198   {"B",  "use-ascii",                FALSE},
199   {"c",  "cookie-jar",               TRUE},
200   {"C",  "continue-at",              TRUE},
201   {"d",  "data",                     TRUE},
202   {"dr", "data-raw",                 TRUE},
203   {"da", "data-ascii",               TRUE},
204   {"db", "data-binary",              TRUE},
205   {"de", "data-urlencode",           TRUE},
206   {"D",  "dump-header",              TRUE},
207   {"e",  "referer",                  TRUE},
208   {"E",  "cert",                     TRUE},
209   {"Ea", "cacert",                   TRUE},
210   {"Eb", "cert-type",                TRUE},
211   {"Ec", "key",                      TRUE},
212   {"Ed", "key-type",                 TRUE},
213   {"Ee", "pass",                     TRUE},
214   {"Ef", "engine",                   TRUE},
215   {"Eg", "capath ",                  TRUE},
216   {"Eh", "pubkey",                   TRUE},
217   {"Ei", "hostpubmd5",               TRUE},
218   {"Ej", "crlfile",                  TRUE},
219   {"Ek", "tlsuser",                  TRUE},
220   {"El", "tlspassword",              TRUE},
221   {"Em", "tlsauthtype",              TRUE},
222   {"En", "ssl-allow-beast",          FALSE},
223   {"Eo", "login-options",            TRUE},
224   {"Ep", "pinnedpubkey",             TRUE},
225   {"Eq", "cert-status",              FALSE},
226   {"Er", "false-start",              FALSE},
227   {"Es", "ssl-no-revoke",            FALSE},
228   {"f",  "fail",                     FALSE},
229   {"F",  "form",                     TRUE},
230   {"Fs", "form-string",              TRUE},
231   {"g",  "globoff",                  FALSE},
232   {"G",  "get",                      FALSE},
233   {"h",  "help",                     FALSE},
234   {"H",  "header",                   TRUE},
235   {"Hp", "proxy-header",             TRUE},
236   {"i",  "include",                  FALSE},
237   {"I",  "head",                     FALSE},
238   {"j",  "junk-session-cookies",     FALSE},
239   {"J",  "remote-header-name",       FALSE},
240   {"k",  "insecure",                 FALSE},
241   {"K",  "config",                   TRUE},
242   {"l",  "list-only",                FALSE},
243   {"L",  "location",                 FALSE},
244   {"Lt", "location-trusted",         FALSE},
245   {"m",  "max-time",                 TRUE},
246   {"M",  "manual",                   FALSE},
247   {"n",  "netrc",                    FALSE},
248   {"no", "netrc-optional",           FALSE},
249   {"ne", "netrc-file",               TRUE},
250   {"N",  "buffer",                   FALSE},
251          /* 'buffer' listed as --no-buffer in the help */
252   {"o",  "output",                   TRUE},
253   {"O",  "remote-name",              FALSE},
254   {"Oa", "remote-name-all",          FALSE},
255   {"p",  "proxytunnel",              FALSE},
256   {"P",  "ftpport",                  TRUE},
257          /* 'ftpport' old version */
258   {"P",  "ftp-port",                 TRUE},
259   {"q",  "disable",                  FALSE},
260   {"Q",  "quote",                    TRUE},
261   {"r",  "range",                    TRUE},
262   {"R",  "remote-time",              FALSE},
263   {"s",  "silent",                   FALSE},
264   {"S",  "show-error",               FALSE},
265   {"t",  "telnet-options",           TRUE},
266          /* 'telnet-options' documented as telnet-option */
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   {"X",  "http-request",             TRUE},
276          /* 'http-request' OBSOLETE VERSION */
277   {"Y",  "speed-limit",              TRUE},
278   {"y",  "speed-time",               TRUE},
279   {"z",  "time-cond",                TRUE},
280   {"#",  "progress-bar",             FALSE},
281   {":",  "next",                     FALSE},
282   {"~",  "xattr",                    FALSE},
283 };
284
285 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
286  * We allow ':' and '\' to be escaped by '\' so that we can use certificate
287  * nicknames containing ':'.  See <https://sourceforge.net/p/curl/bugs/1196/>
288  * for details. */
289 #ifndef UNITTESTS
290 static
291 #endif
292 void parse_cert_parameter(const char *cert_parameter,
293                           char **certname,
294                           char **passphrase)
295 {
296   size_t param_length = strlen(cert_parameter);
297   size_t span;
298   const char *param_place = NULL;
299   char *certname_place = NULL;
300   *certname = NULL;
301   *passphrase = NULL;
302
303   /* most trivial assumption: cert_parameter is empty */
304   if(param_length == 0)
305     return;
306
307   /* next less trivial: cert_parameter contains no colon nor backslash; this
308    * means no passphrase was given and no characters escaped */
309   if(!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
722       default: /* the URL! */
723       {
724         struct getout *url;
725         if(config->url_get || ((config->url_get = config->url_list) != NULL)) {
726           /* there's a node here, if it already is filled-in continue to find
727              an "empty" node */
728           while(config->url_get && (config->url_get->flags & GETOUT_URL))
729             config->url_get = config->url_get->next;
730         }
731
732         /* now there might or might not be an available node to fill in! */
733
734         if(config->url_get)
735           /* existing node */
736           url = config->url_get;
737         else
738           /* there was no free node, create one! */
739           url = new_getout(config);
740
741         if(!url)
742           return PARAM_NO_MEM;
743         else {
744           /* fill in the URL */
745           GetStr(&url->url, nextarg);
746           url->flags |= GETOUT_URL;
747         }
748       }
749       }
750       break;
751     case '$': /* more options without a short option */
752       switch(subletter) {
753       case 'a': /* --ftp-ssl */
754         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
755           return PARAM_LIBCURL_DOESNT_SUPPORT;
756         config->ftp_ssl = toggle;
757         break;
758       case 'b': /* --ftp-pasv */
759         Curl_safefree(config->ftpport);
760         break;
761       case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves
762                    the name locally and passes on the resolved address */
763         GetStr(&config->socksproxy, nextarg);
764         config->socksver = CURLPROXY_SOCKS5;
765         break;
766       case 't': /* --socks4 specifies a socks4 proxy to use */
767         GetStr(&config->socksproxy, nextarg);
768         config->socksver = CURLPROXY_SOCKS4;
769         break;
770       case 'T': /* --socks4a specifies a socks4a proxy to use */
771         GetStr(&config->socksproxy, nextarg);
772         config->socksver = CURLPROXY_SOCKS4A;
773         break;
774       case '2': /* --socks5-hostname specifies a socks5 proxy and enables name
775                    resolving with the proxy */
776         GetStr(&config->socksproxy, nextarg);
777         config->socksver = CURLPROXY_SOCKS5_HOSTNAME;
778         break;
779       case 'd': /* --tcp-nodelay option */
780         config->tcp_nodelay = toggle;
781         break;
782       case 'e': /* --proxy-digest */
783         config->proxydigest = toggle;
784         break;
785       case 'f': /* --proxy-basic */
786         config->proxybasic = toggle;
787         break;
788       case 'g': /* --retry */
789         err = str2unum(&config->req_retry, nextarg);
790         if(err)
791           return err;
792         break;
793       case 'h': /* --retry-delay */
794         err = str2unum(&config->retry_delay, nextarg);
795         if(err)
796           return err;
797         break;
798       case 'i': /* --retry-max-time */
799         err = str2unum(&config->retry_maxtime, nextarg);
800         if(err)
801           return err;
802         break;
803
804       case 'k': /* --proxy-negotiate */
805         if(curlinfo->features & CURL_VERSION_SPNEGO)
806           config->proxynegotiate = toggle;
807         else
808           return PARAM_LIBCURL_DOESNT_SUPPORT;
809         break;
810
811       case 'm': /* --ftp-account */
812         GetStr(&config->ftp_account, nextarg);
813         break;
814       case 'n': /* --proxy-anyauth */
815         config->proxyanyauth = toggle;
816         break;
817       case 'o': /* --trace-time */
818         global->tracetime = toggle;
819         break;
820       case 'p': /* --ignore-content-length */
821         config->ignorecl = toggle;
822         break;
823       case 'q': /* --ftp-skip-pasv-ip */
824         config->ftp_skip_ip = toggle;
825         break;
826       case 'r': /* --ftp-method (undocumented at this point) */
827         config->ftp_filemethod = ftpfilemethod(config, nextarg);
828         break;
829       case 's': /* --local-port */
830         rc = sscanf(nextarg, "%d - %d",
831                     &config->localport,
832                     &config->localportrange);
833         if(!rc)
834           return PARAM_BAD_USE;
835         else if(rc == 1)
836           config->localportrange = 1; /* default number of ports to try */
837         else {
838           config->localportrange -= config->localport;
839           if(config->localportrange < 1) {
840             warnf(global, "bad range input\n");
841             return PARAM_BAD_USE;
842           }
843         }
844         break;
845       case 'u': /* --ftp-alternative-to-user */
846         GetStr(&config->ftp_alternative_to_user, nextarg);
847         break;
848       case 'v': /* --ftp-ssl-reqd */
849         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
850           return PARAM_LIBCURL_DOESNT_SUPPORT;
851         config->ftp_ssl_reqd = toggle;
852         break;
853       case 'w': /* --no-sessionid */
854         config->disable_sessionid = (!toggle)?TRUE:FALSE;
855         break;
856       case 'x': /* --ftp-ssl-control */
857         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
858           return PARAM_LIBCURL_DOESNT_SUPPORT;
859         config->ftp_ssl_control = toggle;
860         break;
861       case 'y': /* --ftp-ssl-ccc */
862         config->ftp_ssl_ccc = toggle;
863         if(!config->ftp_ssl_ccc_mode)
864           config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
865         break;
866       case 'j': /* --ftp-ssl-ccc-mode */
867         config->ftp_ssl_ccc = TRUE;
868         config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
869         break;
870       case 'z': /* --libcurl */
871 #ifdef CURL_DISABLE_LIBCURL_OPTION
872         warnf(global,
873               "--libcurl option was disabled at build-time!\n");
874         return PARAM_OPTION_UNKNOWN;
875 #else
876         GetStr(&global->libcurl, nextarg);
877         break;
878 #endif
879       case '#': /* --raw */
880         config->raw = toggle;
881         break;
882       case '0': /* --post301 */
883         config->post301 = toggle;
884         break;
885       case '1': /* --no-keepalive */
886         config->nokeepalive = (!toggle)?TRUE:FALSE;
887         break;
888       case '3': /* --keepalive-time */
889         err = str2unum(&config->alivetime, nextarg);
890         if(err)
891           return err;
892         break;
893       case '4': /* --post302 */
894         config->post302 = toggle;
895         break;
896       case 'I': /* --post303 */
897         config->post303 = toggle;
898         break;
899       case '5': /* --noproxy */
900         /* This specifies the noproxy list */
901         GetStr(&config->noproxy, nextarg);
902         break;
903       case '6': /* --socks5-gssapi-service */
904         GetStr(&config->socks5_gssapi_service, nextarg);
905         break;
906       case '7': /* --socks5-gssapi-nec*/
907         config->socks5_gssapi_nec = toggle;
908         break;
909       case '8': /* --proxy1.0 */
910         /* http 1.0 proxy */
911         GetStr(&config->proxy, nextarg);
912         config->proxyver = CURLPROXY_HTTP_1_0;
913         break;
914       case '9': /* --tftp-blksize */
915         err = str2unum(&config->tftp_blksize, nextarg);
916         if(err)
917           return err;
918         break;
919       case 'A': /* --mail-from */
920         GetStr(&config->mail_from, nextarg);
921         break;
922       case 'B': /* --mail-rcpt */
923         /* append receiver to a list */
924         err = add2list(&config->mail_rcpt, nextarg);
925         if(err)
926           return err;
927         break;
928       case 'C': /* --ftp-pret */
929         config->ftp_pret = toggle;
930         break;
931       case 'D': /* --proto */
932         config->proto_present = TRUE;
933         if(proto2num(config, &config->proto, nextarg))
934           return PARAM_BAD_USE;
935         break;
936       case 'E': /* --proto-redir */
937         config->proto_redir_present = TRUE;
938         if(proto2num(config, &config->proto_redir, nextarg))
939           return PARAM_BAD_USE;
940         break;
941       case 'F': /* --resolve */
942         err = add2list(&config->resolve, nextarg);
943         if(err)
944           return err;
945         break;
946       case 'G': /* --delegation LEVEL */
947         config->gssapi_delegation = delegation(config, nextarg);
948         break;
949       case 'H': /* --mail-auth */
950         GetStr(&config->mail_auth, nextarg);
951         break;
952       case 'J': /* --metalink */
953         {
954 #ifdef USE_METALINK
955           int mlmaj, mlmin, mlpatch;
956           metalink_get_version(&mlmaj, &mlmin, &mlpatch);
957           if((mlmaj*10000)+(mlmin*100)+mlpatch < CURL_REQ_LIBMETALINK_VERS) {
958             warnf(global,
959                   "--metalink option cannot be used because the version of "
960                   "the linked libmetalink library is too old. "
961                   "Required: %d.%d.%d, found %d.%d.%d\n",
962                   CURL_REQ_LIBMETALINK_MAJOR,
963                   CURL_REQ_LIBMETALINK_MINOR,
964                   CURL_REQ_LIBMETALINK_PATCH,
965                   mlmaj, mlmin, mlpatch);
966             return PARAM_BAD_USE;
967           }
968           else
969             config->use_metalink = toggle;
970 #else
971           warnf(global, "--metalink option is ignored because the binary is "
972                 "built without the Metalink support.\n");
973 #endif
974           break;
975         }
976       case 'K': /* --sasl-ir */
977         config->sasl_ir = toggle;
978         break;
979       case 'L': /* --test-event */
980 #ifdef CURLDEBUG
981         config->test_event_based = toggle;
982 #else
983         warnf(global, "--test-event is ignored unless a debug build!\n");
984 #endif
985         break;
986       case 'M': /* --unix-socket */
987         GetStr(&config->unix_socket_path, nextarg);
988         break;
989       case 'N': /* --path-as-is */
990         config->path_as_is = toggle;
991         break;
992       case 'O': /* --proxy-service-name */
993         GetStr(&config->proxy_service_name, nextarg);
994         break;
995       case 'P': /* --service-name */
996         GetStr(&config->service_name, nextarg);
997         break;
998       case 'Q': /* --proto-default */
999         GetStr(&config->proto_default, nextarg);
1000         err = check_protocol(config->proto_default);
1001         if(err)
1002           return err;
1003         break;
1004       case 'R': /* --expect100-timeout */
1005         err = str2udouble(&config->expect100timeout, nextarg);
1006         if(err)
1007           return err;
1008         break;
1009       case 'S': /* --tftp-no-options */
1010         config->tftp_no_options = toggle;
1011         break;
1012       }
1013       break;
1014     case '#': /* --progress-bar */
1015       if(toggle)
1016         global->progressmode = CURL_PROGRESS_BAR;
1017       else
1018         global->progressmode = CURL_PROGRESS_STATS;
1019       break;
1020     case ':': /* --next */
1021       return PARAM_NEXT_OPERATION;
1022     case '~': /* --xattr */
1023       config->xattr = toggle;
1024       break;
1025     case '0': /* --http* options */
1026       switch(subletter) {
1027       case '\0':
1028         /* HTTP version 1.0 */
1029         config->httpversion = CURL_HTTP_VERSION_1_0;
1030         break;
1031       case '1':
1032         /* HTTP version 1.1 */
1033         config->httpversion = CURL_HTTP_VERSION_1_1;
1034         break;
1035       case '2':
1036         /* HTTP version 2.0 */
1037         config->httpversion = CURL_HTTP_VERSION_2_0;
1038         break;
1039       }
1040       break;
1041     case '1': /* --tlsv1* options */
1042       switch(subletter) {
1043       case '\0':
1044         /* TLS version 1.x */
1045         config->ssl_version = CURL_SSLVERSION_TLSv1;
1046         break;
1047       case '0':
1048         /* TLS version 1.0 */
1049         config->ssl_version = CURL_SSLVERSION_TLSv1_0;
1050         break;
1051       case '1':
1052         /* TLS version 1.1 */
1053         config->ssl_version = CURL_SSLVERSION_TLSv1_1;
1054         break;
1055       case '2':
1056         /* TLS version 1.2 */
1057         config->ssl_version = CURL_SSLVERSION_TLSv1_2;
1058         break;
1059       }
1060       break;
1061     case '2':
1062       /* SSL version 2 */
1063       config->ssl_version = CURL_SSLVERSION_SSLv2;
1064       break;
1065     case '3':
1066       /* SSL version 3 */
1067       config->ssl_version = CURL_SSLVERSION_SSLv3;
1068       break;
1069     case '4':
1070       /* IPv4 */
1071       config->ip_version = 4;
1072       break;
1073     case '6':
1074       /* IPv6 */
1075       config->ip_version = 6;
1076       break;
1077     case 'a':
1078       /* This makes the FTP sessions use APPE instead of STOR */
1079       config->ftp_append = toggle;
1080       break;
1081     case 'A':
1082       /* This specifies the User-Agent name */
1083       GetStr(&config->useragent, nextarg);
1084       break;
1085     case 'b': /* cookie string coming up: */
1086       if(nextarg[0] == '@') {
1087         nextarg++;
1088       }
1089       else if(strchr(nextarg, '=')) {
1090         /* A cookie string must have a =-letter */
1091         GetStr(&config->cookie, nextarg);
1092         break;
1093       }
1094       /* We have a cookie file to read from! */
1095       GetStr(&config->cookiefile, nextarg);
1096       break;
1097     case 'B':
1098       /* use ASCII/text when transferring */
1099       config->use_ascii = toggle;
1100       break;
1101     case 'c':
1102       /* get the file name to dump all cookies in */
1103       GetStr(&config->cookiejar, nextarg);
1104       break;
1105     case 'C':
1106       /* This makes us continue an ftp transfer at given position */
1107       if(!curlx_strequal(nextarg, "-")) {
1108         err = str2offset(&config->resume_from, nextarg);
1109         if(err)
1110           return err;
1111         config->resume_from_current = FALSE;
1112       }
1113       else {
1114         config->resume_from_current = TRUE;
1115         config->resume_from = 0;
1116       }
1117       config->use_resume=TRUE;
1118       break;
1119     case 'd':
1120       /* postfield data */
1121     {
1122       char *postdata = NULL;
1123       FILE *file;
1124       size_t size = 0;
1125       bool raw_mode = (subletter == 'r');
1126
1127       if(subletter == 'e') { /* --data-urlencode*/
1128         /* [name]=[content], we encode the content part only
1129          * [name]@[file name]
1130          *
1131          * Case 2: we first load the file using that name and then encode
1132          * the content.
1133          */
1134         const char *p = strchr(nextarg, '=');
1135         size_t nlen;
1136         char is_file;
1137         if(!p)
1138           /* there was no '=' letter, check for a '@' instead */
1139           p = strchr(nextarg, '@');
1140         if(p) {
1141           nlen = p - nextarg; /* length of the name part */
1142           is_file = *p++; /* pass the separator */
1143         }
1144         else {
1145           /* neither @ nor =, so no name and it isn't a file */
1146           nlen = is_file = 0;
1147           p = nextarg;
1148         }
1149         if('@' == is_file) {
1150           /* a '@' letter, it means that a file name or - (stdin) follows */
1151           if(curlx_strequal("-", p)) {
1152             file = stdin;
1153             set_binmode(stdin);
1154           }
1155           else {
1156             file = fopen(p, "rb");
1157             if(!file)
1158               warnf(global,
1159                     "Couldn't read data from file \"%s\", this makes "
1160                     "an empty POST.\n", nextarg);
1161           }
1162
1163           err = file2memory(&postdata, &size, file);
1164
1165           if(file && (file != stdin))
1166             fclose(file);
1167           if(err)
1168             return err;
1169         }
1170         else {
1171           GetStr(&postdata, p);
1172           if(postdata)
1173             size = strlen(postdata);
1174         }
1175
1176         if(!postdata) {
1177           /* no data from the file, point to a zero byte string to make this
1178              get sent as a POST anyway */
1179           postdata = strdup("");
1180           if(!postdata)
1181             return PARAM_NO_MEM;
1182           size = 0;
1183         }
1184         else {
1185           char *enc = curl_easy_escape(config->easy, postdata, (int)size);
1186           Curl_safefree(postdata); /* no matter if it worked or not */
1187           if(enc) {
1188             /* now make a string with the name from above and append the
1189                encoded string */
1190             size_t outlen = nlen + strlen(enc) + 2;
1191             char *n = malloc(outlen);
1192             if(!n) {
1193               curl_free(enc);
1194               return PARAM_NO_MEM;
1195             }
1196             if(nlen > 0) { /* only append '=' if we have a name */
1197               snprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
1198               size = outlen-1;
1199             }
1200             else {
1201               strcpy(n, enc);
1202               size = outlen-2; /* since no '=' was inserted */
1203             }
1204             curl_free(enc);
1205             postdata = n;
1206           }
1207           else
1208             return PARAM_NO_MEM;
1209         }
1210       }
1211       else if('@' == *nextarg && !raw_mode) {
1212         /* the data begins with a '@' letter, it means that a file name
1213            or - (stdin) follows */
1214         nextarg++; /* pass the @ */
1215
1216         if(curlx_strequal("-", nextarg)) {
1217           file = stdin;
1218           if(subletter == 'b') /* forced data-binary */
1219             set_binmode(stdin);
1220         }
1221         else {
1222           file = fopen(nextarg, "rb");
1223           if(!file)
1224             warnf(global, "Couldn't read data from file \"%s\", this makes "
1225                   "an empty POST.\n", nextarg);
1226         }
1227
1228         if(subletter == 'b')
1229           /* forced binary */
1230           err = file2memory(&postdata, &size, file);
1231         else {
1232           err = file2string(&postdata, file);
1233           if(postdata)
1234             size = strlen(postdata);
1235         }
1236
1237         if(file && (file != stdin))
1238           fclose(file);
1239         if(err)
1240           return err;
1241
1242         if(!postdata) {
1243           /* no data from the file, point to a zero byte string to make this
1244              get sent as a POST anyway */
1245           postdata = strdup("");
1246           if(!postdata)
1247             return PARAM_NO_MEM;
1248         }
1249       }
1250       else {
1251         GetStr(&postdata, nextarg);
1252         if(postdata)
1253           size = strlen(postdata);
1254       }
1255
1256 #ifdef CURL_DOES_CONVERSIONS
1257       if(subletter != 'b') {
1258         /* NOT forced binary, convert to ASCII */
1259         if(convert_to_network(postdata, strlen(postdata))) {
1260           Curl_safefree(postdata);
1261           return PARAM_NO_MEM;
1262         }
1263       }
1264 #endif
1265
1266       if(config->postfields) {
1267         /* we already have a string, we append this one with a separating
1268            &-letter */
1269         char *oldpost = config->postfields;
1270         curl_off_t oldlen = config->postfieldsize;
1271         curl_off_t newlen = oldlen + curlx_uztoso(size) + 2;
1272         config->postfields = malloc((size_t)newlen);
1273         if(!config->postfields) {
1274           Curl_safefree(oldpost);
1275           Curl_safefree(postdata);
1276           return PARAM_NO_MEM;
1277         }
1278         memcpy(config->postfields, oldpost, (size_t)oldlen);
1279         /* use byte value 0x26 for '&' to accommodate non-ASCII platforms */
1280         config->postfields[oldlen] = '\x26';
1281         memcpy(&config->postfields[oldlen+1], postdata, size);
1282         config->postfields[oldlen+1+size] = '\0';
1283         Curl_safefree(oldpost);
1284         Curl_safefree(postdata);
1285         config->postfieldsize += size+1;
1286       }
1287       else {
1288         config->postfields = postdata;
1289         config->postfieldsize = curlx_uztoso(size);
1290       }
1291     }
1292     /*
1293       We can't set the request type here, as this data might be used in
1294       a simple GET if -G is used. Already or soon.
1295
1296       if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) {
1297         Curl_safefree(postdata);
1298         return PARAM_BAD_USE;
1299       }
1300     */
1301     break;
1302     case 'D':
1303       /* dump-header to given file name */
1304       GetStr(&config->headerfile, nextarg);
1305       break;
1306     case 'e':
1307     {
1308       char *ptr = strstr(nextarg, ";auto");
1309       if(ptr) {
1310         /* Automatic referer requested, this may be combined with a
1311            set initial one */
1312         config->autoreferer = TRUE;
1313         *ptr = 0; /* zero terminate here */
1314       }
1315       else
1316         config->autoreferer = FALSE;
1317       GetStr(&config->referer, nextarg);
1318     }
1319     break;
1320     case 'E':
1321       switch(subletter) {
1322       case 'a': /* CA info PEM file */
1323         /* CA info PEM file */
1324         GetStr(&config->cacert, nextarg);
1325         break;
1326       case 'b': /* cert file type */
1327         GetStr(&config->cert_type, nextarg);
1328         break;
1329       case 'c': /* private key file */
1330         GetStr(&config->key, nextarg);
1331         break;
1332       case 'd': /* private key file type */
1333         GetStr(&config->key_type, nextarg);
1334         break;
1335       case 'e': /* private key passphrase */
1336         GetStr(&config->key_passwd, nextarg);
1337         cleanarg(nextarg);
1338         break;
1339       case 'f': /* crypto engine */
1340         GetStr(&config->engine, nextarg);
1341         if(config->engine && curlx_raw_equal(config->engine, "list"))
1342           return PARAM_ENGINES_REQUESTED;
1343         break;
1344       case 'g': /* CA info PEM file */
1345         /* CA cert directory */
1346         GetStr(&config->capath, nextarg);
1347         break;
1348       case 'h': /* --pubkey public key file */
1349         GetStr(&config->pubkey, nextarg);
1350         break;
1351       case 'i': /* --hostpubmd5 md5 of the host public key */
1352         GetStr(&config->hostpubmd5, nextarg);
1353         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
1354           return PARAM_BAD_USE;
1355         break;
1356       case 'j': /* CRL info PEM file */
1357         /* CRL file */
1358         GetStr(&config->crlfile, nextarg);
1359         break;
1360       case 'k': /* TLS username */
1361         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1362           GetStr(&config->tls_username, nextarg);
1363         else
1364           return PARAM_LIBCURL_DOESNT_SUPPORT;
1365         break;
1366       case 'l': /* TLS password */
1367         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1368           GetStr(&config->tls_password, nextarg);
1369         else
1370           return PARAM_LIBCURL_DOESNT_SUPPORT;
1371         break;
1372       case 'm': /* TLS authentication type */
1373         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
1374           GetStr(&config->tls_authtype, nextarg);
1375           if(!strequal(config->tls_authtype, "SRP"))
1376             return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
1377         }
1378         else
1379           return PARAM_LIBCURL_DOESNT_SUPPORT;
1380         break;
1381       case 'n': /* no empty SSL fragments, --ssl-allow-beast */
1382         if(curlinfo->features & CURL_VERSION_SSL)
1383           config->ssl_allow_beast = toggle;
1384         break;
1385
1386       case 'o': /* --login-options */
1387         GetStr(&config->login_options, nextarg);
1388         break;
1389
1390       case 'p': /* Pinned public key DER file */
1391         /* Pinned public key DER file */
1392         GetStr(&config->pinnedpubkey, nextarg);
1393         break;
1394
1395       case 'q': /* --cert-status */
1396         config->verifystatus = TRUE;
1397         break;
1398
1399       case 'r': /* --false-start */
1400         config->falsestart = TRUE;
1401         break;
1402
1403       case 's': /* --ssl-no-revoke */
1404         if(curlinfo->features & CURL_VERSION_SSL)
1405           config->ssl_no_revoke = TRUE;
1406         break;
1407
1408       default: /* certificate file */
1409       {
1410         char *certname, *passphrase;
1411         parse_cert_parameter(nextarg, &certname, &passphrase);
1412         Curl_safefree(config->cert);
1413         config->cert = certname;
1414         if(passphrase) {
1415           Curl_safefree(config->key_passwd);
1416           config->key_passwd = passphrase;
1417         }
1418         cleanarg(nextarg);
1419       }
1420       }
1421       break;
1422     case 'f':
1423       /* fail hard on errors  */
1424       config->failonerror = toggle;
1425       break;
1426     case 'F':
1427       /* "form data" simulation, this is a little advanced so lets do our best
1428          to sort this out slowly and carefully */
1429       if(formparse(config,
1430                    nextarg,
1431                    &config->httppost,
1432                    &config->last_post,
1433                    (subletter=='s')?TRUE:FALSE)) /* 's' means literal string */
1434         return PARAM_BAD_USE;
1435       if(SetHTTPrequest(config, HTTPREQ_FORMPOST, &config->httpreq))
1436         return PARAM_BAD_USE;
1437       break;
1438
1439     case 'g': /* g disables URLglobbing */
1440       config->globoff = toggle;
1441       break;
1442
1443     case 'G': /* HTTP GET */
1444       config->use_httpget = TRUE;
1445       break;
1446
1447     case 'h': /* h for help */
1448       if(toggle) {
1449         return PARAM_HELP_REQUESTED;
1450       }
1451       /* we now actually support --no-help too! */
1452       break;
1453     case 'H':
1454       /* A custom header to append to a list */
1455       if(subletter == 'p') /* --proxy-header */
1456         err = add2list(&config->proxyheaders, nextarg);
1457       else
1458         err = add2list(&config->headers, nextarg);
1459       if(err)
1460         return err;
1461       break;
1462     case 'i':
1463       config->include_headers = toggle; /* include the headers as well in the
1464                                            general output stream */
1465       break;
1466     case 'j':
1467       config->cookiesession = toggle;
1468       break;
1469     case 'I':
1470       /*
1471        * no_body will imply include_headers later on
1472        */
1473       config->no_body = toggle;
1474       if(SetHTTPrequest(config,
1475                         (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
1476                         &config->httpreq))
1477         return PARAM_BAD_USE;
1478       break;
1479     case 'J': /* --remote-header-name */
1480       if(config->include_headers) {
1481         warnf(global,
1482               "--include and --remote-header-name cannot be combined.\n");
1483         return PARAM_BAD_USE;
1484       }
1485       config->content_disposition = toggle;
1486       break;
1487     case 'k': /* allow insecure SSL connects */
1488       config->insecure_ok = toggle;
1489       break;
1490     case 'K': /* parse config file */
1491       if(parseconfig(nextarg, global))
1492         warnf(global, "error trying read config from the '%s' file\n",
1493               nextarg);
1494       break;
1495     case 'l':
1496       config->dirlistonly = toggle; /* only list the names of the FTP dir */
1497       break;
1498     case 'L':
1499       config->followlocation = toggle; /* Follow Location: HTTP headers */
1500       switch (subletter) {
1501       case 't':
1502         /* Continue to send authentication (user+password) when following
1503          * locations, even when hostname changed */
1504         config->unrestricted_auth = toggle;
1505         break;
1506       }
1507       break;
1508     case 'm':
1509       /* specified max time */
1510       err = str2udouble(&config->timeout, nextarg);
1511       if(err)
1512         return err;
1513       break;
1514     case 'M': /* M for manual, huge help */
1515       if(toggle) { /* --no-manual shows no manual... */
1516 #ifdef USE_MANUAL
1517         return PARAM_MANUAL_REQUESTED;
1518 #else
1519         warnf(global,
1520               "built-in manual was disabled at build-time!\n");
1521         return PARAM_OPTION_UNKNOWN;
1522 #endif
1523       }
1524       break;
1525     case 'n':
1526       switch(subletter) {
1527       case 'o': /* CA info PEM file */
1528         /* use .netrc or URL */
1529         config->netrc_opt = toggle;
1530         break;
1531       case 'e': /* netrc-file */
1532         GetStr(&config->netrc_file, nextarg);
1533         break;
1534       default:
1535         /* pick info from .netrc, if this is used for http, curl will
1536            automatically enfore user+password with the request */
1537         config->netrc = toggle;
1538         break;
1539       }
1540       break;
1541     case 'N':
1542       /* disable the output I/O buffering. note that the option is called
1543          --buffer but is mostly used in the negative form: --no-buffer */
1544       if(longopt)
1545         config->nobuffer = (!toggle)?TRUE:FALSE;
1546       else
1547         config->nobuffer = toggle;
1548       break;
1549     case 'O': /* --remote-name */
1550       if(subletter == 'a') { /* --remote-name-all */
1551         config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
1552         break;
1553       }
1554       /* fall-through! */
1555     case 'o': /* --output */
1556       /* output file */
1557     {
1558       struct getout *url;
1559       if(config->url_out || ((config->url_out = config->url_list) != NULL)) {
1560         /* there's a node here, if it already is filled-in continue to find
1561            an "empty" node */
1562         while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1563           config->url_out = config->url_out->next;
1564       }
1565
1566       /* now there might or might not be an available node to fill in! */
1567
1568       if(config->url_out)
1569         /* existing node */
1570         url = config->url_out;
1571       else
1572         /* there was no free node, create one! */
1573         url = new_getout(config);
1574
1575       if(!url)
1576         return PARAM_NO_MEM;
1577       else {
1578         /* fill in the outfile */
1579         if('o' == letter) {
1580           GetStr(&url->outfile, nextarg);
1581           url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1582         }
1583         else {
1584           url->outfile = NULL; /* leave it */
1585           if(toggle)
1586             url->flags |= GETOUT_USEREMOTE;  /* switch on */
1587           else
1588             url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1589         }
1590         url->flags |= GETOUT_OUTFILE;
1591       }
1592     }
1593     break;
1594     case 'P':
1595       /* This makes the FTP sessions use PORT instead of PASV */
1596       /* use <eth0> or <192.168.10.10> style addresses. Anything except
1597          this will make us try to get the "default" address.
1598          NOTE: this is a changed behaviour since the released 4.1!
1599       */
1600       GetStr(&config->ftpport, nextarg);
1601       break;
1602     case 'p':
1603       /* proxy tunnel for non-http protocols */
1604       config->proxytunnel = toggle;
1605       break;
1606
1607     case 'q': /* if used first, already taken care of, we do it like
1608                  this so we don't cause an error! */
1609       break;
1610     case 'Q':
1611       /* QUOTE command to send to FTP server */
1612       switch(nextarg[0]) {
1613       case '-':
1614         /* prefixed with a dash makes it a POST TRANSFER one */
1615         nextarg++;
1616         err = add2list(&config->postquote, nextarg);
1617         break;
1618       case '+':
1619         /* prefixed with a plus makes it a just-before-transfer one */
1620         nextarg++;
1621         err = add2list(&config->prequote, nextarg);
1622         break;
1623       default:
1624         err = add2list(&config->quote, nextarg);
1625         break;
1626       }
1627       if(err)
1628         return err;
1629       break;
1630     case 'r':
1631       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
1632          (and won't actually be range by definition). The man page previously
1633          claimed that to be a good way, why this code is added to work-around
1634          it. */
1635       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
1636         char buffer[32];
1637         curl_off_t off;
1638         warnf(global,
1639               "A specified range MUST include at least one dash (-). "
1640               "Appending one for you!\n");
1641         off = curlx_strtoofft(nextarg, NULL, 10);
1642         snprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
1643         Curl_safefree(config->range);
1644         config->range = strdup(buffer);
1645         if(!config->range)
1646           return PARAM_NO_MEM;
1647       }
1648       {
1649         /* byte range requested */
1650         char *tmp_range;
1651         tmp_range = nextarg;
1652         while(*tmp_range != '\0') {
1653           if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
1654             warnf(global, "Invalid character is found in given range. "
1655                   "A specified range MUST have only digits in "
1656                   "\'start\'-\'stop\'. The server's response to this "
1657                   "request is uncertain.\n");
1658             break;
1659           }
1660           tmp_range++;
1661         }
1662         /* byte range requested */
1663         GetStr(&config->range, nextarg);
1664       }
1665       break;
1666     case 'R':
1667       /* use remote file's time */
1668       config->remote_time = toggle;
1669       break;
1670     case 's':
1671       /* don't show progress meter, don't show errors : */
1672       if(toggle)
1673         global->mute = global->noprogress = TRUE;
1674       else
1675         global->mute = global->noprogress = FALSE;
1676       if(global->showerror < 0)
1677         /* if still on the default value, set showerror to the reverse of
1678            toggle. This is to allow -S and -s to be used in an independent
1679            order but still have the same effect. */
1680         global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */
1681       break;
1682     case 'S':
1683       /* show errors */
1684       global->showerror = toggle?1:0; /* toggle on if used with -s */
1685       break;
1686     case 't':
1687       /* Telnet options */
1688       err = add2list(&config->telnet_options, nextarg);
1689       if(err)
1690         return err;
1691       break;
1692     case 'T':
1693       /* we are uploading */
1694     {
1695       struct getout *url;
1696       if(config->url_out || ((config->url_out = config->url_list) != NULL)) {
1697         /* there's a node here, if it already is filled-in continue to find
1698            an "empty" node */
1699         while(config->url_out && (config->url_out->flags & GETOUT_UPLOAD))
1700           config->url_out = config->url_out->next;
1701       }
1702
1703       /* now there might or might not be an available node to fill in! */
1704
1705       if(config->url_out)
1706         /* existing node */
1707         url = config->url_out;
1708       else
1709         /* there was no free node, create one! */
1710         url = new_getout(config);
1711
1712       if(!url)
1713         return PARAM_NO_MEM;
1714       else {
1715         url->flags |= GETOUT_UPLOAD; /* mark -T used */
1716         if(!*nextarg)
1717           url->flags |= GETOUT_NOUPLOAD;
1718         else {
1719           /* "-" equals stdin, but keep the string around for now */
1720           GetStr(&url->infile, nextarg);
1721         }
1722       }
1723     }
1724     break;
1725     case 'u':
1726       /* user:password  */
1727       GetStr(&config->userpwd, nextarg);
1728       cleanarg(nextarg);
1729       break;
1730     case 'U':
1731       /* Proxy user:password  */
1732       GetStr(&config->proxyuserpwd, nextarg);
1733       cleanarg(nextarg);
1734       break;
1735     case 'v':
1736       if(toggle) {
1737         /* the '%' thing here will cause the trace get sent to stderr */
1738         Curl_safefree(global->trace_dump);
1739         global->trace_dump = strdup("%");
1740         if(!global->trace_dump)
1741           return PARAM_NO_MEM;
1742         if(global->tracetype && (global->tracetype != TRACE_PLAIN))
1743           warnf(global,
1744                 "-v, --verbose overrides an earlier trace/verbose option\n");
1745         global->tracetype = TRACE_PLAIN;
1746       }
1747       else
1748         /* verbose is disabled here */
1749         global->tracetype = TRACE_NONE;
1750       break;
1751     case 'V':
1752       if(toggle)    /* --no-version yields no output! */
1753         return PARAM_VERSION_INFO_REQUESTED;
1754       break;
1755
1756     case 'w':
1757       /* get the output string */
1758       if('@' == *nextarg) {
1759         /* the data begins with a '@' letter, it means that a file name
1760            or - (stdin) follows */
1761         FILE *file;
1762         const char *fname;
1763         nextarg++; /* pass the @ */
1764         if(curlx_strequal("-", nextarg)) {
1765           fname = "<stdin>";
1766           file = stdin;
1767         }
1768         else {
1769           fname = nextarg;
1770           file = fopen(nextarg, FOPEN_READTEXT);
1771         }
1772         err = file2string(&config->writeout, file);
1773         if(file && (file != stdin))
1774           fclose(file);
1775         if(err)
1776           return err;
1777         if(!config->writeout)
1778           warnf(global, "Failed to read %s", fname);
1779       }
1780       else
1781         GetStr(&config->writeout, nextarg);
1782       break;
1783     case 'x':
1784       /* proxy */
1785       GetStr(&config->proxy, nextarg);
1786       config->proxyver = CURLPROXY_HTTP;
1787       break;
1788     case 'X':
1789       /* set custom request */
1790       GetStr(&config->customrequest, nextarg);
1791       break;
1792     case 'y':
1793       /* low speed time */
1794       err = str2unum(&config->low_speed_time, nextarg);
1795       if(err)
1796         return err;
1797       if(!config->low_speed_limit)
1798         config->low_speed_limit = 1;
1799       break;
1800     case 'Y':
1801       /* low speed limit */
1802       err = str2unum(&config->low_speed_limit, nextarg);
1803       if(err)
1804         return err;
1805       if(!config->low_speed_time)
1806         config->low_speed_time = 30;
1807       break;
1808     case 'z': /* time condition coming up */
1809       switch(*nextarg) {
1810       case '+':
1811         nextarg++;
1812         /* FALLTHROUGH */
1813       default:
1814         /* If-Modified-Since: (section 14.28 in RFC2068) */
1815         config->timecond = CURL_TIMECOND_IFMODSINCE;
1816         break;
1817       case '-':
1818         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
1819         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
1820         nextarg++;
1821         break;
1822       case '=':
1823         /* Last-Modified:  (section 14.29 in RFC2068) */
1824         config->timecond = CURL_TIMECOND_LASTMOD;
1825         nextarg++;
1826         break;
1827       }
1828       now = time(NULL);
1829       config->condtime=curl_getdate(nextarg, &now);
1830       if(-1 == (int)config->condtime) {
1831         /* now let's see if it is a file name to get the time from instead! */
1832         struct_stat statbuf;
1833         if(-1 == stat(nextarg, &statbuf)) {
1834           /* failed, remove time condition */
1835           config->timecond = CURL_TIMECOND_NONE;
1836           warnf(global,
1837                 "Illegal date format for -z, --timecond (and not "
1838                 "a file name). Disabling time condition. "
1839                 "See curl_getdate(3) for valid date syntax.\n");
1840         }
1841         else {
1842           /* pull the time out from the file */
1843           config->condtime = statbuf.st_mtime;
1844         }
1845       }
1846       break;
1847     default: /* unknown flag */
1848       return PARAM_OPTION_UNKNOWN;
1849     }
1850     hit = -1;
1851
1852   } while(!longopt && !singleopt && *++parse && !*usedarg);
1853
1854   return PARAM_OK;
1855 }
1856
1857 ParameterError parse_args(struct GlobalConfig *config, int argc,
1858                           argv_item_t argv[])
1859 {
1860   int i;
1861   bool stillflags;
1862   char *orig_opt = NULL;
1863   ParameterError result = PARAM_OK;
1864   struct OperationConfig *operation = config->first;
1865
1866   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
1867     orig_opt = argv[i];
1868
1869     if(stillflags && ('-' == argv[i][0])) {
1870       char *nextarg;
1871       bool passarg;
1872       char *flag = argv[i];
1873
1874       if(curlx_strequal("--", argv[i]))
1875         /* This indicates the end of the flags and thus enables the
1876            following (URL) argument to start with -. */
1877         stillflags = FALSE;
1878       else {
1879         nextarg = (i < (argc - 1)) ? argv[i + 1] : NULL;
1880
1881         result = getparameter(flag, nextarg, &passarg, config, operation);
1882         if(result == PARAM_NEXT_OPERATION) {
1883           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
1884              returned from this function */
1885           result = PARAM_OK;
1886
1887           if(operation->url_list && operation->url_list->url) {
1888             /* Allocate the next config */
1889             operation->next = malloc(sizeof(struct OperationConfig));
1890             if(operation->next) {
1891               /* Initialise the newly created config */
1892               config_init(operation->next);
1893
1894               /* Copy the easy handle */
1895               operation->next->easy = config->easy;
1896
1897               /* Set the global config pointer */
1898               operation->next->global = config;
1899
1900               /* Update the last operation pointer */
1901               config->last = operation->next;
1902
1903               /* Move onto the new config */
1904               operation->next->prev = operation;
1905               operation = operation->next;
1906             }
1907             else
1908               result = PARAM_NO_MEM;
1909           }
1910         }
1911         else if(!result && passarg)
1912           i++; /* we're supposed to skip this */
1913       }
1914     }
1915     else {
1916       bool used;
1917
1918       /* Just add the URL please */
1919       result = getparameter((char *)"--url", argv[i], &used, config,
1920                             operation);
1921     }
1922   }
1923
1924   if(result && result != PARAM_HELP_REQUESTED &&
1925      result != PARAM_MANUAL_REQUESTED &&
1926      result != PARAM_VERSION_INFO_REQUESTED &&
1927      result != PARAM_ENGINES_REQUESTED) {
1928     const char *reason = param2text(result);
1929
1930     if(orig_opt && !curlx_strequal(":", orig_opt))
1931       helpf(config->errors, "option %s: %s\n", orig_opt, reason);
1932     else
1933       helpf(config->errors, "%s\n", reason);
1934   }
1935
1936   return result;
1937 }