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