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