• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.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 "strcase.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_convert.h"
34 #include "tool_filetime.h"
35 #include "tool_formparse.h"
36 #include "tool_getparam.h"
37 #include "tool_helpers.h"
38 #include "tool_libinfo.h"
39 #include "tool_msgs.h"
40 #include "tool_paramhlp.h"
41 #include "tool_parsecfg.h"
42 #include "tool_main.h"
43 
44 #include "memdebug.h" /* keep this as LAST include */
45 
46 #ifdef MSDOS
47 #  define USE_WATT32
48 #endif
49 
50 #define GetStr(str,val) do { \
51   if(*(str)) { \
52     free(*(str)); \
53     *(str) = NULL; \
54   } \
55   if((val)) {              \
56     *(str) = strdup((val)); \
57     if(!(*(str)))          \
58       return PARAM_NO_MEM; \
59   } \
60 } while(0)
61 
62 struct LongShort {
63   const char *letter; /* short name option */
64   const char *lname;  /* long name option */
65   enum {
66     ARG_NONE,   /* stand-alone but not a boolean */
67     ARG_BOOL,   /* accepts a --no-[name] prefix */
68     ARG_STRING, /* requires an argument */
69     ARG_FILENAME /* requires an argument, usually a file name */
70   } desc;
71 };
72 
73 static const struct LongShort aliases[]= {
74   /* 'letter' strings with more than one character have *no* short option to
75      mention. */
76   {"*@", "url",                      ARG_STRING},
77   {"*4", "dns-ipv4-addr",            ARG_STRING},
78   {"*6", "dns-ipv6-addr",            ARG_STRING},
79   {"*a", "random-file",              ARG_FILENAME},
80   {"*b", "egd-file",                 ARG_STRING},
81   {"*B", "oauth2-bearer",            ARG_STRING},
82   {"*c", "connect-timeout",          ARG_STRING},
83   {"*C", "doh-url"        ,          ARG_STRING},
84   {"*d", "ciphers",                  ARG_STRING},
85   {"*D", "dns-interface",            ARG_STRING},
86   {"*e", "disable-epsv",             ARG_BOOL},
87   {"*f", "disallow-username-in-url", ARG_BOOL},
88   {"*E", "epsv",                     ARG_BOOL},
89          /* 'epsv' made like this to make --no-epsv and --epsv to work
90              although --disable-epsv is the documented option */
91   {"*F", "dns-servers",              ARG_STRING},
92   {"*g", "trace",                    ARG_FILENAME},
93   {"*G", "npn",                      ARG_BOOL},
94   {"*h", "trace-ascii",              ARG_FILENAME},
95   {"*H", "alpn",                     ARG_BOOL},
96   {"*i", "limit-rate",               ARG_STRING},
97   {"*j", "compressed",               ARG_BOOL},
98   {"*J", "tr-encoding",              ARG_BOOL},
99   {"*k", "digest",                   ARG_BOOL},
100   {"*l", "negotiate",                ARG_BOOL},
101   {"*m", "ntlm",                     ARG_BOOL},
102   {"*M", "ntlm-wb",                  ARG_BOOL},
103   {"*n", "basic",                    ARG_BOOL},
104   {"*o", "anyauth",                  ARG_BOOL},
105 #ifdef USE_WATT32
106   {"*p", "wdebug",                   ARG_BOOL},
107 #endif
108   {"*q", "ftp-create-dirs",          ARG_BOOL},
109   {"*r", "create-dirs",              ARG_BOOL},
110   {"*R", "create-file-mode",         ARG_STRING},
111   {"*s", "max-redirs",               ARG_STRING},
112   {"*t", "proxy-ntlm",               ARG_BOOL},
113   {"*u", "crlf",                     ARG_BOOL},
114   {"*v", "stderr",                   ARG_FILENAME},
115   {"*V", "aws-sigv4",                ARG_STRING},
116   {"*w", "interface",                ARG_STRING},
117   {"*x", "krb",                      ARG_STRING},
118   {"*x", "krb4",                     ARG_STRING},
119          /* 'krb4' is the previous name */
120   {"*X", "haproxy-protocol",         ARG_BOOL},
121   {"*y", "max-filesize",             ARG_STRING},
122   {"*z", "disable-eprt",             ARG_BOOL},
123   {"*Z", "eprt",                     ARG_BOOL},
124          /* 'eprt' made like this to make --no-eprt and --eprt to work
125              although --disable-eprt is the documented option */
126   {"*~", "xattr",                    ARG_BOOL},
127   {"$a", "ftp-ssl",                  ARG_BOOL},
128          /* 'ftp-ssl' deprecated name since 7.20.0 */
129   {"$a", "ssl",                      ARG_BOOL},
130          /* 'ssl' new option name in 7.20.0, previously this was ftp-ssl */
131   {"$b", "ftp-pasv",                 ARG_BOOL},
132   {"$c", "socks5",                   ARG_STRING},
133   {"$d", "tcp-nodelay",              ARG_BOOL},
134   {"$e", "proxy-digest",             ARG_BOOL},
135   {"$f", "proxy-basic",              ARG_BOOL},
136   {"$g", "retry",                    ARG_STRING},
137   {"$V", "retry-connrefused",        ARG_BOOL},
138   {"$h", "retry-delay",              ARG_STRING},
139   {"$i", "retry-max-time",           ARG_STRING},
140   {"$k", "proxy-negotiate",          ARG_BOOL},
141   {"$m", "ftp-account",              ARG_STRING},
142   {"$n", "proxy-anyauth",            ARG_BOOL},
143   {"$o", "trace-time",               ARG_BOOL},
144   {"$p", "ignore-content-length",    ARG_BOOL},
145   {"$q", "ftp-skip-pasv-ip",         ARG_BOOL},
146   {"$r", "ftp-method",               ARG_STRING},
147   {"$s", "local-port",               ARG_STRING},
148   {"$t", "socks4",                   ARG_STRING},
149   {"$T", "socks4a",                  ARG_STRING},
150   {"$u", "ftp-alternative-to-user",  ARG_STRING},
151   {"$v", "ftp-ssl-reqd",             ARG_BOOL},
152          /* 'ftp-ssl-reqd' deprecated name since 7.20.0 */
153   {"$v", "ssl-reqd",                 ARG_BOOL},
154          /* 'ssl-reqd' new in 7.20.0, previously this was ftp-ssl-reqd */
155   {"$w", "sessionid",                ARG_BOOL},
156          /* 'sessionid' listed as --no-sessionid in the help */
157   {"$x", "ftp-ssl-control",          ARG_BOOL},
158   {"$y", "ftp-ssl-ccc",              ARG_BOOL},
159   {"$j", "ftp-ssl-ccc-mode",         ARG_STRING},
160   {"$z", "libcurl",                  ARG_STRING},
161   {"$#", "raw",                      ARG_BOOL},
162   {"$0", "post301",                  ARG_BOOL},
163   {"$1", "keepalive",                ARG_BOOL},
164          /* 'keepalive' listed as --no-keepalive in the help */
165   {"$2", "socks5-hostname",          ARG_STRING},
166   {"$3", "keepalive-time",           ARG_STRING},
167   {"$4", "post302",                  ARG_BOOL},
168   {"$5", "noproxy",                  ARG_STRING},
169   {"$7", "socks5-gssapi-nec",        ARG_BOOL},
170   {"$8", "proxy1.0",                 ARG_STRING},
171   {"$9", "tftp-blksize",             ARG_STRING},
172   {"$A", "mail-from",                ARG_STRING},
173   {"$B", "mail-rcpt",                ARG_STRING},
174   {"$C", "ftp-pret",                 ARG_BOOL},
175   {"$D", "proto",                    ARG_STRING},
176   {"$E", "proto-redir",              ARG_STRING},
177   {"$F", "resolve",                  ARG_STRING},
178   {"$G", "delegation",               ARG_STRING},
179   {"$H", "mail-auth",                ARG_STRING},
180   {"$I", "post303",                  ARG_BOOL},
181   {"$J", "metalink",                 ARG_BOOL},
182   {"$6", "sasl-authzid",             ARG_STRING},
183   {"$K", "sasl-ir",                  ARG_BOOL },
184   {"$L", "test-event",               ARG_BOOL},
185   {"$M", "unix-socket",              ARG_FILENAME},
186   {"$N", "path-as-is",               ARG_BOOL},
187   {"$O", "socks5-gssapi-service",    ARG_STRING},
188          /* 'socks5-gssapi-service' merged with'proxy-service-name' and
189             deprecated since 7.49.0 */
190   {"$O", "proxy-service-name",       ARG_STRING},
191   {"$P", "service-name",             ARG_STRING},
192   {"$Q", "proto-default",            ARG_STRING},
193   {"$R", "expect100-timeout",        ARG_STRING},
194   {"$S", "tftp-no-options",          ARG_BOOL},
195   {"$U", "connect-to",               ARG_STRING},
196   {"$W", "abstract-unix-socket",     ARG_FILENAME},
197   {"$X", "tls-max",                  ARG_STRING},
198   {"$Y", "suppress-connect-headers", ARG_BOOL},
199   {"$Z", "compressed-ssh",           ARG_BOOL},
200   {"$~", "happy-eyeballs-timeout-ms", ARG_STRING},
201   {"$!", "retry-all-errors",         ARG_BOOL},
202   {"0",   "http1.0",                 ARG_NONE},
203   {"01",  "http1.1",                 ARG_NONE},
204   {"02",  "http2",                   ARG_NONE},
205   {"03",  "http2-prior-knowledge",   ARG_NONE},
206   {"04",  "http3",                   ARG_NONE},
207   {"09",  "http0.9",                 ARG_BOOL},
208   {"1",  "tlsv1",                    ARG_NONE},
209   {"10",  "tlsv1.0",                 ARG_NONE},
210   {"11",  "tlsv1.1",                 ARG_NONE},
211   {"12",  "tlsv1.2",                 ARG_NONE},
212   {"13",  "tlsv1.3",                 ARG_NONE},
213   {"1A", "tls13-ciphers",            ARG_STRING},
214   {"1B", "proxy-tls13-ciphers",      ARG_STRING},
215   {"2",  "sslv2",                    ARG_NONE},
216   {"3",  "sslv3",                    ARG_NONE},
217   {"4",  "ipv4",                     ARG_NONE},
218   {"6",  "ipv6",                     ARG_NONE},
219   {"a",  "append",                   ARG_BOOL},
220   {"A",  "user-agent",               ARG_STRING},
221   {"b",  "cookie",                   ARG_STRING},
222   {"ba", "alt-svc",                  ARG_STRING},
223   {"bb", "hsts",                     ARG_STRING},
224   {"B",  "use-ascii",                ARG_BOOL},
225   {"c",  "cookie-jar",               ARG_STRING},
226   {"C",  "continue-at",              ARG_STRING},
227   {"d",  "data",                     ARG_STRING},
228   {"dr", "data-raw",                 ARG_STRING},
229   {"da", "data-ascii",               ARG_STRING},
230   {"db", "data-binary",              ARG_STRING},
231   {"de", "data-urlencode",           ARG_STRING},
232   {"D",  "dump-header",              ARG_FILENAME},
233   {"e",  "referer",                  ARG_STRING},
234   {"E",  "cert",                     ARG_FILENAME},
235   {"Ea", "cacert",                   ARG_FILENAME},
236   {"Eb", "cert-type",                ARG_STRING},
237   {"Ec", "key",                      ARG_FILENAME},
238   {"Ed", "key-type",                 ARG_STRING},
239   {"Ee", "pass",                     ARG_STRING},
240   {"Ef", "engine",                   ARG_STRING},
241   {"Eg", "capath",                   ARG_FILENAME},
242   {"Eh", "pubkey",                   ARG_STRING},
243   {"Ei", "hostpubmd5",               ARG_STRING},
244   {"EF", "hostpubsha256",            ARG_STRING},
245   {"Ej", "crlfile",                  ARG_FILENAME},
246   {"Ek", "tlsuser",                  ARG_STRING},
247   {"El", "tlspassword",              ARG_STRING},
248   {"Em", "tlsauthtype",              ARG_STRING},
249   {"En", "ssl-allow-beast",          ARG_BOOL},
250   {"Eo", "ssl-auto-client-cert",     ARG_BOOL},
251   {"EO", "proxy-ssl-auto-client-cert", ARG_BOOL},
252   {"Ep", "pinnedpubkey",             ARG_STRING},
253   {"EP", "proxy-pinnedpubkey",       ARG_STRING},
254   {"Eq", "cert-status",              ARG_BOOL},
255   {"EQ", "doh-cert-status",          ARG_BOOL},
256   {"Er", "false-start",              ARG_BOOL},
257   {"Es", "ssl-no-revoke",            ARG_BOOL},
258   {"ES", "ssl-revoke-best-effort",   ARG_BOOL},
259   {"Et", "tcp-fastopen",             ARG_BOOL},
260   {"Eu", "proxy-tlsuser",            ARG_STRING},
261   {"Ev", "proxy-tlspassword",        ARG_STRING},
262   {"Ew", "proxy-tlsauthtype",        ARG_STRING},
263   {"Ex", "proxy-cert",               ARG_FILENAME},
264   {"Ey", "proxy-cert-type",          ARG_STRING},
265   {"Ez", "proxy-key",                ARG_FILENAME},
266   {"E0", "proxy-key-type",           ARG_STRING},
267   {"E1", "proxy-pass",               ARG_STRING},
268   {"E2", "proxy-ciphers",            ARG_STRING},
269   {"E3", "proxy-crlfile",            ARG_FILENAME},
270   {"E4", "proxy-ssl-allow-beast",    ARG_BOOL},
271   {"E5", "login-options",            ARG_STRING},
272   {"E6", "proxy-cacert",             ARG_FILENAME},
273   {"E7", "proxy-capath",             ARG_FILENAME},
274   {"E8", "proxy-insecure",           ARG_BOOL},
275   {"E9", "proxy-tlsv1",              ARG_NONE},
276   {"EA", "socks5-basic",             ARG_BOOL},
277   {"EB", "socks5-gssapi",            ARG_BOOL},
278   {"EC", "etag-save",                ARG_FILENAME},
279   {"ED", "etag-compare",             ARG_FILENAME},
280   {"EE", "curves",                   ARG_STRING},
281   {"f",  "fail",                     ARG_BOOL},
282   {"fa", "fail-early",               ARG_BOOL},
283   {"fb", "styled-output",            ARG_BOOL},
284   {"fc", "mail-rcpt-allowfails",     ARG_BOOL},
285   {"fd", "fail-with-body",           ARG_BOOL},
286   {"F",  "form",                     ARG_STRING},
287   {"Fs", "form-string",              ARG_STRING},
288   {"g",  "globoff",                  ARG_BOOL},
289   {"G",  "get",                      ARG_NONE},
290   {"Ga", "request-target",           ARG_STRING},
291   {"h",  "help",                     ARG_BOOL},
292   {"H",  "header",                   ARG_STRING},
293   {"Hp", "proxy-header",             ARG_STRING},
294   {"i",  "include",                  ARG_BOOL},
295   {"I",  "head",                     ARG_BOOL},
296   {"j",  "junk-session-cookies",     ARG_BOOL},
297   {"J",  "remote-header-name",       ARG_BOOL},
298   {"k",  "insecure",                 ARG_BOOL},
299   {"kd", "doh-insecure",             ARG_BOOL},
300   {"K",  "config",                   ARG_FILENAME},
301   {"l",  "list-only",                ARG_BOOL},
302   {"L",  "location",                 ARG_BOOL},
303   {"Lt", "location-trusted",         ARG_BOOL},
304   {"m",  "max-time",                 ARG_STRING},
305   {"M",  "manual",                   ARG_BOOL},
306   {"n",  "netrc",                    ARG_BOOL},
307   {"no", "netrc-optional",           ARG_BOOL},
308   {"ne", "netrc-file",               ARG_FILENAME},
309   {"N",  "buffer",                   ARG_BOOL},
310          /* 'buffer' listed as --no-buffer in the help */
311   {"o",  "output",                   ARG_FILENAME},
312   {"O",  "remote-name",              ARG_NONE},
313   {"Oa", "remote-name-all",          ARG_BOOL},
314   {"Ob", "output-dir",               ARG_STRING},
315   {"p",  "proxytunnel",              ARG_BOOL},
316   {"P",  "ftp-port",                 ARG_STRING},
317   {"q",  "disable",                  ARG_BOOL},
318   {"Q",  "quote",                    ARG_STRING},
319   {"r",  "range",                    ARG_STRING},
320   {"R",  "remote-time",              ARG_BOOL},
321   {"s",  "silent",                   ARG_BOOL},
322   {"S",  "show-error",               ARG_BOOL},
323   {"t",  "telnet-option",            ARG_STRING},
324   {"T",  "upload-file",              ARG_FILENAME},
325   {"u",  "user",                     ARG_STRING},
326   {"U",  "proxy-user",               ARG_STRING},
327   {"v",  "verbose",                  ARG_BOOL},
328   {"V",  "version",                  ARG_BOOL},
329   {"w",  "write-out",                ARG_STRING},
330   {"x",  "proxy",                    ARG_STRING},
331   {"xa", "preproxy",                 ARG_STRING},
332   {"X",  "request",                  ARG_STRING},
333   {"Y",  "speed-limit",              ARG_STRING},
334   {"y",  "speed-time",               ARG_STRING},
335   {"z",  "time-cond",                ARG_STRING},
336   {"Z",  "parallel",                 ARG_BOOL},
337   {"Zb", "parallel-max",             ARG_STRING},
338   {"Zc", "parallel-immediate",       ARG_BOOL},
339   {"#",  "progress-bar",             ARG_BOOL},
340   {"#m", "progress-meter",           ARG_BOOL},
341   {":",  "next",                     ARG_NONE},
342 };
343 
344 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
345  * We allow ':' and '\' to be escaped by '\' so that we can use certificate
346  * nicknames containing ':'.  See <https://sourceforge.net/p/curl/bugs/1196/>
347  * for details. */
348 #ifndef UNITTESTS
349 static
350 #endif
parse_cert_parameter(const char * cert_parameter,char ** certname,char ** passphrase)351 void parse_cert_parameter(const char *cert_parameter,
352                           char **certname,
353                           char **passphrase)
354 {
355   size_t param_length = strlen(cert_parameter);
356   size_t span;
357   const char *param_place = NULL;
358   char *certname_place = NULL;
359   *certname = NULL;
360   *passphrase = NULL;
361 
362   /* most trivial assumption: cert_parameter is empty */
363   if(param_length == 0)
364     return;
365 
366   /* next less trivial: cert_parameter starts 'pkcs11:' and thus
367    * looks like a RFC7512 PKCS#11 URI which can be used as-is.
368    * Also if cert_parameter contains no colon nor backslash, this
369    * means no passphrase was given and no characters escaped */
370   if(curl_strnequal(cert_parameter, "pkcs11:", 7) ||
371      !strpbrk(cert_parameter, ":\\")) {
372     *certname = strdup(cert_parameter);
373     return;
374   }
375   /* deal with escaped chars; find unescaped colon if it exists */
376   certname_place = malloc(param_length + 1);
377   if(!certname_place)
378     return;
379 
380   *certname = certname_place;
381   param_place = cert_parameter;
382   while(*param_place) {
383     span = strcspn(param_place, ":\\");
384     strncpy(certname_place, param_place, span);
385     param_place += span;
386     certname_place += span;
387     /* we just ate all the non-special chars. now we're on either a special
388      * char or the end of the string. */
389     switch(*param_place) {
390     case '\0':
391       break;
392     case '\\':
393       param_place++;
394       switch(*param_place) {
395         case '\0':
396           *certname_place++ = '\\';
397           break;
398         case '\\':
399           *certname_place++ = '\\';
400           param_place++;
401           break;
402         case ':':
403           *certname_place++ = ':';
404           param_place++;
405           break;
406         default:
407           *certname_place++ = '\\';
408           *certname_place++ = *param_place;
409           param_place++;
410           break;
411       }
412       break;
413     case ':':
414       /* Since we live in a world of weirdness and confusion, the win32
415          dudes can use : when using drive letters and thus c:\file:password
416          needs to work. In order not to break compatibility, we still use : as
417          separator, but we try to detect when it is used for a file name! On
418          windows. */
419 #ifdef WIN32
420       if(param_place &&
421           (param_place == &cert_parameter[1]) &&
422           (cert_parameter[2] == '\\' || cert_parameter[2] == '/') &&
423           (ISALPHA(cert_parameter[0])) ) {
424         /* colon in the second column, followed by a backslash, and the
425            first character is an alphabetic letter:
426 
427            this is a drive letter colon */
428         *certname_place++ = ':';
429         param_place++;
430         break;
431       }
432 #endif
433       /* escaped colons and Windows drive letter colons were handled
434        * above; if we're still here, this is a separating colon */
435       param_place++;
436       if(*param_place) {
437         *passphrase = strdup(param_place);
438       }
439       goto done;
440     }
441   }
442 done:
443   *certname_place = '\0';
444 }
445 
446 /* Replace (in-place) '%20' by '+' according to RFC1866 */
replace_url_encoded_space_by_plus(char * url)447 static size_t replace_url_encoded_space_by_plus(char *url)
448 {
449   size_t orig_len = strlen(url);
450   size_t orig_index = 0;
451   size_t new_index = 0;
452 
453   while(orig_index < orig_len) {
454     if((url[orig_index] == '%') &&
455        (url[orig_index + 1] == '2') &&
456        (url[orig_index + 2] == '0')) {
457       url[new_index] = '+';
458       orig_index += 3;
459     }
460     else{
461       if(new_index != orig_index) {
462         url[new_index] = url[orig_index];
463       }
464       orig_index++;
465     }
466     new_index++;
467   }
468 
469   url[new_index] = 0; /* terminate string */
470 
471   return new_index; /* new size */
472 }
473 
474 static void
GetFileAndPassword(char * nextarg,char ** file,char ** password)475 GetFileAndPassword(char *nextarg, char **file, char **password)
476 {
477   char *certname, *passphrase;
478   parse_cert_parameter(nextarg, &certname, &passphrase);
479   Curl_safefree(*file);
480   *file = certname;
481   if(passphrase) {
482     Curl_safefree(*password);
483     *password = passphrase;
484   }
485   cleanarg(nextarg);
486 }
487 
488 /* Get a size parameter for '--limit-rate' or '--max-filesize'.
489  * We support a 'G', 'M' or 'K' suffix too.
490   */
GetSizeParameter(struct GlobalConfig * global,const char * arg,const char * which,curl_off_t * value_out)491 static ParameterError GetSizeParameter(struct GlobalConfig *global,
492                                        const char *arg,
493                                        const char *which,
494                                        curl_off_t *value_out)
495 {
496   char *unit;
497   curl_off_t value;
498 
499   if(curlx_strtoofft(arg, &unit, 0, &value)) {
500     warnf(global, "invalid number specified for %s\n", which);
501     return PARAM_BAD_USE;
502   }
503 
504   if(!*unit)
505     unit = (char *)"b";
506   else if(strlen(unit) > 1)
507     unit = (char *)"w"; /* unsupported */
508 
509   switch(*unit) {
510   case 'G':
511   case 'g':
512     if(value > (CURL_OFF_T_MAX / (1024*1024*1024)))
513       return PARAM_NUMBER_TOO_LARGE;
514     value *= 1024*1024*1024;
515     break;
516   case 'M':
517   case 'm':
518     if(value > (CURL_OFF_T_MAX / (1024*1024)))
519       return PARAM_NUMBER_TOO_LARGE;
520     value *= 1024*1024;
521     break;
522   case 'K':
523   case 'k':
524     if(value > (CURL_OFF_T_MAX / 1024))
525       return PARAM_NUMBER_TOO_LARGE;
526     value *= 1024;
527     break;
528   case 'b':
529   case 'B':
530     /* for plain bytes, leave as-is */
531     break;
532   default:
533     warnf(global, "unsupported %s unit. Use G, M, K or B!\n", which);
534     return PARAM_BAD_USE;
535   }
536   *value_out = value;
537   return PARAM_OK;
538 }
539 
getparameter(const char * flag,char * nextarg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)540 ParameterError getparameter(const char *flag, /* f or -long-flag */
541                             char *nextarg,    /* NULL if unset */
542                             bool *usedarg,    /* set to TRUE if the arg
543                                                  has been used */
544                             struct GlobalConfig *global,
545                             struct OperationConfig *config)
546 {
547   char letter;
548   char subletter = '\0'; /* subletters can only occur on long options */
549   int rc;
550   const char *parse = NULL;
551   unsigned int j;
552   time_t now;
553   int hit = -1;
554   bool longopt = FALSE;
555   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
556   ParameterError err;
557   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
558                          by using --OPTION or --no-OPTION */
559 
560   *usedarg = FALSE; /* default is that we don't use the arg */
561 
562   if(('-' != flag[0]) || ('-' == flag[1])) {
563     /* this should be a long name */
564     const char *word = ('-' == flag[0]) ? flag + 2 : flag;
565     size_t fnam = strlen(word);
566     int numhits = 0;
567     bool noflagged = FALSE;
568 
569     if(!strncmp(word, "no-", 3)) {
570       /* disable this option but ignore the "no-" part when looking for it */
571       word += 3;
572       toggle = FALSE;
573       noflagged = TRUE;
574     }
575 
576     for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
577       if(curl_strnequal(aliases[j].lname, word, fnam)) {
578         longopt = TRUE;
579         numhits++;
580         if(curl_strequal(aliases[j].lname, word)) {
581           parse = aliases[j].letter;
582           hit = j;
583           numhits = 1; /* a single unique hit */
584           break;
585         }
586         parse = aliases[j].letter;
587         hit = j;
588       }
589     }
590     if(numhits > 1) {
591       /* this is at least the second match! */
592       return PARAM_OPTION_AMBIGUOUS;
593     }
594     if(hit < 0) {
595       return PARAM_OPTION_UNKNOWN;
596     }
597     if(noflagged && (aliases[hit].desc != ARG_BOOL))
598       /* --no- prefixed an option that isn't boolean! */
599       return PARAM_NO_NOT_BOOLEAN;
600   }
601   else {
602     flag++; /* prefixed with one dash, pass it */
603     hit = -1;
604     parse = flag;
605   }
606 
607   do {
608     /* we can loop here if we have multiple single-letters */
609 
610     if(!longopt) {
611       letter = (char)*parse;
612       subletter = '\0';
613     }
614     else {
615       letter = parse[0];
616       subletter = parse[1];
617     }
618 
619     if(hit < 0) {
620       for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
621         if(letter == aliases[j].letter[0]) {
622           hit = j;
623           break;
624         }
625       }
626       if(hit < 0) {
627         return PARAM_OPTION_UNKNOWN;
628       }
629     }
630 
631     if(aliases[hit].desc >= ARG_STRING) {
632       /* this option requires an extra parameter */
633       if(!longopt && parse[1]) {
634         nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
635         singleopt = TRUE;   /* don't loop anymore after this */
636       }
637       else if(!nextarg)
638         return PARAM_REQUIRES_PARAMETER;
639       else
640         *usedarg = TRUE; /* mark it as used */
641 
642       if((aliases[hit].desc == ARG_FILENAME) &&
643          (nextarg[0] == '-') && nextarg[1]) {
644         /* if the file name looks like a command line option */
645         warnf(global, "The file name argument '%s' looks like a flag.\n",
646               nextarg);
647       }
648     }
649     else if((aliases[hit].desc == ARG_NONE) && !toggle)
650       return PARAM_NO_PREFIX;
651 
652     switch(letter) {
653     case '*': /* options without a short option */
654       switch(subletter) {
655       case '4': /* --dns-ipv4-addr */
656         /* addr in dot notation */
657         GetStr(&config->dns_ipv4_addr, nextarg);
658         break;
659       case '6': /* --dns-ipv6-addr */
660         /* addr in dot notation */
661         GetStr(&config->dns_ipv6_addr, nextarg);
662         break;
663       case 'a': /* random-file */
664         GetStr(&config->random_file, nextarg);
665         break;
666       case 'b': /* egd-file */
667         GetStr(&config->egd_file, nextarg);
668         break;
669       case 'B': /* OAuth 2.0 bearer token */
670         GetStr(&config->oauth_bearer, nextarg);
671         config->authtype |= CURLAUTH_BEARER;
672         break;
673       case 'c': /* connect-timeout */
674         err = str2udouble(&config->connecttimeout, nextarg,
675                           LONG_MAX/1000);
676         if(err)
677           return err;
678         break;
679       case 'C': /* doh-url */
680         GetStr(&config->doh_url, nextarg);
681         break;
682       case 'd': /* ciphers */
683         GetStr(&config->cipher_list, nextarg);
684         break;
685       case 'D': /* --dns-interface */
686         /* interface name */
687         GetStr(&config->dns_interface, nextarg);
688         break;
689       case 'e': /* --disable-epsv */
690         config->disable_epsv = toggle;
691         break;
692       case 'f': /* --disallow-username-in-url */
693         config->disallow_username_in_url = toggle;
694         break;
695       case 'E': /* --epsv */
696         config->disable_epsv = (!toggle)?TRUE:FALSE;
697         break;
698       case 'F': /* --dns-servers */
699         /* IP addrs of DNS servers */
700         GetStr(&config->dns_servers, nextarg);
701         break;
702       case 'g': /* --trace */
703         GetStr(&global->trace_dump, nextarg);
704         if(global->tracetype && (global->tracetype != TRACE_BIN))
705           warnf(global, "--trace overrides an earlier trace/verbose option\n");
706         global->tracetype = TRACE_BIN;
707         break;
708       case 'G': /* --npn */
709         config->nonpn = (!toggle)?TRUE:FALSE;
710         break;
711       case 'h': /* --trace-ascii */
712         GetStr(&global->trace_dump, nextarg);
713         if(global->tracetype && (global->tracetype != TRACE_ASCII))
714           warnf(global,
715                 "--trace-ascii overrides an earlier trace/verbose option\n");
716         global->tracetype = TRACE_ASCII;
717         break;
718       case 'H': /* --alpn */
719         config->noalpn = (!toggle)?TRUE:FALSE;
720         break;
721       case 'i': /* --limit-rate */
722       {
723         curl_off_t value;
724         ParameterError pe = GetSizeParameter(global, nextarg, "rate", &value);
725 
726         if(pe != PARAM_OK)
727            return pe;
728         config->recvpersecond = value;
729         config->sendpersecond = value;
730       }
731       break;
732 
733       case 'j': /* --compressed */
734         if(toggle &&
735            !(curlinfo->features & (CURL_VERSION_LIBZ |
736                                    CURL_VERSION_BROTLI | CURL_VERSION_ZSTD)))
737           return PARAM_LIBCURL_DOESNT_SUPPORT;
738         config->encoding = toggle;
739         break;
740 
741       case 'J': /* --tr-encoding */
742         config->tr_encoding = toggle;
743         break;
744 
745       case 'k': /* --digest */
746         if(toggle)
747           config->authtype |= CURLAUTH_DIGEST;
748         else
749           config->authtype &= ~CURLAUTH_DIGEST;
750         break;
751 
752       case 'l': /* --negotiate */
753         if(toggle) {
754           if(curlinfo->features & CURL_VERSION_SPNEGO)
755             config->authtype |= CURLAUTH_NEGOTIATE;
756           else
757             return PARAM_LIBCURL_DOESNT_SUPPORT;
758         }
759         else
760           config->authtype &= ~CURLAUTH_NEGOTIATE;
761         break;
762 
763       case 'm': /* --ntlm */
764         if(toggle) {
765           if(curlinfo->features & CURL_VERSION_NTLM)
766             config->authtype |= CURLAUTH_NTLM;
767           else
768             return PARAM_LIBCURL_DOESNT_SUPPORT;
769         }
770         else
771           config->authtype &= ~CURLAUTH_NTLM;
772         break;
773 
774       case 'M': /* --ntlm-wb */
775         if(toggle) {
776           if(curlinfo->features & CURL_VERSION_NTLM_WB)
777             config->authtype |= CURLAUTH_NTLM_WB;
778           else
779             return PARAM_LIBCURL_DOESNT_SUPPORT;
780         }
781         else
782           config->authtype &= ~CURLAUTH_NTLM_WB;
783         break;
784 
785       case 'n': /* --basic for completeness */
786         if(toggle)
787           config->authtype |= CURLAUTH_BASIC;
788         else
789           config->authtype &= ~CURLAUTH_BASIC;
790         break;
791 
792       case 'o': /* --anyauth, let libcurl pick it */
793         if(toggle)
794           config->authtype = CURLAUTH_ANY;
795         /* --no-anyauth simply doesn't touch it */
796         break;
797 
798 #ifdef USE_WATT32
799       case 'p': /* --wdebug */
800         dbug_init();
801         break;
802 #endif
803       case 'q': /* --ftp-create-dirs */
804         config->ftp_create_dirs = toggle;
805         break;
806 
807       case 'r': /* --create-dirs */
808         config->create_dirs = toggle;
809         break;
810 
811       case 'R': /* --create-file-mode */
812         err = oct2nummax(&config->create_file_mode, nextarg, 0777);
813         if(err)
814           return err;
815         break;
816 
817       case 's': /* --max-redirs */
818         /* specified max no of redirects (http(s)), this accepts -1 as a
819            special condition */
820         err = str2num(&config->maxredirs, nextarg);
821         if(err)
822           return err;
823         if(config->maxredirs < -1)
824           return PARAM_BAD_NUMERIC;
825         break;
826 
827       case 't': /* --proxy-ntlm */
828         if(curlinfo->features & CURL_VERSION_NTLM)
829           config->proxyntlm = toggle;
830         else
831           return PARAM_LIBCURL_DOESNT_SUPPORT;
832         break;
833 
834       case 'u': /* --crlf */
835         /* LF -> CRLF conversion? */
836         config->crlf = toggle;
837         break;
838 
839       case 'V': /* --aws-sigv4 */
840         config->authtype |= CURLAUTH_AWS_SIGV4;
841         GetStr(&config->aws_sigv4, nextarg);
842         break;
843 
844       case 'v': /* --stderr */
845         if(strcmp(nextarg, "-")) {
846           FILE *newfile = fopen(nextarg, FOPEN_WRITETEXT);
847           if(!newfile)
848             warnf(global, "Failed to open %s!\n", nextarg);
849           else {
850             if(global->errors_fopened)
851               fclose(global->errors);
852             global->errors = newfile;
853             global->errors_fopened = TRUE;
854           }
855         }
856         else
857           global->errors = stdout;
858         break;
859       case 'w': /* --interface */
860         /* interface */
861         GetStr(&config->iface, nextarg);
862         break;
863       case 'x': /* --krb */
864         /* kerberos level string */
865         if(curlinfo->features & CURL_VERSION_SPNEGO)
866           GetStr(&config->krblevel, nextarg);
867         else
868           return PARAM_LIBCURL_DOESNT_SUPPORT;
869         break;
870       case 'X': /* --haproxy-protocol */
871         config->haproxy_protocol = toggle;
872         break;
873       case 'y': /* --max-filesize */
874         {
875           curl_off_t value;
876           ParameterError pe =
877             GetSizeParameter(global, nextarg, "max-filesize", &value);
878 
879           if(pe != PARAM_OK)
880              return pe;
881           config->max_filesize = value;
882         }
883         break;
884       case 'z': /* --disable-eprt */
885         config->disable_eprt = toggle;
886         break;
887       case 'Z': /* --eprt */
888         config->disable_eprt = (!toggle)?TRUE:FALSE;
889         break;
890       case '~': /* --xattr */
891         config->xattr = toggle;
892         break;
893       case '@': /* the URL! */
894       {
895         struct getout *url;
896 
897         if(!config->url_get)
898           config->url_get = config->url_list;
899 
900         if(config->url_get) {
901           /* there's a node here, if it already is filled-in continue to find
902              an "empty" node */
903           while(config->url_get && (config->url_get->flags & GETOUT_URL))
904             config->url_get = config->url_get->next;
905         }
906 
907         /* now there might or might not be an available node to fill in! */
908 
909         if(config->url_get)
910           /* existing node */
911           url = config->url_get;
912         else
913           /* there was no free node, create one! */
914           config->url_get = url = new_getout(config);
915 
916         if(!url)
917           return PARAM_NO_MEM;
918 
919         /* fill in the URL */
920         GetStr(&url->url, nextarg);
921         url->flags |= GETOUT_URL;
922       }
923       }
924       break;
925     case '$': /* more options without a short option */
926       switch(subletter) {
927       case 'a': /* --ssl */
928         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
929           return PARAM_LIBCURL_DOESNT_SUPPORT;
930         config->ftp_ssl = toggle;
931         break;
932       case 'b': /* --ftp-pasv */
933         Curl_safefree(config->ftpport);
934         break;
935       case 'c': /* --socks5 specifies a socks5 proxy to use, and resolves
936                    the name locally and passes on the resolved address */
937         GetStr(&config->proxy, nextarg);
938         config->proxyver = CURLPROXY_SOCKS5;
939         break;
940       case 't': /* --socks4 specifies a socks4 proxy to use */
941         GetStr(&config->proxy, nextarg);
942         config->proxyver = CURLPROXY_SOCKS4;
943         break;
944       case 'T': /* --socks4a specifies a socks4a proxy to use */
945         GetStr(&config->proxy, nextarg);
946         config->proxyver = CURLPROXY_SOCKS4A;
947         break;
948       case '2': /* --socks5-hostname specifies a socks5 proxy and enables name
949                    resolving with the proxy */
950         GetStr(&config->proxy, nextarg);
951         config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
952         break;
953       case 'd': /* --tcp-nodelay option */
954         config->tcp_nodelay = toggle;
955         break;
956       case 'e': /* --proxy-digest */
957         config->proxydigest = toggle;
958         break;
959       case 'f': /* --proxy-basic */
960         config->proxybasic = toggle;
961         break;
962       case 'g': /* --retry */
963         err = str2unum(&config->req_retry, nextarg);
964         if(err)
965           return err;
966         break;
967       case 'V': /* --retry-connrefused */
968         config->retry_connrefused = toggle;
969         break;
970       case 'h': /* --retry-delay */
971         err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
972         if(err)
973           return err;
974         break;
975       case 'i': /* --retry-max-time */
976         err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
977         if(err)
978           return err;
979         break;
980       case '!': /* --retry-all-errors */
981         config->retry_all_errors = toggle;
982         break;
983 
984       case 'k': /* --proxy-negotiate */
985         if(curlinfo->features & CURL_VERSION_SPNEGO)
986           config->proxynegotiate = toggle;
987         else
988           return PARAM_LIBCURL_DOESNT_SUPPORT;
989         break;
990 
991       case 'm': /* --ftp-account */
992         GetStr(&config->ftp_account, nextarg);
993         break;
994       case 'n': /* --proxy-anyauth */
995         config->proxyanyauth = toggle;
996         break;
997       case 'o': /* --trace-time */
998         global->tracetime = toggle;
999         break;
1000       case 'p': /* --ignore-content-length */
1001         config->ignorecl = toggle;
1002         break;
1003       case 'q': /* --ftp-skip-pasv-ip */
1004         config->ftp_skip_ip = toggle;
1005         break;
1006       case 'r': /* --ftp-method (undocumented at this point) */
1007         config->ftp_filemethod = ftpfilemethod(config, nextarg);
1008         break;
1009       case 's': { /* --local-port */
1010         /* 16bit base 10 is 5 digits, but we allow 6 so that this catches
1011            overflows, not just truncates */
1012         char lrange[7]="";
1013         char *p = nextarg;
1014         while(ISDIGIT(*p))
1015           p++;
1016         if(*p) {
1017           /* if there's anything more than a plain decimal number */
1018           rc = sscanf(p, " - %6s", lrange);
1019           *p = 0; /* null-terminate to make str2unum() work below */
1020         }
1021         else
1022           rc = 0;
1023 
1024         err = str2unum(&config->localport, nextarg);
1025         if(err || (config->localport > 65535))
1026           return PARAM_BAD_USE;
1027         if(!rc)
1028           config->localportrange = 1; /* default number of ports to try */
1029         else {
1030           err = str2unum(&config->localportrange, lrange);
1031           if(err || (config->localportrange > 65535))
1032             return PARAM_BAD_USE;
1033           config->localportrange -= (config->localport-1);
1034           if(config->localportrange < 1)
1035             return PARAM_BAD_USE;
1036         }
1037         break;
1038       }
1039       case 'u': /* --ftp-alternative-to-user */
1040         GetStr(&config->ftp_alternative_to_user, nextarg);
1041         break;
1042       case 'v': /* --ssl-reqd */
1043         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
1044           return PARAM_LIBCURL_DOESNT_SUPPORT;
1045         config->ftp_ssl_reqd = toggle;
1046         break;
1047       case 'w': /* --no-sessionid */
1048         config->disable_sessionid = (!toggle)?TRUE:FALSE;
1049         break;
1050       case 'x': /* --ftp-ssl-control */
1051         if(toggle && !(curlinfo->features & CURL_VERSION_SSL))
1052           return PARAM_LIBCURL_DOESNT_SUPPORT;
1053         config->ftp_ssl_control = toggle;
1054         break;
1055       case 'y': /* --ftp-ssl-ccc */
1056         config->ftp_ssl_ccc = toggle;
1057         if(!config->ftp_ssl_ccc_mode)
1058           config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
1059         break;
1060       case 'j': /* --ftp-ssl-ccc-mode */
1061         config->ftp_ssl_ccc = TRUE;
1062         config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
1063         break;
1064       case 'z': /* --libcurl */
1065 #ifdef CURL_DISABLE_LIBCURL_OPTION
1066         warnf(global,
1067               "--libcurl option was disabled at build-time!\n");
1068         return PARAM_OPTION_UNKNOWN;
1069 #else
1070         GetStr(&global->libcurl, nextarg);
1071         break;
1072 #endif
1073       case '#': /* --raw */
1074         config->raw = toggle;
1075         break;
1076       case '0': /* --post301 */
1077         config->post301 = toggle;
1078         break;
1079       case '1': /* --no-keepalive */
1080         config->nokeepalive = (!toggle)?TRUE:FALSE;
1081         break;
1082       case '3': /* --keepalive-time */
1083         err = str2unum(&config->alivetime, nextarg);
1084         if(err)
1085           return err;
1086         break;
1087       case '4': /* --post302 */
1088         config->post302 = toggle;
1089         break;
1090       case 'I': /* --post303 */
1091         config->post303 = toggle;
1092         break;
1093       case '5': /* --noproxy */
1094         /* This specifies the noproxy list */
1095         GetStr(&config->noproxy, nextarg);
1096         break;
1097        case '7': /* --socks5-gssapi-nec*/
1098         config->socks5_gssapi_nec = toggle;
1099         break;
1100       case '8': /* --proxy1.0 */
1101         /* http 1.0 proxy */
1102         GetStr(&config->proxy, nextarg);
1103         config->proxyver = CURLPROXY_HTTP_1_0;
1104         break;
1105       case '9': /* --tftp-blksize */
1106         err = str2unum(&config->tftp_blksize, nextarg);
1107         if(err)
1108           return err;
1109         break;
1110       case 'A': /* --mail-from */
1111         GetStr(&config->mail_from, nextarg);
1112         break;
1113       case 'B': /* --mail-rcpt */
1114         /* append receiver to a list */
1115         err = add2list(&config->mail_rcpt, nextarg);
1116         if(err)
1117           return err;
1118         break;
1119       case 'C': /* --ftp-pret */
1120         config->ftp_pret = toggle;
1121         break;
1122       case 'D': /* --proto */
1123         config->proto_present = TRUE;
1124         if(proto2num(config, &config->proto, nextarg))
1125           return PARAM_BAD_USE;
1126         break;
1127       case 'E': /* --proto-redir */
1128         config->proto_redir_present = TRUE;
1129         if(proto2num(config, &config->proto_redir, nextarg))
1130           return PARAM_BAD_USE;
1131         break;
1132       case 'F': /* --resolve */
1133         err = add2list(&config->resolve, nextarg);
1134         if(err)
1135           return err;
1136         break;
1137       case 'G': /* --delegation LEVEL */
1138         config->gssapi_delegation = delegation(config, nextarg);
1139         break;
1140       case 'H': /* --mail-auth */
1141         GetStr(&config->mail_auth, nextarg);
1142         break;
1143       case 'J': /* --metalink */
1144         errorf(global, "--metalink is disabled\n");
1145         return PARAM_BAD_USE;
1146       case '6': /* --sasl-authzid */
1147         GetStr(&config->sasl_authzid, nextarg);
1148         break;
1149       case 'K': /* --sasl-ir */
1150         config->sasl_ir = toggle;
1151         break;
1152       case 'L': /* --test-event */
1153 #ifdef CURLDEBUG
1154         global->test_event_based = toggle;
1155 #else
1156         warnf(global, "--test-event is ignored unless a debug build!\n");
1157 #endif
1158         break;
1159       case 'M': /* --unix-socket */
1160         config->abstract_unix_socket = FALSE;
1161         GetStr(&config->unix_socket_path, nextarg);
1162         break;
1163       case 'N': /* --path-as-is */
1164         config->path_as_is = toggle;
1165         break;
1166       case 'O': /* --proxy-service-name */
1167         GetStr(&config->proxy_service_name, nextarg);
1168         break;
1169       case 'P': /* --service-name */
1170         GetStr(&config->service_name, nextarg);
1171         break;
1172       case 'Q': /* --proto-default */
1173         GetStr(&config->proto_default, nextarg);
1174         err = check_protocol(config->proto_default);
1175         if(err)
1176           return err;
1177         break;
1178       case 'R': /* --expect100-timeout */
1179         err = str2udouble(&config->expect100timeout, nextarg, LONG_MAX/1000);
1180         if(err)
1181           return err;
1182         break;
1183       case 'S': /* --tftp-no-options */
1184         config->tftp_no_options = toggle;
1185         break;
1186       case 'U': /* --connect-to */
1187         err = add2list(&config->connect_to, nextarg);
1188         if(err)
1189           return err;
1190         break;
1191       case 'W': /* --abstract-unix-socket */
1192         config->abstract_unix_socket = TRUE;
1193         GetStr(&config->unix_socket_path, nextarg);
1194         break;
1195       case 'X': /* --tls-max */
1196         err = str2tls_max(&config->ssl_version_max, nextarg);
1197         if(err)
1198           return err;
1199         break;
1200       case 'Y': /* --suppress-connect-headers */
1201         config->suppress_connect_headers = toggle;
1202         break;
1203       case 'Z': /* --compressed-ssh */
1204         config->ssh_compression = toggle;
1205         break;
1206       case '~': /* --happy-eyeballs-timeout-ms */
1207         err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
1208         if(err)
1209           return err;
1210         /* 0 is a valid value for this timeout */
1211         break;
1212       }
1213       break;
1214     case '#':
1215       switch(subletter) {
1216       case 'm': /* --progress-meter */
1217         global->noprogress = !toggle;
1218         break;
1219       default:  /* --progress-bar */
1220         global->progressmode =
1221           toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
1222         break;
1223       }
1224       break;
1225     case ':': /* --next */
1226       return PARAM_NEXT_OPERATION;
1227     case '0': /* --http* options */
1228       switch(subletter) {
1229       case '\0':
1230         /* HTTP version 1.0 */
1231         config->httpversion = CURL_HTTP_VERSION_1_0;
1232         break;
1233       case '1':
1234         /* HTTP version 1.1 */
1235         config->httpversion = CURL_HTTP_VERSION_1_1;
1236         break;
1237       case '2':
1238         /* HTTP version 2.0 */
1239         config->httpversion = CURL_HTTP_VERSION_2_0;
1240         break;
1241       case '3': /* --http2-prior-knowledge */
1242         /* HTTP version 2.0 over clean TCP*/
1243         config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE;
1244         break;
1245       case '4': /* --http3 */
1246         /* HTTP version 3 go over QUIC - at once */
1247         if(curlinfo->features & CURL_VERSION_HTTP3)
1248           config->httpversion = CURL_HTTP_VERSION_3;
1249         else
1250           return PARAM_LIBCURL_DOESNT_SUPPORT;
1251         break;
1252       case '9':
1253         /* Allow HTTP/0.9 responses! */
1254         config->http09_allowed = toggle;
1255         break;
1256       }
1257       break;
1258     case '1': /* --tlsv1* options */
1259       switch(subletter) {
1260       case '\0':
1261         /* TLS version 1.x */
1262         config->ssl_version = CURL_SSLVERSION_TLSv1;
1263         break;
1264       case '0':
1265         /* TLS version 1.0 */
1266         config->ssl_version = CURL_SSLVERSION_TLSv1_0;
1267         break;
1268       case '1':
1269         /* TLS version 1.1 */
1270         config->ssl_version = CURL_SSLVERSION_TLSv1_1;
1271         break;
1272       case '2':
1273         /* TLS version 1.2 */
1274         config->ssl_version = CURL_SSLVERSION_TLSv1_2;
1275         break;
1276       case '3':
1277         /* TLS version 1.3 */
1278         config->ssl_version = CURL_SSLVERSION_TLSv1_3;
1279         break;
1280       case 'A': /* --tls13-ciphers */
1281         GetStr(&config->cipher13_list, nextarg);
1282         break;
1283       case 'B': /* --proxy-tls13-ciphers */
1284         GetStr(&config->proxy_cipher13_list, nextarg);
1285         break;
1286       }
1287       break;
1288     case '2':
1289       /* SSL version 2 */
1290       warnf(global, "Ignores instruction to use SSLv2\n");
1291       break;
1292     case '3':
1293       /* SSL version 3 */
1294       warnf(global, "Ignores instruction to use SSLv3\n");
1295       break;
1296     case '4':
1297       /* IPv4 */
1298       config->ip_version = CURL_IPRESOLVE_V4;
1299       break;
1300     case '6':
1301       /* IPv6 */
1302       config->ip_version = CURL_IPRESOLVE_V6;
1303       break;
1304     case 'a':
1305       /* This makes the FTP sessions use APPE instead of STOR */
1306       config->ftp_append = toggle;
1307       break;
1308     case 'A':
1309       /* This specifies the User-Agent name */
1310       GetStr(&config->useragent, nextarg);
1311       break;
1312     case 'b':
1313       switch(subletter) {
1314       case 'a': /* --alt-svc */
1315         if(curlinfo->features & CURL_VERSION_ALTSVC)
1316           GetStr(&config->altsvc, nextarg);
1317         else
1318           return PARAM_LIBCURL_DOESNT_SUPPORT;
1319         break;
1320       case 'b': /* --hsts */
1321         if(curlinfo->features & CURL_VERSION_HSTS)
1322           GetStr(&config->hsts, nextarg);
1323         else
1324           return PARAM_LIBCURL_DOESNT_SUPPORT;
1325         break;
1326       default:  /* --cookie string coming up: */
1327         if(nextarg[0] == '@') {
1328           nextarg++;
1329         }
1330         else if(strchr(nextarg, '=')) {
1331           /* A cookie string must have a =-letter */
1332           err = add2list(&config->cookies, nextarg);
1333           if(err)
1334             return err;
1335           break;
1336         }
1337         /* We have a cookie file to read from! */
1338         err = add2list(&config->cookiefiles, nextarg);
1339         if(err)
1340           return err;
1341       }
1342       break;
1343     case 'B':
1344       /* use ASCII/text when transferring */
1345       config->use_ascii = toggle;
1346       break;
1347     case 'c':
1348       /* get the file name to dump all cookies in */
1349       GetStr(&config->cookiejar, nextarg);
1350       break;
1351     case 'C':
1352       /* This makes us continue an ftp transfer at given position */
1353       if(strcmp(nextarg, "-")) {
1354         err = str2offset(&config->resume_from, nextarg);
1355         if(err)
1356           return err;
1357         config->resume_from_current = FALSE;
1358       }
1359       else {
1360         config->resume_from_current = TRUE;
1361         config->resume_from = 0;
1362       }
1363       config->use_resume = TRUE;
1364       break;
1365     case 'd':
1366       /* postfield data */
1367     {
1368       char *postdata = NULL;
1369       FILE *file;
1370       size_t size = 0;
1371       bool raw_mode = (subletter == 'r');
1372 
1373       if(subletter == 'e') { /* --data-urlencode*/
1374         /* [name]=[content], we encode the content part only
1375          * [name]@[file name]
1376          *
1377          * Case 2: we first load the file using that name and then encode
1378          * the content.
1379          */
1380         const char *p = strchr(nextarg, '=');
1381         size_t nlen;
1382         char is_file;
1383         if(!p)
1384           /* there was no '=' letter, check for a '@' instead */
1385           p = strchr(nextarg, '@');
1386         if(p) {
1387           nlen = p - nextarg; /* length of the name part */
1388           is_file = *p++; /* pass the separator */
1389         }
1390         else {
1391           /* neither @ nor =, so no name and it isn't a file */
1392           nlen = is_file = 0;
1393           p = nextarg;
1394         }
1395         if('@' == is_file) {
1396           /* a '@' letter, it means that a file name or - (stdin) follows */
1397           if(!strcmp("-", p)) {
1398             file = stdin;
1399             set_binmode(stdin);
1400           }
1401           else {
1402             file = fopen(p, "rb");
1403             if(!file)
1404               warnf(global,
1405                     "Couldn't read data from file \"%s\", this makes "
1406                     "an empty POST.\n", nextarg);
1407           }
1408 
1409           err = file2memory(&postdata, &size, file);
1410 
1411           if(file && (file != stdin))
1412             fclose(file);
1413           if(err)
1414             return err;
1415         }
1416         else {
1417           GetStr(&postdata, p);
1418           if(postdata)
1419             size = strlen(postdata);
1420         }
1421 
1422         if(!postdata) {
1423           /* no data from the file, point to a zero byte string to make this
1424              get sent as a POST anyway */
1425           postdata = strdup("");
1426           if(!postdata)
1427             return PARAM_NO_MEM;
1428           size = 0;
1429         }
1430         else {
1431           char *enc = curl_easy_escape(NULL, postdata, (int)size);
1432           Curl_safefree(postdata); /* no matter if it worked or not */
1433           if(enc) {
1434             /* replace (in-place) '%20' by '+' according to RFC1866 */
1435             size_t enclen = replace_url_encoded_space_by_plus(enc);
1436             /* now make a string with the name from above and append the
1437                encoded string */
1438             size_t outlen = nlen + enclen + 2;
1439             char *n = malloc(outlen);
1440             if(!n) {
1441               curl_free(enc);
1442               return PARAM_NO_MEM;
1443             }
1444             if(nlen > 0) { /* only append '=' if we have a name */
1445               msnprintf(n, outlen, "%.*s=%s", nlen, nextarg, enc);
1446               size = outlen-1;
1447             }
1448             else {
1449               strcpy(n, enc);
1450               size = outlen-2; /* since no '=' was inserted */
1451             }
1452             curl_free(enc);
1453             postdata = n;
1454           }
1455           else
1456             return PARAM_NO_MEM;
1457         }
1458       }
1459       else if('@' == *nextarg && !raw_mode) {
1460         /* the data begins with a '@' letter, it means that a file name
1461            or - (stdin) follows */
1462         nextarg++; /* pass the @ */
1463 
1464         if(!strcmp("-", nextarg)) {
1465           file = stdin;
1466           if(subletter == 'b') /* forced data-binary */
1467             set_binmode(stdin);
1468         }
1469         else {
1470           file = fopen(nextarg, "rb");
1471           if(!file)
1472             warnf(global, "Couldn't read data from file \"%s\", this makes "
1473                   "an empty POST.\n", nextarg);
1474         }
1475 
1476         if(subletter == 'b')
1477           /* forced binary */
1478           err = file2memory(&postdata, &size, file);
1479         else {
1480           err = file2string(&postdata, file);
1481           if(postdata)
1482             size = strlen(postdata);
1483         }
1484 
1485         if(file && (file != stdin))
1486           fclose(file);
1487         if(err)
1488           return err;
1489 
1490         if(!postdata) {
1491           /* no data from the file, point to a zero byte string to make this
1492              get sent as a POST anyway */
1493           postdata = strdup("");
1494           if(!postdata)
1495             return PARAM_NO_MEM;
1496         }
1497       }
1498       else {
1499         GetStr(&postdata, nextarg);
1500         if(postdata)
1501           size = strlen(postdata);
1502       }
1503 
1504 #ifdef CURL_DOES_CONVERSIONS
1505       if(subletter != 'b') {
1506         /* NOT forced binary, convert to ASCII */
1507         if(convert_to_network(postdata, strlen(postdata))) {
1508           Curl_safefree(postdata);
1509           return PARAM_NO_MEM;
1510         }
1511       }
1512 #endif
1513 
1514       if(config->postfields) {
1515         /* we already have a string, we append this one with a separating
1516            &-letter */
1517         char *oldpost = config->postfields;
1518         curl_off_t oldlen = config->postfieldsize;
1519         curl_off_t newlen = oldlen + curlx_uztoso(size) + 2;
1520         config->postfields = malloc((size_t)newlen);
1521         if(!config->postfields) {
1522           Curl_safefree(oldpost);
1523           Curl_safefree(postdata);
1524           return PARAM_NO_MEM;
1525         }
1526         memcpy(config->postfields, oldpost, (size_t)oldlen);
1527         /* use byte value 0x26 for '&' to accommodate non-ASCII platforms */
1528         config->postfields[oldlen] = '\x26';
1529         memcpy(&config->postfields[oldlen + 1], postdata, size);
1530         config->postfields[oldlen + 1 + size] = '\0';
1531         Curl_safefree(oldpost);
1532         Curl_safefree(postdata);
1533         config->postfieldsize += size + 1;
1534       }
1535       else {
1536         config->postfields = postdata;
1537         config->postfieldsize = curlx_uztoso(size);
1538       }
1539     }
1540     /*
1541       We can't set the request type here, as this data might be used in
1542       a simple GET if -G is used. Already or soon.
1543 
1544       if(SetHTTPrequest(HTTPREQ_SIMPLEPOST, &config->httpreq)) {
1545         Curl_safefree(postdata);
1546         return PARAM_BAD_USE;
1547       }
1548     */
1549     break;
1550     case 'D':
1551       /* dump-header to given file name */
1552       GetStr(&config->headerfile, nextarg);
1553       break;
1554     case 'e':
1555     {
1556       char *ptr = strstr(nextarg, ";auto");
1557       if(ptr) {
1558         /* Automatic referer requested, this may be combined with a
1559            set initial one */
1560         config->autoreferer = TRUE;
1561         *ptr = 0; /* null-terminate here */
1562       }
1563       else
1564         config->autoreferer = FALSE;
1565       ptr = *nextarg ? nextarg : NULL;
1566       GetStr(&config->referer, ptr);
1567     }
1568     break;
1569     case 'E':
1570       switch(subletter) {
1571       case '\0': /* certificate file */
1572         GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
1573         break;
1574       case 'a': /* CA info PEM file */
1575         GetStr(&config->cacert, nextarg);
1576         break;
1577       case 'b': /* cert file type */
1578         GetStr(&config->cert_type, nextarg);
1579         break;
1580       case 'c': /* private key file */
1581         GetStr(&config->key, nextarg);
1582         break;
1583       case 'd': /* private key file type */
1584         GetStr(&config->key_type, nextarg);
1585         break;
1586       case 'e': /* private key passphrase */
1587         GetStr(&config->key_passwd, nextarg);
1588         cleanarg(nextarg);
1589         break;
1590       case 'f': /* crypto engine */
1591         GetStr(&config->engine, nextarg);
1592         if(config->engine && curl_strequal(config->engine, "list"))
1593           return PARAM_ENGINES_REQUESTED;
1594         break;
1595       case 'g': /* CA cert directory */
1596         GetStr(&config->capath, nextarg);
1597         break;
1598       case 'h': /* --pubkey public key file */
1599         GetStr(&config->pubkey, nextarg);
1600         break;
1601       case 'i': /* --hostpubmd5 md5 of the host public key */
1602         GetStr(&config->hostpubmd5, nextarg);
1603         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
1604           return PARAM_BAD_USE;
1605         break;
1606       case 'F': /* --hostpubsha256 sha256 of the host public key */
1607         GetStr(&config->hostpubsha256, nextarg);
1608         break;
1609       case 'j': /* CRL file */
1610         GetStr(&config->crlfile, nextarg);
1611         break;
1612       case 'k': /* TLS username */
1613         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1614           GetStr(&config->tls_username, nextarg);
1615         else
1616           return PARAM_LIBCURL_DOESNT_SUPPORT;
1617         break;
1618       case 'l': /* TLS password */
1619         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1620           GetStr(&config->tls_password, nextarg);
1621         else
1622           return PARAM_LIBCURL_DOESNT_SUPPORT;
1623         break;
1624       case 'm': /* TLS authentication type */
1625         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
1626           GetStr(&config->tls_authtype, nextarg);
1627           if(!curl_strequal(config->tls_authtype, "SRP"))
1628             return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
1629         }
1630         else
1631           return PARAM_LIBCURL_DOESNT_SUPPORT;
1632         break;
1633       case 'n': /* no empty SSL fragments, --ssl-allow-beast */
1634         if(curlinfo->features & CURL_VERSION_SSL)
1635           config->ssl_allow_beast = toggle;
1636         break;
1637 
1638       case 'o': /* --ssl-auto-client-cert */
1639         if(curlinfo->features & CURL_VERSION_SSL)
1640           config->ssl_auto_client_cert = toggle;
1641         break;
1642 
1643       case 'O': /* --proxy-ssl-auto-client-cert */
1644         if(curlinfo->features & CURL_VERSION_SSL)
1645           config->proxy_ssl_auto_client_cert = toggle;
1646         break;
1647 
1648       case 'p': /* Pinned public key DER file */
1649         GetStr(&config->pinnedpubkey, nextarg);
1650         break;
1651 
1652       case 'P': /* proxy pinned public key */
1653         GetStr(&config->proxy_pinnedpubkey, nextarg);
1654         break;
1655 
1656       case 'q': /* --cert-status */
1657         config->verifystatus = TRUE;
1658         break;
1659 
1660       case 'Q': /* --doh-cert-status */
1661         config->doh_verifystatus = TRUE;
1662         break;
1663 
1664       case 'r': /* --false-start */
1665         config->falsestart = TRUE;
1666         break;
1667 
1668       case 's': /* --ssl-no-revoke */
1669         if(curlinfo->features & CURL_VERSION_SSL)
1670           config->ssl_no_revoke = TRUE;
1671         break;
1672 
1673       case 'S': /* --ssl-revoke-best-effort */
1674         if(curlinfo->features & CURL_VERSION_SSL)
1675           config->ssl_revoke_best_effort = TRUE;
1676         break;
1677 
1678       case 't': /* --tcp-fastopen */
1679         config->tcp_fastopen = TRUE;
1680         break;
1681 
1682       case 'u': /* TLS username for proxy */
1683         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1684           GetStr(&config->proxy_tls_username, nextarg);
1685         else
1686           return PARAM_LIBCURL_DOESNT_SUPPORT;
1687         break;
1688 
1689       case 'v': /* TLS password for proxy */
1690         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP)
1691           GetStr(&config->proxy_tls_password, nextarg);
1692         else
1693           return PARAM_LIBCURL_DOESNT_SUPPORT;
1694         break;
1695 
1696       case 'w': /* TLS authentication type for proxy */
1697         if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
1698           GetStr(&config->proxy_tls_authtype, nextarg);
1699           if(!curl_strequal(config->proxy_tls_authtype, "SRP"))
1700             return PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
1701         }
1702         else
1703           return PARAM_LIBCURL_DOESNT_SUPPORT;
1704         break;
1705 
1706       case 'x': /* certificate file for proxy */
1707         GetFileAndPassword(nextarg, &config->proxy_cert,
1708                            &config->proxy_key_passwd);
1709         break;
1710 
1711       case 'y': /* cert file type for proxy */
1712         GetStr(&config->proxy_cert_type, nextarg);
1713         break;
1714 
1715       case 'z': /* private key file for proxy */
1716         GetStr(&config->proxy_key, nextarg);
1717         break;
1718 
1719       case '0': /* private key file type for proxy */
1720         GetStr(&config->proxy_key_type, nextarg);
1721         break;
1722 
1723       case '1': /* private key passphrase for proxy */
1724         GetStr(&config->proxy_key_passwd, nextarg);
1725         cleanarg(nextarg);
1726         break;
1727 
1728       case '2': /* ciphers for proxy */
1729         GetStr(&config->proxy_cipher_list, nextarg);
1730         break;
1731 
1732       case '3': /* CRL file for proxy */
1733         GetStr(&config->proxy_crlfile, nextarg);
1734         break;
1735 
1736       case '4': /* no empty SSL fragments for proxy */
1737         if(curlinfo->features & CURL_VERSION_SSL)
1738           config->proxy_ssl_allow_beast = toggle;
1739         break;
1740 
1741       case '5': /* --login-options */
1742         GetStr(&config->login_options, nextarg);
1743         break;
1744 
1745       case '6': /* CA info PEM file for proxy */
1746         GetStr(&config->proxy_cacert, nextarg);
1747         break;
1748 
1749       case '7': /* CA cert directory for proxy */
1750         GetStr(&config->proxy_capath, nextarg);
1751         break;
1752 
1753       case '8': /* allow insecure SSL connects for proxy */
1754         config->proxy_insecure_ok = toggle;
1755         break;
1756 
1757       case '9': /* --proxy-tlsv1 */
1758         /* TLS version 1 for proxy */
1759         config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
1760         break;
1761 
1762       case 'A':
1763         /* --socks5-basic */
1764         if(toggle)
1765           config->socks5_auth |= CURLAUTH_BASIC;
1766         else
1767           config->socks5_auth &= ~CURLAUTH_BASIC;
1768         break;
1769 
1770       case 'B':
1771         /* --socks5-gssapi */
1772         if(toggle)
1773           config->socks5_auth |= CURLAUTH_GSSAPI;
1774         else
1775           config->socks5_auth &= ~CURLAUTH_GSSAPI;
1776         break;
1777 
1778       case 'C':
1779         GetStr(&config->etag_save_file, nextarg);
1780         break;
1781 
1782       case 'D':
1783         GetStr(&config->etag_compare_file, nextarg);
1784         break;
1785 
1786       case 'E':
1787         GetStr(&config->ssl_ec_curves, nextarg);
1788         break;
1789 
1790       default: /* unknown flag */
1791         return PARAM_OPTION_UNKNOWN;
1792       }
1793       break;
1794     case 'f':
1795       switch(subletter) {
1796       case 'a': /* --fail-early */
1797         global->fail_early = toggle;
1798         break;
1799       case 'b': /* --styled-output */
1800         global->styled_output = toggle;
1801         break;
1802       case 'c': /* --mail-rcpt-allowfails */
1803         config->mail_rcpt_allowfails = toggle;
1804         break;
1805       case 'd': /* --fail-with-body */
1806         config->failwithbody = toggle;
1807         break;
1808       default: /* --fail (hard on errors)  */
1809         config->failonerror = toggle;
1810         break;
1811       }
1812       if(config->failonerror && config->failwithbody) {
1813         errorf(config->global, "You must select either --fail or "
1814                "--fail-with-body, not both.\n");
1815         return PARAM_BAD_USE;
1816       }
1817       break;
1818     case 'F':
1819       /* "form data" simulation, this is a little advanced so lets do our best
1820          to sort this out slowly and carefully */
1821       if(formparse(config,
1822                    nextarg,
1823                    &config->mimeroot,
1824                    &config->mimecurrent,
1825                    (subletter == 's')?TRUE:FALSE)) /* 's' is literal string */
1826         return PARAM_BAD_USE;
1827       if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq))
1828         return PARAM_BAD_USE;
1829       break;
1830 
1831     case 'g': /* g disables URLglobbing */
1832       config->globoff = toggle;
1833       break;
1834 
1835     case 'G': /* HTTP GET */
1836       if(subletter == 'a') { /* --request-target */
1837         GetStr(&config->request_target, nextarg);
1838       }
1839       else
1840         config->use_httpget = TRUE;
1841       break;
1842 
1843     case 'h': /* h for help */
1844       if(toggle) {
1845         if(nextarg) {
1846           global->help_category = strdup(nextarg);
1847           if(!global->help_category)
1848             return PARAM_NO_MEM;
1849         }
1850         return PARAM_HELP_REQUESTED;
1851       }
1852       /* we now actually support --no-help too! */
1853       break;
1854     case 'H':
1855       /* A custom header to append to a list */
1856       if(nextarg[0] == '@') {
1857         /* read many headers from a file or stdin */
1858         char *string;
1859         size_t len;
1860         bool use_stdin = !strcmp(&nextarg[1], "-");
1861         FILE *file = use_stdin?stdin:fopen(&nextarg[1], FOPEN_READTEXT);
1862         if(!file)
1863           warnf(global, "Failed to open %s!\n", &nextarg[1]);
1864         else {
1865           err = file2memory(&string, &len, file);
1866           if(!err && string) {
1867             /* Allow strtok() here since this isn't used threaded */
1868             /* !checksrc! disable BANNEDFUNC 2 */
1869             char *h = strtok(string, "\r\n");
1870             while(h) {
1871               if(subletter == 'p') /* --proxy-header */
1872                 err = add2list(&config->proxyheaders, h);
1873               else
1874                 err = add2list(&config->headers, h);
1875               if(err)
1876                 break;
1877               h = strtok(NULL, "\r\n");
1878             }
1879             free(string);
1880           }
1881           if(!use_stdin)
1882             fclose(file);
1883           if(err)
1884             return err;
1885         }
1886       }
1887       else {
1888         if(subletter == 'p') /* --proxy-header */
1889           err = add2list(&config->proxyheaders, nextarg);
1890         else
1891           err = add2list(&config->headers, nextarg);
1892         if(err)
1893           return err;
1894       }
1895       break;
1896     case 'i':
1897       config->show_headers = toggle; /* show the headers as well in the
1898                                         general output stream */
1899       break;
1900     case 'j':
1901       config->cookiesession = toggle;
1902       break;
1903     case 'I': /* --head */
1904       config->no_body = toggle;
1905       config->show_headers = toggle;
1906       if(SetHTTPrequest(config,
1907                         (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
1908                         &config->httpreq))
1909         return PARAM_BAD_USE;
1910       break;
1911     case 'J': /* --remote-header-name */
1912       config->content_disposition = toggle;
1913       break;
1914     case 'k': /* allow insecure SSL connects */
1915       if(subletter == 'd') /* --doh-insecure */
1916         config->doh_insecure_ok = toggle;
1917       else
1918         config->insecure_ok = toggle;
1919       break;
1920     case 'K': /* parse config file */
1921       if(parseconfig(nextarg, global))
1922         warnf(global, "error trying read config from the '%s' file\n",
1923               nextarg);
1924       break;
1925     case 'l':
1926       config->dirlistonly = toggle; /* only list the names of the FTP dir */
1927       break;
1928     case 'L':
1929       config->followlocation = toggle; /* Follow Location: HTTP headers */
1930       switch(subletter) {
1931       case 't':
1932         /* Continue to send authentication (user+password) when following
1933          * locations, even when hostname changed */
1934         config->unrestricted_auth = toggle;
1935         break;
1936       }
1937       break;
1938     case 'm':
1939       /* specified max time */
1940       err = str2udouble(&config->timeout, nextarg, LONG_MAX/1000);
1941       if(err)
1942         return err;
1943       break;
1944     case 'M': /* M for manual, huge help */
1945       if(toggle) { /* --no-manual shows no manual... */
1946 #ifdef USE_MANUAL
1947         return PARAM_MANUAL_REQUESTED;
1948 #else
1949         warnf(global,
1950               "built-in manual was disabled at build-time!\n");
1951         return PARAM_OPTION_UNKNOWN;
1952 #endif
1953       }
1954       break;
1955     case 'n':
1956       switch(subletter) {
1957       case 'o': /* use .netrc or URL */
1958         config->netrc_opt = toggle;
1959         break;
1960       case 'e': /* netrc-file */
1961         GetStr(&config->netrc_file, nextarg);
1962         break;
1963       default:
1964         /* pick info from .netrc, if this is used for http, curl will
1965            automatically enforce user+password with the request */
1966         config->netrc = toggle;
1967         break;
1968       }
1969       break;
1970     case 'N':
1971       /* disable the output I/O buffering. note that the option is called
1972          --buffer but is mostly used in the negative form: --no-buffer */
1973       if(longopt)
1974         config->nobuffer = (!toggle)?TRUE:FALSE;
1975       else
1976         config->nobuffer = toggle;
1977       break;
1978     case 'O': /* --remote-name */
1979       if(subletter == 'a') { /* --remote-name-all */
1980         config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
1981         break;
1982       }
1983       else if(subletter == 'b') { /* --output-dir */
1984         GetStr(&config->output_dir, nextarg);
1985         break;
1986       }
1987       /* FALLTHROUGH */
1988     case 'o': /* --output */
1989       /* output file */
1990     {
1991       struct getout *url;
1992       if(!config->url_out)
1993         config->url_out = config->url_list;
1994       if(config->url_out) {
1995         /* there's a node here, if it already is filled-in continue to find
1996            an "empty" node */
1997         while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1998           config->url_out = config->url_out->next;
1999       }
2000 
2001       /* now there might or might not be an available node to fill in! */
2002 
2003       if(config->url_out)
2004         /* existing node */
2005         url = config->url_out;
2006       else
2007         /* there was no free node, create one! */
2008         config->url_out = url = new_getout(config);
2009 
2010       if(!url)
2011         return PARAM_NO_MEM;
2012 
2013       /* fill in the outfile */
2014       if('o' == letter) {
2015         GetStr(&url->outfile, nextarg);
2016         url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2017       }
2018       else {
2019         url->outfile = NULL; /* leave it */
2020         if(toggle)
2021           url->flags |= GETOUT_USEREMOTE;  /* switch on */
2022         else
2023           url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2024       }
2025       url->flags |= GETOUT_OUTFILE;
2026     }
2027     break;
2028     case 'P':
2029       /* This makes the FTP sessions use PORT instead of PASV */
2030       /* use <eth0> or <192.168.10.10> style addresses. Anything except
2031          this will make us try to get the "default" address.
2032          NOTE: this is a changed behavior since the released 4.1!
2033       */
2034       GetStr(&config->ftpport, nextarg);
2035       break;
2036     case 'p':
2037       /* proxy tunnel for non-http protocols */
2038       config->proxytunnel = toggle;
2039       break;
2040 
2041     case 'q': /* if used first, already taken care of, we do it like
2042                  this so we don't cause an error! */
2043       break;
2044     case 'Q':
2045       /* QUOTE command to send to FTP server */
2046       switch(nextarg[0]) {
2047       case '-':
2048         /* prefixed with a dash makes it a POST TRANSFER one */
2049         nextarg++;
2050         err = add2list(&config->postquote, nextarg);
2051         break;
2052       case '+':
2053         /* prefixed with a plus makes it a just-before-transfer one */
2054         nextarg++;
2055         err = add2list(&config->prequote, nextarg);
2056         break;
2057       default:
2058         err = add2list(&config->quote, nextarg);
2059         break;
2060       }
2061       if(err)
2062         return err;
2063       break;
2064     case 'r':
2065       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
2066          (and won't actually be range by definition). The man page previously
2067          claimed that to be a good way, why this code is added to work-around
2068          it. */
2069       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
2070         char buffer[32];
2071         curl_off_t off;
2072         if(curlx_strtoofft(nextarg, NULL, 10, &off)) {
2073           warnf(global, "unsupported range point\n");
2074           return PARAM_BAD_USE;
2075         }
2076         warnf(global,
2077               "A specified range MUST include at least one dash (-). "
2078               "Appending one for you!\n");
2079         msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", off);
2080         Curl_safefree(config->range);
2081         config->range = strdup(buffer);
2082         if(!config->range)
2083           return PARAM_NO_MEM;
2084       }
2085       else {
2086         /* byte range requested */
2087         const char *tmp_range = nextarg;
2088         while(*tmp_range != '\0') {
2089           if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
2090             warnf(global, "Invalid character is found in given range. "
2091                   "A specified range MUST have only digits in "
2092                   "\'start\'-\'stop\'. The server's response to this "
2093                   "request is uncertain.\n");
2094             break;
2095           }
2096           tmp_range++;
2097         }
2098         GetStr(&config->range, nextarg);
2099       }
2100       break;
2101     case 'R':
2102       /* use remote file's time */
2103       config->remote_time = toggle;
2104       break;
2105     case 's':
2106       /* don't show progress meter, don't show errors : */
2107       if(toggle)
2108         global->mute = global->noprogress = TRUE;
2109       else
2110         global->mute = global->noprogress = FALSE;
2111       if(global->showerror < 0)
2112         /* if still on the default value, set showerror to the reverse of
2113            toggle. This is to allow -S and -s to be used in an independent
2114            order but still have the same effect. */
2115         global->showerror = (!toggle)?TRUE:FALSE; /* toggle off */
2116       break;
2117     case 'S':
2118       /* show errors */
2119       global->showerror = toggle?1:0; /* toggle on if used with -s */
2120       break;
2121     case 't':
2122       /* Telnet options */
2123       err = add2list(&config->telnet_options, nextarg);
2124       if(err)
2125         return err;
2126       break;
2127     case 'T':
2128       /* we are uploading */
2129     {
2130       struct getout *url;
2131       if(!config->url_ul)
2132         config->url_ul = config->url_list;
2133       if(config->url_ul) {
2134         /* there's a node here, if it already is filled-in continue to find
2135            an "empty" node */
2136         while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
2137           config->url_ul = config->url_ul->next;
2138       }
2139 
2140       /* now there might or might not be an available node to fill in! */
2141 
2142       if(config->url_ul)
2143         /* existing node */
2144         url = config->url_ul;
2145       else
2146         /* there was no free node, create one! */
2147         config->url_ul = url = new_getout(config);
2148 
2149       if(!url)
2150         return PARAM_NO_MEM;
2151 
2152       url->flags |= GETOUT_UPLOAD; /* mark -T used */
2153       if(!*nextarg)
2154         url->flags |= GETOUT_NOUPLOAD;
2155       else {
2156         /* "-" equals stdin, but keep the string around for now */
2157         GetStr(&url->infile, nextarg);
2158       }
2159     }
2160     break;
2161     case 'u':
2162       /* user:password  */
2163       GetStr(&config->userpwd, nextarg);
2164       cleanarg(nextarg);
2165       break;
2166     case 'U':
2167       /* Proxy user:password  */
2168       GetStr(&config->proxyuserpwd, nextarg);
2169       cleanarg(nextarg);
2170       break;
2171     case 'v':
2172       if(toggle) {
2173         /* the '%' thing here will cause the trace get sent to stderr */
2174         Curl_safefree(global->trace_dump);
2175         global->trace_dump = strdup("%");
2176         if(!global->trace_dump)
2177           return PARAM_NO_MEM;
2178         if(global->tracetype && (global->tracetype != TRACE_PLAIN))
2179           warnf(global,
2180                 "-v, --verbose overrides an earlier trace/verbose option\n");
2181         global->tracetype = TRACE_PLAIN;
2182       }
2183       else
2184         /* verbose is disabled here */
2185         global->tracetype = TRACE_NONE;
2186       break;
2187     case 'V':
2188       if(toggle)    /* --no-version yields no output! */
2189         return PARAM_VERSION_INFO_REQUESTED;
2190       break;
2191 
2192     case 'w':
2193       /* get the output string */
2194       if('@' == *nextarg) {
2195         /* the data begins with a '@' letter, it means that a file name
2196            or - (stdin) follows */
2197         FILE *file;
2198         const char *fname;
2199         nextarg++; /* pass the @ */
2200         if(!strcmp("-", nextarg)) {
2201           fname = "<stdin>";
2202           file = stdin;
2203         }
2204         else {
2205           fname = nextarg;
2206           file = fopen(nextarg, FOPEN_READTEXT);
2207         }
2208         Curl_safefree(config->writeout);
2209         err = file2string(&config->writeout, file);
2210         if(file && (file != stdin))
2211           fclose(file);
2212         if(err)
2213           return err;
2214         if(!config->writeout)
2215           warnf(global, "Failed to read %s", fname);
2216       }
2217       else
2218         GetStr(&config->writeout, nextarg);
2219       break;
2220     case 'x':
2221       switch(subletter) {
2222       case 'a': /* --preproxy */
2223         GetStr(&config->preproxy, nextarg);
2224         break;
2225       default:
2226         /* --proxy */
2227         GetStr(&config->proxy, nextarg);
2228         config->proxyver = CURLPROXY_HTTP;
2229         break;
2230       }
2231       break;
2232     case 'X':
2233       /* set custom request */
2234       GetStr(&config->customrequest, nextarg);
2235       break;
2236     case 'y':
2237       /* low speed time */
2238       err = str2unum(&config->low_speed_time, nextarg);
2239       if(err)
2240         return err;
2241       if(!config->low_speed_limit)
2242         config->low_speed_limit = 1;
2243       break;
2244     case 'Y':
2245       /* low speed limit */
2246       err = str2unum(&config->low_speed_limit, nextarg);
2247       if(err)
2248         return err;
2249       if(!config->low_speed_time)
2250         config->low_speed_time = 30;
2251       break;
2252     case 'Z':
2253       switch(subletter) {
2254       case '\0':  /* --parallel */
2255         global->parallel = toggle;
2256         break;
2257       case 'b':   /* --parallel-max */
2258         err = str2unum(&global->parallel_max, nextarg);
2259         if(err)
2260           return err;
2261         if((global->parallel_max > MAX_PARALLEL) ||
2262            (global->parallel_max < 1))
2263           global->parallel_max = PARALLEL_DEFAULT;
2264         break;
2265       case 'c':   /* --parallel-connect */
2266         global->parallel_connect = toggle;
2267         break;
2268       }
2269       break;
2270     case 'z': /* time condition coming up */
2271       switch(*nextarg) {
2272       case '+':
2273         nextarg++;
2274         /* FALLTHROUGH */
2275       default:
2276         /* If-Modified-Since: (section 14.28 in RFC2068) */
2277         config->timecond = CURL_TIMECOND_IFMODSINCE;
2278         break;
2279       case '-':
2280         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
2281         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
2282         nextarg++;
2283         break;
2284       case '=':
2285         /* Last-Modified:  (section 14.29 in RFC2068) */
2286         config->timecond = CURL_TIMECOND_LASTMOD;
2287         nextarg++;
2288         break;
2289       }
2290       now = time(NULL);
2291       config->condtime = (curl_off_t)curl_getdate(nextarg, &now);
2292       if(-1 == config->condtime) {
2293         /* now let's see if it is a file name to get the time from instead! */
2294         curl_off_t filetime = getfiletime(nextarg, global);
2295         if(filetime >= 0) {
2296           /* pull the time out from the file */
2297           config->condtime = filetime;
2298         }
2299         else {
2300           /* failed, remove time condition */
2301           config->timecond = CURL_TIMECOND_NONE;
2302           warnf(global,
2303                 "Illegal date format for -z, --time-cond (and not "
2304                 "a file name). Disabling time condition. "
2305                 "See curl_getdate(3) for valid date syntax.\n");
2306         }
2307       }
2308       break;
2309     default: /* unknown flag */
2310       return PARAM_OPTION_UNKNOWN;
2311     }
2312     hit = -1;
2313 
2314   } while(!longopt && !singleopt && *++parse && !*usedarg);
2315 
2316   return PARAM_OK;
2317 }
2318 
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2319 ParameterError parse_args(struct GlobalConfig *global, int argc,
2320                           argv_item_t argv[])
2321 {
2322   int i;
2323   bool stillflags;
2324   char *orig_opt = NULL;
2325   ParameterError result = PARAM_OK;
2326   struct OperationConfig *config = global->first;
2327 
2328   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2329     orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2330     if(!orig_opt)
2331       return PARAM_NO_MEM;
2332 
2333     if(stillflags && ('-' == orig_opt[0])) {
2334       bool passarg;
2335 
2336       if(!strcmp("--", orig_opt))
2337         /* This indicates the end of the flags and thus enables the
2338            following (URL) argument to start with -. */
2339         stillflags = FALSE;
2340       else {
2341         char *nextarg = (i < (argc - 1))
2342           ? curlx_convert_tchar_to_UTF8(argv[i + 1])
2343           : NULL;
2344 
2345         result = getparameter(orig_opt, nextarg, &passarg, global, config);
2346         curlx_unicodefree(nextarg);
2347         config = global->last;
2348         if(result == PARAM_NEXT_OPERATION) {
2349           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2350              returned from this function */
2351           result = PARAM_OK;
2352 
2353           if(config->url_list && config->url_list->url) {
2354             /* Allocate the next config */
2355             config->next = malloc(sizeof(struct OperationConfig));
2356             if(config->next) {
2357               /* Initialise the newly created config */
2358               config_init(config->next);
2359 
2360               /* Set the global config pointer */
2361               config->next->global = global;
2362 
2363               /* Update the last config pointer */
2364               global->last = config->next;
2365 
2366               /* Move onto the new config */
2367               config->next->prev = config;
2368               config = config->next;
2369             }
2370             else
2371               result = PARAM_NO_MEM;
2372           }
2373         }
2374         else if(!result && passarg)
2375           i++; /* we're supposed to skip this */
2376       }
2377     }
2378     else {
2379       bool used;
2380 
2381       /* Just add the URL please */
2382       result = getparameter("--url", orig_opt, &used, global,
2383                             config);
2384     }
2385 
2386     if(!result)
2387       curlx_unicodefree(orig_opt);
2388   }
2389 
2390   if(!result && config->content_disposition) {
2391     if(config->show_headers)
2392       result = PARAM_CONTDISP_SHOW_HEADER;
2393     else if(config->resume_from_current)
2394       result = PARAM_CONTDISP_RESUME_FROM;
2395   }
2396 
2397   if(result && result != PARAM_HELP_REQUESTED &&
2398      result != PARAM_MANUAL_REQUESTED &&
2399      result != PARAM_VERSION_INFO_REQUESTED &&
2400      result != PARAM_ENGINES_REQUESTED) {
2401     const char *reason = param2text(result);
2402 
2403     if(orig_opt && strcmp(":", orig_opt))
2404       helpf(global->errors, "option %s: %s\n", orig_opt, reason);
2405     else
2406       helpf(global->errors, "%s\n", reason);
2407   }
2408 
2409   curlx_unicodefree(orig_opt);
2410   return result;
2411 }
2412