• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "tool_setup.h"
25 
26 #include "strcase.h"
27 
28 #define ENABLE_CURLX_PRINTF
29 /* use our own printf() functions */
30 #include "curlx.h"
31 
32 #include "tool_binmode.h"
33 #include "tool_cfgable.h"
34 #include "tool_cb_prg.h"
35 #include "tool_filetime.h"
36 #include "tool_formparse.h"
37 #include "tool_getparam.h"
38 #include "tool_helpers.h"
39 #include "tool_libinfo.h"
40 #include "tool_msgs.h"
41 #include "tool_paramhlp.h"
42 #include "tool_parsecfg.h"
43 #include "tool_main.h"
44 #include "dynbuf.h"
45 #include "tool_stderr.h"
46 #include "var.h"
47 
48 #include "memdebug.h" /* keep this as LAST include */
49 
50 #ifdef MSDOS
51 #  define USE_WATT32
52 #endif
53 
54 #define ALLOW_BLANK TRUE
55 #define DENY_BLANK FALSE
56 
getstr(char ** str,const char * val,bool allowblank)57 static ParameterError getstr(char **str, const char *val, bool allowblank)
58 {
59   if(*str) {
60     free(*str);
61     *str = NULL;
62   }
63   if(val) {
64     if(!allowblank && !val[0])
65       return PARAM_BLANK_STRING;
66 
67     *str = strdup(val);
68     if(!*str)
69       return PARAM_NO_MEM;
70   }
71   return PARAM_OK;
72 }
73 
74 /* one enum for every command line option. The name is the verbatim long
75    option name, but in uppercase with periods and minuses replaced with
76    underscores using a "C_" prefix. */
77 typedef enum {
78   C_ABSTRACT_UNIX_SOCKET,
79   C_ALPN,
80   C_ALT_SVC,
81   C_ANYAUTH,
82   C_APPEND,
83   C_AWS_SIGV4,
84   C_BASIC,
85   C_BUFFER,
86   C_CA_NATIVE,
87   C_CACERT,
88   C_CAPATH,
89   C_CERT,
90   C_CERT_STATUS,
91   C_CERT_TYPE,
92   C_CIPHERS,
93   C_CLOBBER,
94   C_COMPRESSED,
95   C_COMPRESSED_SSH,
96   C_CONFIG,
97   C_CONNECT_TIMEOUT,
98   C_CONNECT_TO,
99   C_CONTINUE_AT,
100   C_COOKIE,
101   C_COOKIE_JAR,
102   C_CREATE_DIRS,
103   C_CREATE_FILE_MODE,
104   C_CRLF,
105   C_CRLFILE,
106   C_CURVES,
107   C_DATA,
108   C_DATA_ASCII,
109   C_DATA_BINARY,
110   C_DATA_RAW,
111   C_DATA_URLENCODE,
112   C_DELEGATION,
113   C_DIGEST,
114   C_DISABLE,
115   C_DISABLE_EPRT,
116   C_DISABLE_EPSV,
117   C_DISALLOW_USERNAME_IN_URL,
118   C_DNS_INTERFACE,
119   C_DNS_IPV4_ADDR,
120   C_DNS_IPV6_ADDR,
121   C_DNS_SERVERS,
122   C_DOH_CERT_STATUS,
123   C_DOH_INSECURE,
124   C_DOH_URL,
125   C_DUMP_HEADER,
126   C_EGD_FILE,
127   C_ENGINE,
128   C_EPRT,
129   C_EPSV,
130   C_ETAG_COMPARE,
131   C_ETAG_SAVE,
132   C_EXPECT100_TIMEOUT,
133   C_FAIL,
134   C_FAIL_EARLY,
135   C_FAIL_WITH_BODY,
136   C_FALSE_START,
137   C_FORM,
138   C_FORM_ESCAPE,
139   C_FORM_STRING,
140   C_FTP_ACCOUNT,
141   C_FTP_ALTERNATIVE_TO_USER,
142   C_FTP_CREATE_DIRS,
143   C_FTP_METHOD,
144   C_FTP_PASV,
145   C_FTP_PORT,
146   C_FTP_PRET,
147   C_FTP_SKIP_PASV_IP,
148   C_FTP_SSL,
149   C_FTP_SSL_CCC,
150   C_FTP_SSL_CCC_MODE,
151   C_FTP_SSL_CONTROL,
152   C_FTP_SSL_REQD,
153   C_GET,
154   C_GLOBOFF,
155   C_HAPPY_EYEBALLS_TIMEOUT_MS,
156   C_HAPROXY_CLIENTIP,
157   C_HAPROXY_PROTOCOL,
158   C_HEAD,
159   C_HEADER,
160   C_HELP,
161   C_HOSTPUBMD5,
162   C_HOSTPUBSHA256,
163   C_HSTS,
164   C_HTTP0_9,
165   C_HTTP1_0,
166   C_HTTP1_1,
167   C_HTTP2,
168   C_HTTP2_PRIOR_KNOWLEDGE,
169   C_HTTP3,
170   C_HTTP3_ONLY,
171   C_IGNORE_CONTENT_LENGTH,
172   C_INCLUDE,
173   C_INSECURE,
174   C_INTERFACE,
175   C_IPFS_GATEWAY,
176   C_IPV4,
177   C_IPV6,
178   C_JSON,
179   C_JUNK_SESSION_COOKIES,
180   C_KEEPALIVE,
181   C_KEEPALIVE_TIME,
182   C_KEY,
183   C_KEY_TYPE,
184   C_KRB,
185   C_KRB4,
186   C_LIBCURL,
187   C_LIMIT_RATE,
188   C_LIST_ONLY,
189   C_LOCAL_PORT,
190   C_LOCATION,
191   C_LOCATION_TRUSTED,
192   C_LOGIN_OPTIONS,
193   C_MAIL_AUTH,
194   C_MAIL_FROM,
195   C_MAIL_RCPT,
196   C_MAIL_RCPT_ALLOWFAILS,
197   C_MANUAL,
198   C_MAX_FILESIZE,
199   C_MAX_REDIRS,
200   C_MAX_TIME,
201   C_METALINK,
202   C_NEGOTIATE,
203   C_NETRC,
204   C_NETRC_FILE,
205   C_NETRC_OPTIONAL,
206   C_NEXT,
207   C_NOPROXY,
208   C_NPN,
209   C_NTLM,
210   C_NTLM_WB,
211   C_OAUTH2_BEARER,
212   C_OUTPUT,
213   C_OUTPUT_DIR,
214   C_PARALLEL,
215   C_PARALLEL_IMMEDIATE,
216   C_PARALLEL_MAX,
217   C_PASS,
218   C_PATH_AS_IS,
219   C_PINNEDPUBKEY,
220   C_POST301,
221   C_POST302,
222   C_POST303,
223   C_PREPROXY,
224   C_PROGRESS_BAR,
225   C_PROGRESS_METER,
226   C_PROTO,
227   C_PROTO_DEFAULT,
228   C_PROTO_REDIR,
229   C_PROXY,
230   C_PROXY_ANYAUTH,
231   C_PROXY_BASIC,
232   C_PROXY_CA_NATIVE,
233   C_PROXY_CACERT,
234   C_PROXY_CAPATH,
235   C_PROXY_CERT,
236   C_PROXY_CERT_TYPE,
237   C_PROXY_CIPHERS,
238   C_PROXY_CRLFILE,
239   C_PROXY_DIGEST,
240   C_PROXY_HEADER,
241   C_PROXY_HTTP2,
242   C_PROXY_INSECURE,
243   C_PROXY_KEY,
244   C_PROXY_KEY_TYPE,
245   C_PROXY_NEGOTIATE,
246   C_PROXY_NTLM,
247   C_PROXY_PASS,
248   C_PROXY_PINNEDPUBKEY,
249   C_PROXY_SERVICE_NAME,
250   C_PROXY_SSL_ALLOW_BEAST,
251   C_PROXY_SSL_AUTO_CLIENT_CERT,
252   C_PROXY_TLS13_CIPHERS,
253   C_PROXY_TLSAUTHTYPE,
254   C_PROXY_TLSPASSWORD,
255   C_PROXY_TLSUSER,
256   C_PROXY_TLSV1,
257   C_PROXY_USER,
258   C_PROXY1_0,
259   C_PROXYTUNNEL,
260   C_PUBKEY,
261   C_QUOTE,
262   C_RANDOM_FILE,
263   C_RANGE,
264   C_RATE,
265   C_RAW,
266   C_REFERER,
267   C_REMOTE_HEADER_NAME,
268   C_REMOTE_NAME,
269   C_REMOTE_NAME_ALL,
270   C_REMOTE_TIME,
271   C_REMOVE_ON_ERROR,
272   C_REQUEST,
273   C_REQUEST_TARGET,
274   C_RESOLVE,
275   C_RETRY,
276   C_RETRY_ALL_ERRORS,
277   C_RETRY_CONNREFUSED,
278   C_RETRY_DELAY,
279   C_RETRY_MAX_TIME,
280   C_SASL_AUTHZID,
281   C_SASL_IR,
282   C_SERVICE_NAME,
283   C_SESSIONID,
284   C_SHOW_ERROR,
285   C_SILENT,
286   C_SOCKS4,
287   C_SOCKS4A,
288   C_SOCKS5,
289   C_SOCKS5_BASIC,
290   C_SOCKS5_GSSAPI,
291   C_SOCKS5_GSSAPI_NEC,
292   C_SOCKS5_GSSAPI_SERVICE,
293   C_SOCKS5_HOSTNAME,
294   C_SPEED_LIMIT,
295   C_SPEED_TIME,
296   C_SSL,
297   C_SSL_ALLOW_BEAST,
298   C_SSL_AUTO_CLIENT_CERT,
299   C_SSL_NO_REVOKE,
300   C_SSL_REQD,
301   C_SSL_REVOKE_BEST_EFFORT,
302   C_SSLV2,
303   C_SSLV3,
304   C_STDERR,
305   C_STYLED_OUTPUT,
306   C_SUPPRESS_CONNECT_HEADERS,
307   C_TCP_FASTOPEN,
308   C_TCP_NODELAY,
309   C_TELNET_OPTION,
310   C_TEST_EVENT,
311   C_TFTP_BLKSIZE,
312   C_TFTP_NO_OPTIONS,
313   C_TIME_COND,
314   C_TLS_MAX,
315   C_TLS13_CIPHERS,
316   C_TLSAUTHTYPE,
317   C_TLSPASSWORD,
318   C_TLSUSER,
319   C_TLSV1,
320   C_TLSV1_0,
321   C_TLSV1_1,
322   C_TLSV1_2,
323   C_TLSV1_3,
324   C_TR_ENCODING,
325   C_TRACE,
326   C_TRACE_ASCII,
327   C_TRACE_CONFIG,
328   C_TRACE_IDS,
329   C_TRACE_TIME,
330   C_UNIX_SOCKET,
331   C_UPLOAD_FILE,
332   C_URL,
333   C_URL_QUERY,
334   C_USE_ASCII,
335   C_USER,
336   C_USER_AGENT,
337   C_VARIABLE,
338   C_VERBOSE,
339   C_VERSION,
340   C_WDEBUG,
341   C_WRITE_OUT,
342   C_XATTR
343 } cmdline_t;
344 
345 struct LongShort {
346   const char *lname;  /* long name option */
347   enum {
348     ARG_NONE, /* stand-alone but not a boolean */
349     ARG_BOOL, /* accepts a --no-[name] prefix */
350     ARG_STRG, /* requires an argument */
351     ARG_FILE  /* requires an argument, usually a file name */
352   } desc;
353   char letter;  /* short name option or ' ' */
354   cmdline_t cmd;
355 };
356 
357 /* this array MUST be alphasorted based on the 'lname' */
358 static const struct LongShort aliases[]= {
359   {"abstract-unix-socket",       ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET},
360   {"alpn",                       ARG_BOOL, ' ', C_ALPN},
361   {"alt-svc",                    ARG_STRG, ' ', C_ALT_SVC},
362   {"anyauth",                    ARG_BOOL, ' ', C_ANYAUTH},
363   {"append",                     ARG_BOOL, 'a', C_APPEND},
364   {"aws-sigv4",                  ARG_STRG, ' ', C_AWS_SIGV4},
365   {"basic",                      ARG_BOOL, ' ', C_BASIC},
366   {"buffer",                     ARG_BOOL, 'N', C_BUFFER},
367   {"ca-native",                  ARG_BOOL, ' ', C_CA_NATIVE},
368   {"cacert",                     ARG_FILE, ' ', C_CACERT},
369   {"capath",                     ARG_FILE, ' ', C_CAPATH},
370   {"cert",                       ARG_FILE, 'E', C_CERT},
371   {"cert-status",                ARG_BOOL, ' ', C_CERT_STATUS},
372   {"cert-type",                  ARG_STRG, ' ', C_CERT_TYPE},
373   {"ciphers",                    ARG_STRG, ' ', C_CIPHERS},
374   {"clobber",                    ARG_BOOL, ' ', C_CLOBBER},
375   {"compressed",                 ARG_BOOL, ' ', C_COMPRESSED},
376   {"compressed-ssh",             ARG_BOOL, ' ', C_COMPRESSED_SSH},
377   {"config",                     ARG_FILE, 'K', C_CONFIG},
378   {"connect-timeout",            ARG_STRG, ' ', C_CONNECT_TIMEOUT},
379   {"connect-to",                 ARG_STRG, ' ', C_CONNECT_TO},
380   {"continue-at",                ARG_STRG, 'C', C_CONTINUE_AT},
381   {"cookie",                     ARG_STRG, 'b', C_COOKIE},
382   {"cookie-jar",                 ARG_STRG, 'c', C_COOKIE_JAR},
383   {"create-dirs",                ARG_BOOL, ' ', C_CREATE_DIRS},
384   {"create-file-mode",           ARG_STRG, ' ', C_CREATE_FILE_MODE},
385   {"crlf",                       ARG_BOOL, ' ', C_CRLF},
386   {"crlfile",                    ARG_FILE, ' ', C_CRLFILE},
387   {"curves",                     ARG_STRG, ' ', C_CURVES},
388   {"data",                       ARG_STRG, 'd', C_DATA},
389   {"data-ascii",                 ARG_STRG, ' ', C_DATA_ASCII},
390   {"data-binary",                ARG_STRG, ' ', C_DATA_BINARY},
391   {"data-raw",                   ARG_STRG, ' ', C_DATA_RAW},
392   {"data-urlencode",             ARG_STRG, ' ', C_DATA_URLENCODE},
393   {"delegation",                 ARG_STRG, ' ', C_DELEGATION},
394   {"digest",                     ARG_BOOL, ' ', C_DIGEST},
395   {"disable",                    ARG_BOOL, 'q', C_DISABLE},
396   {"disable-eprt",               ARG_BOOL, ' ', C_DISABLE_EPRT},
397   {"disable-epsv",               ARG_BOOL, ' ', C_DISABLE_EPSV},
398   {"disallow-username-in-url",   ARG_BOOL, ' ', C_DISALLOW_USERNAME_IN_URL},
399   {"dns-interface",              ARG_STRG, ' ', C_DNS_INTERFACE},
400   {"dns-ipv4-addr",              ARG_STRG, ' ', C_DNS_IPV4_ADDR},
401   {"dns-ipv6-addr",              ARG_STRG, ' ', C_DNS_IPV6_ADDR},
402   {"dns-servers",                ARG_STRG, ' ', C_DNS_SERVERS},
403   {"doh-cert-status",            ARG_BOOL, ' ', C_DOH_CERT_STATUS},
404   {"doh-insecure",               ARG_BOOL, ' ', C_DOH_INSECURE},
405   {"doh-url"        ,            ARG_STRG, ' ', C_DOH_URL},
406   {"dump-header",                ARG_FILE, 'D', C_DUMP_HEADER},
407   {"egd-file",                   ARG_STRG, ' ', C_EGD_FILE},
408   {"engine",                     ARG_STRG, ' ', C_ENGINE},
409   {"eprt",                       ARG_BOOL, ' ', C_EPRT},
410   {"epsv",                       ARG_BOOL, ' ', C_EPSV},
411   {"etag-compare",               ARG_FILE, ' ', C_ETAG_COMPARE},
412   {"etag-save",                  ARG_FILE, ' ', C_ETAG_SAVE},
413   {"expect100-timeout",          ARG_STRG, ' ', C_EXPECT100_TIMEOUT},
414   {"fail",                       ARG_BOOL, 'f', C_FAIL},
415   {"fail-early",                 ARG_BOOL, ' ', C_FAIL_EARLY},
416   {"fail-with-body",             ARG_BOOL, ' ', C_FAIL_WITH_BODY},
417   {"false-start",                ARG_BOOL, ' ', C_FALSE_START},
418   {"form",                       ARG_STRG, 'F', C_FORM},
419   {"form-escape",                ARG_BOOL, ' ', C_FORM_ESCAPE},
420   {"form-string",                ARG_STRG, ' ', C_FORM_STRING},
421   {"ftp-account",                ARG_STRG, ' ', C_FTP_ACCOUNT},
422   {"ftp-alternative-to-user",    ARG_STRG, ' ', C_FTP_ALTERNATIVE_TO_USER},
423   {"ftp-create-dirs",            ARG_BOOL, ' ', C_FTP_CREATE_DIRS},
424   {"ftp-method",                 ARG_STRG, ' ', C_FTP_METHOD},
425   {"ftp-pasv",                   ARG_BOOL, ' ', C_FTP_PASV},
426   {"ftp-port",                   ARG_STRG, 'P', C_FTP_PORT},
427   {"ftp-pret",                   ARG_BOOL, ' ', C_FTP_PRET},
428   {"ftp-skip-pasv-ip",           ARG_BOOL, ' ', C_FTP_SKIP_PASV_IP},
429   {"ftp-ssl",                    ARG_BOOL, ' ', C_FTP_SSL},
430   {"ftp-ssl-ccc",                ARG_BOOL, ' ', C_FTP_SSL_CCC},
431   {"ftp-ssl-ccc-mode",           ARG_STRG, ' ', C_FTP_SSL_CCC_MODE},
432   {"ftp-ssl-control",            ARG_BOOL, ' ', C_FTP_SSL_CONTROL},
433   {"ftp-ssl-reqd",               ARG_BOOL, ' ', C_FTP_SSL_REQD},
434   {"get",                        ARG_BOOL, 'G', C_GET},
435   {"globoff",                    ARG_BOOL, 'g', C_GLOBOFF},
436   {"happy-eyeballs-timeout-ms",  ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS},
437   {"haproxy-clientip",           ARG_STRG, ' ', C_HAPROXY_CLIENTIP},
438   {"haproxy-protocol",           ARG_BOOL, ' ', C_HAPROXY_PROTOCOL},
439   {"head",                       ARG_BOOL, 'I', C_HEAD},
440   {"header",                     ARG_STRG, 'H', C_HEADER},
441   {"help",                       ARG_BOOL, 'h', C_HELP},
442   {"hostpubmd5",                 ARG_STRG, ' ', C_HOSTPUBMD5},
443   {"hostpubsha256",              ARG_STRG, ' ', C_HOSTPUBSHA256},
444   {"hsts",                       ARG_STRG, ' ', C_HSTS},
445   {"http0.9",                    ARG_BOOL, ' ', C_HTTP0_9},
446   {"http1.0",                    ARG_NONE, '0', C_HTTP1_0},
447   {"http1.1",                    ARG_NONE, ' ', C_HTTP1_1},
448   {"http2",                      ARG_NONE, ' ', C_HTTP2},
449   {"http2-prior-knowledge",      ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE},
450   {"http3",                      ARG_NONE, ' ', C_HTTP3},
451   {"http3-only",                 ARG_NONE, ' ', C_HTTP3_ONLY},
452   {"ignore-content-length",      ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH},
453   {"include",                    ARG_BOOL, 'i', C_INCLUDE},
454   {"insecure",                   ARG_BOOL, 'k', C_INSECURE},
455   {"interface",                  ARG_STRG, ' ', C_INTERFACE},
456   {"ipfs-gateway",               ARG_STRG, ' ', C_IPFS_GATEWAY},
457   {"ipv4",                       ARG_NONE, '4', C_IPV4},
458   {"ipv6",                       ARG_NONE, '6', C_IPV6},
459   {"json",                       ARG_STRG, ' ', C_JSON},
460   {"junk-session-cookies",       ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES},
461   {"keepalive",                  ARG_BOOL, ' ', C_KEEPALIVE},
462   {"keepalive-time",             ARG_STRG, ' ', C_KEEPALIVE_TIME},
463   {"key",                        ARG_FILE, ' ', C_KEY},
464   {"key-type",                   ARG_STRG, ' ', C_KEY_TYPE},
465   {"krb",                        ARG_STRG, ' ', C_KRB},
466   {"krb4",                       ARG_STRG, ' ', C_KRB4},
467   {"libcurl",                    ARG_STRG, ' ', C_LIBCURL},
468   {"limit-rate",                 ARG_STRG, ' ', C_LIMIT_RATE},
469   {"list-only",                  ARG_BOOL, 'l', C_LIST_ONLY},
470   {"local-port",                 ARG_STRG, ' ', C_LOCAL_PORT},
471   {"location",                   ARG_BOOL, 'L', C_LOCATION},
472   {"location-trusted",           ARG_BOOL, ' ', C_LOCATION_TRUSTED},
473   {"login-options",              ARG_STRG, ' ', C_LOGIN_OPTIONS},
474   {"mail-auth",                  ARG_STRG, ' ', C_MAIL_AUTH},
475   {"mail-from",                  ARG_STRG, ' ', C_MAIL_FROM},
476   {"mail-rcpt",                  ARG_STRG, ' ', C_MAIL_RCPT},
477   {"mail-rcpt-allowfails",       ARG_BOOL, ' ', C_MAIL_RCPT_ALLOWFAILS},
478   {"manual",                     ARG_BOOL, 'M', C_MANUAL},
479   {"max-filesize",               ARG_STRG, ' ', C_MAX_FILESIZE},
480   {"max-redirs",                 ARG_STRG, ' ', C_MAX_REDIRS},
481   {"max-time",                   ARG_STRG, 'm', C_MAX_TIME},
482   {"metalink",                   ARG_BOOL, ' ', C_METALINK},
483   {"negotiate",                  ARG_BOOL, ' ', C_NEGOTIATE},
484   {"netrc",                      ARG_BOOL, 'n', C_NETRC},
485   {"netrc-file",                 ARG_FILE, ' ', C_NETRC_FILE},
486   {"netrc-optional",             ARG_BOOL, ' ', C_NETRC_OPTIONAL},
487   {"next",                       ARG_NONE, ':', C_NEXT},
488   {"noproxy",                    ARG_STRG, ' ', C_NOPROXY},
489   {"npn",                        ARG_BOOL, ' ', C_NPN},
490   {"ntlm",                       ARG_BOOL, ' ', C_NTLM},
491   {"ntlm-wb",                    ARG_BOOL, ' ', C_NTLM_WB},
492   {"oauth2-bearer",              ARG_STRG, ' ', C_OAUTH2_BEARER},
493   {"output",                     ARG_FILE, 'o', C_OUTPUT},
494   {"output-dir",                 ARG_STRG, ' ', C_OUTPUT_DIR},
495   {"parallel",                   ARG_BOOL, 'Z', C_PARALLEL},
496   {"parallel-immediate",         ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE},
497   {"parallel-max",               ARG_STRG, ' ', C_PARALLEL_MAX},
498   {"pass",                       ARG_STRG, ' ', C_PASS},
499   {"path-as-is",                 ARG_BOOL, ' ', C_PATH_AS_IS},
500   {"pinnedpubkey",               ARG_STRG, ' ', C_PINNEDPUBKEY},
501   {"post301",                    ARG_BOOL, ' ', C_POST301},
502   {"post302",                    ARG_BOOL, ' ', C_POST302},
503   {"post303",                    ARG_BOOL, ' ', C_POST303},
504   {"preproxy",                   ARG_STRG, ' ', C_PREPROXY},
505   {"progress-bar",               ARG_BOOL, '#', C_PROGRESS_BAR},
506   {"progress-meter",             ARG_BOOL, ' ', C_PROGRESS_METER},
507   {"proto",                      ARG_STRG, ' ', C_PROTO},
508   {"proto-default",              ARG_STRG, ' ', C_PROTO_DEFAULT},
509   {"proto-redir",                ARG_STRG, ' ', C_PROTO_REDIR},
510   {"proxy",                      ARG_STRG, 'x', C_PROXY},
511   {"proxy-anyauth",              ARG_BOOL, ' ', C_PROXY_ANYAUTH},
512   {"proxy-basic",                ARG_BOOL, ' ', C_PROXY_BASIC},
513   {"proxy-ca-native",            ARG_BOOL, ' ', C_PROXY_CA_NATIVE},
514   {"proxy-cacert",               ARG_FILE, ' ', C_PROXY_CACERT},
515   {"proxy-capath",               ARG_FILE, ' ', C_PROXY_CAPATH},
516   {"proxy-cert",                 ARG_FILE, ' ', C_PROXY_CERT},
517   {"proxy-cert-type",            ARG_STRG, ' ', C_PROXY_CERT_TYPE},
518   {"proxy-ciphers",              ARG_STRG, ' ', C_PROXY_CIPHERS},
519   {"proxy-crlfile",              ARG_FILE, ' ', C_PROXY_CRLFILE},
520   {"proxy-digest",               ARG_BOOL, ' ', C_PROXY_DIGEST},
521   {"proxy-header",               ARG_STRG, ' ', C_PROXY_HEADER},
522   {"proxy-http2",                ARG_BOOL, ' ', C_PROXY_HTTP2},
523   {"proxy-insecure",             ARG_BOOL, ' ', C_PROXY_INSECURE},
524   {"proxy-key",                  ARG_FILE, ' ', C_PROXY_KEY},
525   {"proxy-key-type",             ARG_STRG, ' ', C_PROXY_KEY_TYPE},
526   {"proxy-negotiate",            ARG_BOOL, ' ', C_PROXY_NEGOTIATE},
527   {"proxy-ntlm",                 ARG_BOOL, ' ', C_PROXY_NTLM},
528   {"proxy-pass",                 ARG_STRG, ' ', C_PROXY_PASS},
529   {"proxy-pinnedpubkey",         ARG_STRG, ' ', C_PROXY_PINNEDPUBKEY},
530   {"proxy-service-name",         ARG_STRG, ' ', C_PROXY_SERVICE_NAME},
531   {"proxy-ssl-allow-beast",      ARG_BOOL, ' ', C_PROXY_SSL_ALLOW_BEAST},
532   {"proxy-ssl-auto-client-cert", ARG_BOOL, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT},
533   {"proxy-tls13-ciphers",        ARG_STRG, ' ', C_PROXY_TLS13_CIPHERS},
534   {"proxy-tlsauthtype",          ARG_STRG, ' ', C_PROXY_TLSAUTHTYPE},
535   {"proxy-tlspassword",          ARG_STRG, ' ', C_PROXY_TLSPASSWORD},
536   {"proxy-tlsuser",              ARG_STRG, ' ', C_PROXY_TLSUSER},
537   {"proxy-tlsv1",                ARG_NONE, ' ', C_PROXY_TLSV1},
538   {"proxy-user",                 ARG_STRG, 'U', C_PROXY_USER},
539   {"proxy1.0",                   ARG_STRG, ' ', C_PROXY1_0},
540   {"proxytunnel",                ARG_BOOL, 'p', C_PROXYTUNNEL},
541   {"pubkey",                     ARG_STRG, ' ', C_PUBKEY},
542   {"quote",                      ARG_STRG, 'Q', C_QUOTE},
543   {"random-file",                ARG_FILE, ' ', C_RANDOM_FILE},
544   {"range",                      ARG_STRG, 'r', C_RANGE},
545   {"rate",                       ARG_STRG, ' ', C_RATE},
546   {"raw",                        ARG_BOOL, ' ', C_RAW},
547   {"referer",                    ARG_STRG, 'e', C_REFERER},
548   {"remote-header-name",         ARG_BOOL, 'J', C_REMOTE_HEADER_NAME},
549   {"remote-name",                ARG_BOOL, 'O', C_REMOTE_NAME},
550   {"remote-name-all",            ARG_BOOL, ' ', C_REMOTE_NAME_ALL},
551   {"remote-time",                ARG_BOOL, 'R', C_REMOTE_TIME},
552   {"remove-on-error",            ARG_BOOL, ' ', C_REMOVE_ON_ERROR},
553   {"request",                    ARG_STRG, 'X', C_REQUEST},
554   {"request-target",             ARG_STRG, ' ', C_REQUEST_TARGET},
555   {"resolve",                    ARG_STRG, ' ', C_RESOLVE},
556   {"retry",                      ARG_STRG, ' ', C_RETRY},
557   {"retry-all-errors",           ARG_BOOL, ' ', C_RETRY_ALL_ERRORS},
558   {"retry-connrefused",          ARG_BOOL, ' ', C_RETRY_CONNREFUSED},
559   {"retry-delay",                ARG_STRG, ' ', C_RETRY_DELAY},
560   {"retry-max-time",             ARG_STRG, ' ', C_RETRY_MAX_TIME},
561   {"sasl-authzid",               ARG_STRG, ' ', C_SASL_AUTHZID},
562   {"sasl-ir",                    ARG_BOOL, ' ', C_SASL_IR},
563   {"service-name",               ARG_STRG, ' ', C_SERVICE_NAME},
564   {"sessionid",                  ARG_BOOL, ' ', C_SESSIONID},
565   {"show-error",                 ARG_BOOL, 'S', C_SHOW_ERROR},
566   {"silent",                     ARG_BOOL, 's', C_SILENT},
567   {"socks4",                     ARG_STRG, ' ', C_SOCKS4},
568   {"socks4a",                    ARG_STRG, ' ', C_SOCKS4A},
569   {"socks5",                     ARG_STRG, ' ', C_SOCKS5},
570   {"socks5-basic",               ARG_BOOL, ' ', C_SOCKS5_BASIC},
571   {"socks5-gssapi",              ARG_BOOL, ' ', C_SOCKS5_GSSAPI},
572   {"socks5-gssapi-nec",          ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC},
573   {"socks5-gssapi-service",      ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE},
574   {"socks5-hostname",            ARG_STRG, ' ', C_SOCKS5_HOSTNAME},
575   {"speed-limit",                ARG_STRG, 'Y', C_SPEED_LIMIT},
576   {"speed-time",                 ARG_STRG, 'y', C_SPEED_TIME},
577   {"ssl",                        ARG_BOOL, ' ', C_SSL},
578   {"ssl-allow-beast",            ARG_BOOL, ' ', C_SSL_ALLOW_BEAST},
579   {"ssl-auto-client-cert",       ARG_BOOL, ' ', C_SSL_AUTO_CLIENT_CERT},
580   {"ssl-no-revoke",              ARG_BOOL, ' ', C_SSL_NO_REVOKE},
581   {"ssl-reqd",                   ARG_BOOL, ' ', C_SSL_REQD},
582   {"ssl-revoke-best-effort",     ARG_BOOL, ' ', C_SSL_REVOKE_BEST_EFFORT},
583   {"sslv2",                      ARG_NONE, '2', C_SSLV2},
584   {"sslv3",                      ARG_NONE, '3', C_SSLV3},
585   {"stderr",                     ARG_FILE, ' ', C_STDERR},
586   {"styled-output",              ARG_BOOL, ' ', C_STYLED_OUTPUT},
587   {"suppress-connect-headers",   ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS},
588   {"tcp-fastopen",               ARG_BOOL, ' ', C_TCP_FASTOPEN},
589   {"tcp-nodelay",                ARG_BOOL, ' ', C_TCP_NODELAY},
590   {"telnet-option",              ARG_STRG, 't', C_TELNET_OPTION},
591   {"test-event",                 ARG_BOOL, ' ', C_TEST_EVENT},
592   {"tftp-blksize",               ARG_STRG, ' ', C_TFTP_BLKSIZE},
593   {"tftp-no-options",            ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},
594   {"time-cond",                  ARG_STRG, 'z', C_TIME_COND},
595   {"tls-max",                    ARG_STRG, ' ', C_TLS_MAX},
596   {"tls13-ciphers",              ARG_STRG, ' ', C_TLS13_CIPHERS},
597   {"tlsauthtype",                ARG_STRG, ' ', C_TLSAUTHTYPE},
598   {"tlspassword",                ARG_STRG, ' ', C_TLSPASSWORD},
599   {"tlsuser",                    ARG_STRG, ' ', C_TLSUSER},
600   {"tlsv1",                      ARG_NONE, '1', C_TLSV1},
601   {"tlsv1.0",                    ARG_NONE, ' ', C_TLSV1_0},
602   {"tlsv1.1",                    ARG_NONE, ' ', C_TLSV1_1},
603   {"tlsv1.2",                    ARG_NONE, ' ', C_TLSV1_2},
604   {"tlsv1.3",                    ARG_NONE, ' ', C_TLSV1_3},
605   {"tr-encoding",                ARG_BOOL, ' ', C_TR_ENCODING},
606   {"trace",                      ARG_FILE, ' ', C_TRACE},
607   {"trace-ascii",                ARG_FILE, ' ', C_TRACE_ASCII},
608   {"trace-config",               ARG_STRG, ' ', C_TRACE_CONFIG},
609   {"trace-ids",                  ARG_BOOL, ' ', C_TRACE_IDS},
610   {"trace-time",                 ARG_BOOL, ' ', C_TRACE_TIME},
611   {"unix-socket",                ARG_FILE, ' ', C_UNIX_SOCKET},
612   {"upload-file",                ARG_FILE, 'T', C_UPLOAD_FILE},
613   {"url",                        ARG_STRG, ' ', C_URL},
614   {"url-query",                  ARG_STRG, ' ', C_URL_QUERY},
615   {"use-ascii",                  ARG_BOOL, 'B', C_USE_ASCII},
616   {"user",                       ARG_STRG, 'u', C_USER},
617   {"user-agent",                 ARG_STRG, 'A', C_USER_AGENT},
618   {"variable",                   ARG_STRG, ' ', C_VARIABLE},
619   {"verbose",                    ARG_BOOL, 'v', C_VERBOSE},
620   {"version",                    ARG_BOOL, 'V', C_VERSION},
621 #ifdef USE_WATT32
622   {"wdebug",                     ARG_BOOL, ' ', C_WDEBUG},
623 #endif
624   {"write-out",                  ARG_STRG, 'w', C_WRITE_OUT},
625   {"xattr",                      ARG_BOOL, ' ', C_XATTR},
626 };
627 
628 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
629  * We allow ':' and '\' to be escaped by '\' so that we can use certificate
630  * nicknames containing ':'.  See <https://sourceforge.net/p/curl/bugs/1196/>
631  * for details. */
632 #ifndef UNITTESTS
633 static
634 #endif
parse_cert_parameter(const char * cert_parameter,char ** certname,char ** passphrase)635 void parse_cert_parameter(const char *cert_parameter,
636                           char **certname,
637                           char **passphrase)
638 {
639   size_t param_length = strlen(cert_parameter);
640   size_t span;
641   const char *param_place = NULL;
642   char *certname_place = NULL;
643   *certname = NULL;
644   *passphrase = NULL;
645 
646   /* most trivial assumption: cert_parameter is empty */
647   if(param_length == 0)
648     return;
649 
650   /* next less trivial: cert_parameter starts 'pkcs11:' and thus
651    * looks like a RFC7512 PKCS#11 URI which can be used as-is.
652    * Also if cert_parameter contains no colon nor backslash, this
653    * means no passphrase was given and no characters escaped */
654   if(curl_strnequal(cert_parameter, "pkcs11:", 7) ||
655      !strpbrk(cert_parameter, ":\\")) {
656     *certname = strdup(cert_parameter);
657     return;
658   }
659   /* deal with escaped chars; find unescaped colon if it exists */
660   certname_place = malloc(param_length + 1);
661   if(!certname_place)
662     return;
663 
664   *certname = certname_place;
665   param_place = cert_parameter;
666   while(*param_place) {
667     span = strcspn(param_place, ":\\");
668     strncpy(certname_place, param_place, span);
669     param_place += span;
670     certname_place += span;
671     /* we just ate all the non-special chars. now we're on either a special
672      * char or the end of the string. */
673     switch(*param_place) {
674     case '\0':
675       break;
676     case '\\':
677       param_place++;
678       switch(*param_place) {
679         case '\0':
680           *certname_place++ = '\\';
681           break;
682         case '\\':
683           *certname_place++ = '\\';
684           param_place++;
685           break;
686         case ':':
687           *certname_place++ = ':';
688           param_place++;
689           break;
690         default:
691           *certname_place++ = '\\';
692           *certname_place++ = *param_place;
693           param_place++;
694           break;
695       }
696       break;
697     case ':':
698       /* Since we live in a world of weirdness and confusion, the win32
699          dudes can use : when using drive letters and thus c:\file:password
700          needs to work. In order not to break compatibility, we still use : as
701          separator, but we try to detect when it is used for a file name! On
702          windows. */
703 #ifdef _WIN32
704       if((param_place == &cert_parameter[1]) &&
705          (cert_parameter[2] == '\\' || cert_parameter[2] == '/') &&
706          (ISALPHA(cert_parameter[0])) ) {
707         /* colon in the second column, followed by a backslash, and the
708            first character is an alphabetic letter:
709 
710            this is a drive letter colon */
711         *certname_place++ = ':';
712         param_place++;
713         break;
714       }
715 #endif
716       /* escaped colons and Windows drive letter colons were handled
717        * above; if we're still here, this is a separating colon */
718       param_place++;
719       if(*param_place) {
720         *passphrase = strdup(param_place);
721       }
722       goto done;
723     }
724   }
725 done:
726   *certname_place = '\0';
727 }
728 
729 /* Replace (in-place) '%20' by '+' according to RFC1866 */
replace_url_encoded_space_by_plus(char * url)730 static size_t replace_url_encoded_space_by_plus(char *url)
731 {
732   size_t orig_len = strlen(url);
733   size_t orig_index = 0;
734   size_t new_index = 0;
735 
736   while(orig_index < orig_len) {
737     if((url[orig_index] == '%') &&
738        (url[orig_index + 1] == '2') &&
739        (url[orig_index + 2] == '0')) {
740       url[new_index] = '+';
741       orig_index += 3;
742     }
743     else{
744       if(new_index != orig_index) {
745         url[new_index] = url[orig_index];
746       }
747       orig_index++;
748     }
749     new_index++;
750   }
751 
752   url[new_index] = 0; /* terminate string */
753 
754   return new_index; /* new size */
755 }
756 
757 static void
GetFileAndPassword(char * nextarg,char ** file,char ** password)758 GetFileAndPassword(char *nextarg, char **file, char **password)
759 {
760   char *certname, *passphrase;
761   if(nextarg) {
762     parse_cert_parameter(nextarg, &certname, &passphrase);
763     Curl_safefree(*file);
764     *file = certname;
765     if(passphrase) {
766       Curl_safefree(*password);
767       *password = passphrase;
768     }
769   }
770 }
771 
772 /* Get a size parameter for '--limit-rate' or '--max-filesize'.
773  * We support a 'G', 'M' or 'K' suffix too.
774   */
GetSizeParameter(struct GlobalConfig * global,const char * arg,const char * which,curl_off_t * value_out)775 static ParameterError GetSizeParameter(struct GlobalConfig *global,
776                                        const char *arg,
777                                        const char *which,
778                                        curl_off_t *value_out)
779 {
780   char *unit;
781   curl_off_t value;
782 
783   if(curlx_strtoofft(arg, &unit, 10, &value)) {
784     warnf(global, "invalid number specified for %s", which);
785     return PARAM_BAD_USE;
786   }
787 
788   if(!*unit)
789     unit = (char *)"b";
790   else if(strlen(unit) > 1)
791     unit = (char *)"w"; /* unsupported */
792 
793   switch(*unit) {
794   case 'G':
795   case 'g':
796     if(value > (CURL_OFF_T_MAX / (1024*1024*1024)))
797       return PARAM_NUMBER_TOO_LARGE;
798     value *= 1024*1024*1024;
799     break;
800   case 'M':
801   case 'm':
802     if(value > (CURL_OFF_T_MAX / (1024*1024)))
803       return PARAM_NUMBER_TOO_LARGE;
804     value *= 1024*1024;
805     break;
806   case 'K':
807   case 'k':
808     if(value > (CURL_OFF_T_MAX / 1024))
809       return PARAM_NUMBER_TOO_LARGE;
810     value *= 1024;
811     break;
812   case 'b':
813   case 'B':
814     /* for plain bytes, leave as-is */
815     break;
816   default:
817     warnf(global, "unsupported %s unit. Use G, M, K or B", which);
818     return PARAM_BAD_USE;
819   }
820   *value_out = value;
821   return PARAM_OK;
822 }
823 
824 #ifdef HAVE_WRITABLE_ARGV
cleanarg(argv_item_t str)825 static void cleanarg(argv_item_t str)
826 {
827   /* now that getstr has copied the contents of nextarg, wipe the next
828    * argument out so that the username:password isn't displayed in the
829    * system process list */
830   if(str) {
831     size_t len = strlen(str);
832     memset(str, ' ', len);
833   }
834 }
835 #else
836 #define cleanarg(x)
837 #endif
838 
839 /* --data-urlencode */
data_urlencode(struct GlobalConfig * global,char * nextarg,char ** postp,size_t * lenp)840 static ParameterError data_urlencode(struct GlobalConfig *global,
841                                      char *nextarg,
842                                      char **postp,
843                                      size_t *lenp)
844 {
845   /* [name]=[content], we encode the content part only
846    * [name]@[file name]
847    *
848    * Case 2: we first load the file using that name and then encode
849    * the content.
850    */
851   ParameterError err;
852   const char *p = strchr(nextarg, '=');
853   size_t nlen;
854   char is_file;
855   char *postdata = NULL;
856   size_t size = 0;
857   if(!p)
858     /* there was no '=' letter, check for a '@' instead */
859     p = strchr(nextarg, '@');
860   if(p) {
861     nlen = p - nextarg; /* length of the name part */
862     is_file = *p++; /* pass the separator */
863   }
864   else {
865     /* neither @ nor =, so no name and it isn't a file */
866     nlen = is_file = 0;
867     p = nextarg;
868   }
869   if('@' == is_file) {
870     FILE *file;
871     /* a '@' letter, it means that a file name or - (stdin) follows */
872     if(!strcmp("-", p)) {
873       file = stdin;
874       set_binmode(stdin);
875     }
876     else {
877       file = fopen(p, "rb");
878       if(!file) {
879         errorf(global, "Failed to open %s", p);
880         return PARAM_READ_ERROR;
881       }
882     }
883 
884     err = file2memory(&postdata, &size, file);
885 
886     if(file && (file != stdin))
887       fclose(file);
888     if(err)
889       return err;
890   }
891   else {
892     err = getstr(&postdata, p, ALLOW_BLANK);
893     if(err)
894       goto error;
895     if(postdata)
896       size = strlen(postdata);
897   }
898 
899   if(!postdata) {
900     /* no data from the file, point to a zero byte string to make this
901        get sent as a POST anyway */
902     postdata = strdup("");
903     if(!postdata)
904       return PARAM_NO_MEM;
905     size = 0;
906   }
907   else {
908     char *enc = curl_easy_escape(NULL, postdata, (int)size);
909     Curl_safefree(postdata); /* no matter if it worked or not */
910     if(enc) {
911       char *n;
912       replace_url_encoded_space_by_plus(enc);
913       if(nlen > 0) { /* only append '=' if we have a name */
914         n = aprintf("%.*s=%s", (int)nlen, nextarg, enc);
915         curl_free(enc);
916         if(!n)
917           return PARAM_NO_MEM;
918       }
919       else
920         n = enc;
921 
922       size = strlen(n);
923       postdata = n;
924     }
925     else
926       return PARAM_NO_MEM;
927   }
928   *postp = postdata;
929   *lenp = size;
930   return PARAM_OK;
931 error:
932   return err;
933 }
934 
sethttpver(struct GlobalConfig * global,struct OperationConfig * config,long httpversion)935 static void sethttpver(struct GlobalConfig *global,
936                        struct OperationConfig *config,
937                        long httpversion)
938 {
939   if(config->httpversion &&
940      (config->httpversion != httpversion))
941     warnf(global, "Overrides previous HTTP version option");
942 
943   config->httpversion = httpversion;
944 }
945 
set_trace_config(struct GlobalConfig * global,const char * config)946 static CURLcode set_trace_config(struct GlobalConfig *global,
947                                  const char *config)
948 {
949   CURLcode result = CURLE_OK;
950   char *token, *tmp, *name;
951   bool toggle;
952 
953   tmp = strdup(config);
954   if(!tmp)
955     return CURLE_OUT_OF_MEMORY;
956 
957   /* Allow strtok() here since this isn't used threaded */
958   /* !checksrc! disable BANNEDFUNC 2 */
959   token = strtok(tmp, ", ");
960   while(token) {
961     switch(*token) {
962       case '-':
963         toggle = FALSE;
964         name = token + 1;
965         break;
966       case '+':
967         toggle = TRUE;
968         name = token + 1;
969         break;
970       default:
971         toggle = TRUE;
972         name = token;
973         break;
974     }
975 
976     if(strcasecompare(name, "all")) {
977       global->traceids = toggle;
978       global->tracetime = toggle;
979       result = curl_global_trace(token);
980       if(result)
981         goto out;
982     }
983     else if(strcasecompare(name, "ids")) {
984       global->traceids = toggle;
985     }
986     else if(strcasecompare(name, "time")) {
987       global->tracetime = toggle;
988     }
989     else {
990       result = curl_global_trace(token);
991       if(result)
992         goto out;
993     }
994     token = strtok(NULL, ", ");
995   }
996 out:
997   free(tmp);
998   return result;
999 }
1000 
findarg(const void * a,const void * b)1001 static int findarg(const void *a, const void *b)
1002 {
1003   const struct LongShort *aa = a;
1004   const struct LongShort *bb = b;
1005   return strcmp(aa->lname, bb->lname);
1006 }
1007 
single(char letter)1008 static const struct LongShort *single(char letter)
1009 {
1010   static const struct LongShort *singles[128 - ' ']; /* ASCII => pointer */
1011   static bool singles_done = FALSE;
1012   DEBUGASSERT((letter < 127) && (letter > ' '));
1013 
1014   if(!singles_done) {
1015     unsigned int j;
1016     for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
1017       if(aliases[j].letter != ' ') {
1018         unsigned char l = aliases[j].letter;
1019         singles[l - ' '] = &aliases[j];
1020       }
1021     }
1022     singles_done = TRUE;
1023   }
1024   return singles[letter - ' '];
1025 }
1026 
1027 #define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */
url_query(char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)1028 static ParameterError url_query(char *nextarg,
1029                                 struct GlobalConfig *global,
1030                                 struct OperationConfig *config)
1031 {
1032   size_t size = 0;
1033   ParameterError err = PARAM_OK;
1034   char *query;
1035   struct curlx_dynbuf dyn;
1036   curlx_dyn_init(&dyn, MAX_QUERY_LEN);
1037 
1038   if(*nextarg == '+') {
1039     /* use without encoding */
1040     query = strdup(&nextarg[1]);
1041     if(!query)
1042       err = PARAM_NO_MEM;
1043   }
1044   else
1045     err = data_urlencode(global, nextarg, &query, &size);
1046 
1047   if(!err) {
1048     if(config->query) {
1049       CURLcode result = curlx_dyn_addf(&dyn, "%s&%s", config->query, query);
1050       free(query);
1051       if(result)
1052         err = PARAM_NO_MEM;
1053       else {
1054         free(config->query);
1055         config->query = curlx_dyn_ptr(&dyn);
1056       }
1057     }
1058     else
1059       config->query = query;
1060   }
1061   return err;
1062 }
1063 
set_data(cmdline_t cmd,char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)1064 static ParameterError set_data(cmdline_t cmd,
1065                                char *nextarg,
1066                                struct GlobalConfig *global,
1067                                struct OperationConfig *config)
1068 {
1069   char *postdata = NULL;
1070   FILE *file;
1071   size_t size = 0;
1072   ParameterError err = PARAM_OK;
1073 
1074   if(cmd == C_DATA_URLENCODE) { /* --data-urlencode */
1075     err = data_urlencode(global, nextarg, &postdata, &size);
1076     if(err)
1077       return err;
1078   }
1079   else if('@' == *nextarg && (cmd != C_DATA_RAW)) {
1080     /* the data begins with a '@' letter, it means that a file name
1081        or - (stdin) follows */
1082     nextarg++; /* pass the @ */
1083 
1084     if(!strcmp("-", nextarg)) {
1085       file = stdin;
1086       if(cmd == C_DATA_BINARY) /* forced data-binary */
1087         set_binmode(stdin);
1088     }
1089     else {
1090       file = fopen(nextarg, "rb");
1091       if(!file) {
1092         errorf(global, "Failed to open %s", nextarg);
1093         return PARAM_READ_ERROR;
1094       }
1095     }
1096 
1097     if((cmd == C_DATA_BINARY) || /* --data-binary */
1098        (cmd == C_JSON) /* --json */)
1099       /* forced binary */
1100       err = file2memory(&postdata, &size, file);
1101     else {
1102       err = file2string(&postdata, file);
1103       if(postdata)
1104         size = strlen(postdata);
1105     }
1106 
1107     if(file && (file != stdin))
1108       fclose(file);
1109     if(err)
1110       return err;
1111 
1112     if(!postdata) {
1113       /* no data from the file, point to a zero byte string to make this
1114          get sent as a POST anyway */
1115       postdata = strdup("");
1116       if(!postdata)
1117         return PARAM_NO_MEM;
1118     }
1119   }
1120   else {
1121     err = getstr(&postdata, nextarg, ALLOW_BLANK);
1122     if(err)
1123       return err;
1124     if(postdata)
1125       size = strlen(postdata);
1126   }
1127   if(cmd == C_JSON)
1128     config->jsoned = TRUE;
1129 
1130   if(curlx_dyn_len(&config->postdata)) {
1131     /* skip separator append for --json */
1132     if(!err && (cmd != C_JSON)  &&
1133        curlx_dyn_addn(&config->postdata, "&", 1))
1134       err = PARAM_NO_MEM;
1135   }
1136 
1137   if(!err && curlx_dyn_addn(&config->postdata, postdata, size))
1138     err = PARAM_NO_MEM;
1139 
1140   Curl_safefree(postdata);
1141 
1142   config->postfields = curlx_dyn_ptr(&config->postdata);
1143   return err;
1144 }
1145 
set_rate(struct GlobalConfig * global,char * nextarg)1146 static ParameterError set_rate(struct GlobalConfig *global,
1147                                char *nextarg)
1148 {
1149   /* --rate */
1150   /* support a few different suffixes, extract the suffix first, then
1151      get the number and convert to per hour.
1152      /s == per second
1153      /m == per minute
1154      /h == per hour (default)
1155      /d == per day (24 hours)
1156   */
1157   ParameterError err = PARAM_OK;
1158   char *div = strchr(nextarg, '/');
1159   char number[26];
1160   long denominator;
1161   long numerator = 60*60*1000; /* default per hour */
1162   size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg);
1163   if(numlen > sizeof(number) -1)
1164     return PARAM_NUMBER_TOO_LARGE;
1165 
1166   strncpy(number, nextarg, numlen);
1167   number[numlen] = 0;
1168   err = str2unum(&denominator, number);
1169   if(err)
1170     return err;
1171 
1172   if(denominator < 1)
1173     return PARAM_BAD_USE;
1174 
1175   if(div) {
1176     char unit = div[1];
1177     switch(unit) {
1178     case 's': /* per second */
1179       numerator = 1000;
1180       break;
1181     case 'm': /* per minute */
1182       numerator = 60*1000;
1183       break;
1184     case 'h': /* per hour */
1185       break;
1186     case 'd': /* per day */
1187       numerator = 24*60*60*1000;
1188       break;
1189     default:
1190       errorf(global, "unsupported --rate unit");
1191       err = PARAM_BAD_USE;
1192       break;
1193     }
1194   }
1195 
1196   if(err)
1197     ;
1198   else if(denominator > numerator)
1199     err = PARAM_NUMBER_TOO_LARGE;
1200   else
1201     global->ms_per_transfer = numerator/denominator;
1202 
1203   return err;
1204 }
1205 
1206 
getparameter(const char * flag,char * nextarg,argv_item_t cleararg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)1207 ParameterError getparameter(const char *flag, /* f or -long-flag */
1208                             char *nextarg,    /* NULL if unset */
1209                             argv_item_t cleararg,
1210                             bool *usedarg,    /* set to TRUE if the arg
1211                                                  has been used */
1212                             struct GlobalConfig *global,
1213                             struct OperationConfig *config)
1214 {
1215   int rc;
1216   const char *parse = NULL;
1217   time_t now;
1218   bool longopt = FALSE;
1219   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
1220   ParameterError err = PARAM_OK;
1221   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
1222                          by using --OPTION or --no-OPTION */
1223   bool nextalloc = FALSE; /* if nextarg is allocated */
1224   struct getout *url;
1225   static const char *redir_protos[] = {
1226     "http",
1227     "https",
1228     "ftp",
1229     "ftps",
1230     NULL
1231   };
1232   const struct LongShort *a = NULL;
1233   curl_off_t value;
1234 #ifdef HAVE_WRITABLE_ARGV
1235   argv_item_t clearthis = NULL;
1236 #else
1237   (void)cleararg;
1238 #endif
1239 
1240   *usedarg = FALSE; /* default is that we don't use the arg */
1241 
1242   if(('-' != flag[0]) || ('-' == flag[1])) {
1243     /* this should be a long name */
1244     const char *word = ('-' == flag[0]) ? flag + 2 : flag;
1245     bool noflagged = FALSE;
1246     bool expand = FALSE;
1247     struct LongShort key;
1248 
1249     if(!strncmp(word, "no-", 3)) {
1250       /* disable this option but ignore the "no-" part when looking for it */
1251       word += 3;
1252       toggle = FALSE;
1253       noflagged = TRUE;
1254     }
1255     else if(!strncmp(word, "expand-", 7)) {
1256       /* variable expansions is to be done on the argument */
1257       word += 7;
1258       expand = TRUE;
1259     }
1260     key.lname = word;
1261 
1262     a = bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]),
1263                 sizeof(aliases[0]), findarg);
1264     if(a) {
1265       longopt = TRUE;
1266     }
1267     else {
1268       err = PARAM_OPTION_UNKNOWN;
1269       goto error;
1270     }
1271     if(noflagged && (a->desc != ARG_BOOL)) {
1272       /* --no- prefixed an option that isn't boolean! */
1273       err = PARAM_NO_NOT_BOOLEAN;
1274       goto error;
1275     }
1276     else if(expand && nextarg) {
1277       struct curlx_dynbuf nbuf;
1278       bool replaced;
1279 
1280       if((a->desc != ARG_STRG) &&
1281          (a->desc != ARG_FILE)) {
1282         /* --expand on an option that isn't a string or a filename */
1283         err = PARAM_EXPAND_ERROR;
1284         goto error;
1285       }
1286       err = varexpand(global, nextarg, &nbuf, &replaced);
1287       if(err) {
1288         curlx_dyn_free(&nbuf);
1289         goto error;
1290       }
1291       if(replaced) {
1292         nextarg = curlx_dyn_ptr(&nbuf);
1293         nextalloc = TRUE;
1294       }
1295     }
1296   }
1297   else {
1298     flag++; /* prefixed with one dash, pass it */
1299     parse = flag;
1300   }
1301 
1302   do {
1303     /* we can loop here if we have multiple single-letters */
1304     char letter;
1305     cmdline_t cmd;
1306 
1307     if(!longopt && !a) {
1308       a = single(*parse);
1309       if(!a) {
1310         err = PARAM_OPTION_UNKNOWN;
1311         break;
1312       }
1313     }
1314     letter = a->letter;
1315     cmd = a->cmd;
1316     if(a->desc >= ARG_STRG) {
1317       /* this option requires an extra parameter */
1318       if(!longopt && parse[1]) {
1319         nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
1320         singleopt = TRUE;   /* don't loop anymore after this */
1321       }
1322       else if(!nextarg) {
1323         err = PARAM_REQUIRES_PARAMETER;
1324         break;
1325       }
1326       else {
1327 #ifdef HAVE_WRITABLE_ARGV
1328         clearthis = cleararg;
1329 #endif
1330         *usedarg = TRUE; /* mark it as used */
1331       }
1332 
1333       if((a->desc == ARG_FILE) &&
1334          (nextarg[0] == '-') && nextarg[1]) {
1335         /* if the file name looks like a command line option */
1336         warnf(global, "The file name argument '%s' looks like a flag.",
1337               nextarg);
1338       }
1339     }
1340     else if((a->desc == ARG_NONE) && !toggle) {
1341       err = PARAM_NO_PREFIX;
1342       break;
1343     }
1344 
1345     if(!nextarg)
1346       /* this is a precaution mostly to please scan-build, as all arguments
1347          that use nextarg should be marked as such and they will check that
1348          nextarg is set before continuing, but code analyzers are not always
1349          that aware of that state */
1350       nextarg = (char *)"";
1351 
1352     switch(cmd) {
1353     case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */
1354       if(!curlinfo->ares_num) /* c-ares is needed for this */
1355         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1356       else
1357         /* addr in dot notation */
1358         err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK);
1359       break;
1360     case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */
1361       if(!curlinfo->ares_num) /* c-ares is needed for this */
1362         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1363       else
1364         /* addr in dot notation */
1365         err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK);
1366       break;
1367     case C_RANDOM_FILE: /* --random-file */
1368       break;
1369     case C_EGD_FILE: /* --egd-file */
1370       break;
1371     case C_OAUTH2_BEARER: /* --oauth2-bearer */
1372       err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK);
1373       if(!err) {
1374         cleanarg(clearthis);
1375         config->authtype |= CURLAUTH_BEARER;
1376       }
1377       break;
1378     case C_CONNECT_TIMEOUT: /* --connect-timeout */
1379       err = secs2ms(&config->connecttimeout_ms, nextarg);
1380       break;
1381     case C_DOH_URL: /* --doh-url */
1382       err = getstr(&config->doh_url, nextarg, ALLOW_BLANK);
1383       if(!err && config->doh_url && !config->doh_url[0])
1384         /* if given a blank string, make it NULL again */
1385         Curl_safefree(config->doh_url);
1386       break;
1387     case C_CIPHERS: /* -- ciphers */
1388       err = getstr(&config->cipher_list, nextarg, DENY_BLANK);
1389       break;
1390     case C_DNS_INTERFACE: /* --dns-interface */
1391       if(!curlinfo->ares_num) /* c-ares is needed for this */
1392         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1393       else
1394         /* interface name */
1395         err = getstr(&config->dns_interface, nextarg, DENY_BLANK);
1396       break;
1397     case C_DISABLE_EPSV: /* --disable-epsv */
1398       config->disable_epsv = toggle;
1399       break;
1400     case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */
1401       config->disallow_username_in_url = toggle;
1402       break;
1403     case C_EPSV: /* --epsv */
1404       config->disable_epsv = (!toggle)?TRUE:FALSE;
1405       break;
1406     case C_DNS_SERVERS: /* --dns-servers */
1407       if(!curlinfo->ares_num) /* c-ares is needed for this */
1408         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1409       else
1410         /* IP addrs of DNS servers */
1411         err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
1412       break;
1413     case C_TRACE: /* --trace */
1414       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1415       if(!err) {
1416         if(global->tracetype && (global->tracetype != TRACE_BIN))
1417           warnf(global, "--trace overrides an earlier trace/verbose option");
1418         global->tracetype = TRACE_BIN;
1419       }
1420       break;
1421     case C_NPN: /* --npn */
1422       warnf(global, "--npn is no longer supported");
1423       break;
1424     case C_TRACE_ASCII: /* --trace-ascii */
1425       err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1426       if(!err) {
1427         if(global->tracetype && (global->tracetype != TRACE_ASCII))
1428           warnf(global,
1429                 "--trace-ascii overrides an earlier trace/verbose option");
1430         global->tracetype = TRACE_ASCII;
1431       }
1432       break;
1433     case C_ALPN: /* --alpn */
1434       config->noalpn = (!toggle)?TRUE:FALSE;
1435       break;
1436     case C_LIMIT_RATE: /* --limit-rate */
1437       err = GetSizeParameter(global, nextarg, "rate", &value);
1438       if(!err) {
1439         config->recvpersecond = value;
1440         config->sendpersecond = value;
1441       }
1442       break;
1443     case C_RATE:
1444       err = set_rate(global, nextarg);
1445       break;
1446     case C_COMPRESSED: /* --compressed */
1447       if(toggle && !(feature_libz || feature_brotli || feature_zstd))
1448         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1449       else
1450         config->encoding = toggle;
1451       break;
1452     case C_TR_ENCODING: /* --tr-encoding */
1453       config->tr_encoding = toggle;
1454       break;
1455     case C_DIGEST: /* --digest */
1456       if(toggle)
1457         config->authtype |= CURLAUTH_DIGEST;
1458       else
1459         config->authtype &= ~CURLAUTH_DIGEST;
1460       break;
1461     case C_NEGOTIATE: /* --negotiate */
1462       if(!toggle)
1463         config->authtype &= ~CURLAUTH_NEGOTIATE;
1464       else if(feature_spnego)
1465         config->authtype |= CURLAUTH_NEGOTIATE;
1466       else
1467         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1468       break;
1469     case C_NTLM: /* --ntlm */
1470       if(!toggle)
1471         config->authtype &= ~CURLAUTH_NTLM;
1472       else if(feature_ntlm)
1473         config->authtype |= CURLAUTH_NTLM;
1474       else
1475         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1476       break;
1477     case C_NTLM_WB: /* --ntlm-wb */
1478       if(!toggle)
1479         config->authtype &= ~CURLAUTH_NTLM_WB;
1480       else if(feature_ntlm_wb)
1481         config->authtype |= CURLAUTH_NTLM_WB;
1482       else
1483         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1484       break;
1485     case C_BASIC: /* --basic */
1486       if(toggle)
1487         config->authtype |= CURLAUTH_BASIC;
1488       else
1489         config->authtype &= ~CURLAUTH_BASIC;
1490       break;
1491     case C_ANYAUTH: /* --anyauth */
1492       if(toggle)
1493         config->authtype = CURLAUTH_ANY;
1494       /* --no-anyauth simply doesn't touch it */
1495       break;
1496 #ifdef USE_WATT32
1497     case C_WDEBUG: /* --wdebug */
1498       dbug_init();
1499       break;
1500 #endif
1501     case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */
1502       config->ftp_create_dirs = toggle;
1503       break;
1504     case C_CREATE_DIRS: /* --create-dirs */
1505       config->create_dirs = toggle;
1506       break;
1507     case C_CREATE_FILE_MODE: /* --create-file-mode */
1508       err = oct2nummax(&config->create_file_mode, nextarg, 0777);
1509       break;
1510     case C_MAX_REDIRS: /* --max-redirs */
1511       /* specified max no of redirects (http(s)), this accepts -1 as a
1512          special condition */
1513       err = str2num(&config->maxredirs, nextarg);
1514       if(!err && (config->maxredirs < -1))
1515         err = PARAM_BAD_NUMERIC;
1516       break;
1517     case C_IPFS_GATEWAY: /* --ipfs-gateway */
1518       err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK);
1519       break;
1520     case C_PROXY_NTLM: /* --proxy-ntlm */
1521       if(!feature_ntlm)
1522         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1523       else
1524         config->proxyntlm = toggle;
1525       break;
1526     case C_CRLF: /* --crlf */
1527       /* LF -> CRLF conversion? */
1528       config->crlf = toggle;
1529       break;
1530     case C_AWS_SIGV4: /* --aws-sigv4 */
1531       config->authtype |= CURLAUTH_AWS_SIGV4;
1532       err = getstr(&config->aws_sigv4, nextarg, DENY_BLANK);
1533       break;
1534     case C_STDERR: /* --stderr */
1535       tool_set_stderr_file(global, nextarg);
1536       break;
1537     case C_INTERFACE: /* --interface */
1538       /* interface */
1539       err = getstr(&config->iface, nextarg, DENY_BLANK);
1540       break;
1541     case C_KRB: /* --krb */
1542       /* kerberos level string */
1543       if(!feature_spnego)
1544         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1545       else
1546         err = getstr(&config->krblevel, nextarg, DENY_BLANK);
1547       break;
1548     case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */
1549       config->haproxy_protocol = toggle;
1550       break;
1551     case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */
1552       err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
1553       break;
1554     case C_MAX_FILESIZE: /* --max-filesize */
1555       err = GetSizeParameter(global, nextarg, "max-filesize", &value);
1556       if(!err)
1557         config->max_filesize = value;
1558       break;
1559     case C_DISABLE_EPRT: /* --disable-eprt */
1560       config->disable_eprt = toggle;
1561       break;
1562     case C_EPRT: /* --eprt */
1563       config->disable_eprt = (!toggle)?TRUE:FALSE;
1564       break;
1565     case C_XATTR: /* --xattr */
1566       config->xattr = toggle;
1567       break;
1568     case C_URL: /* --url */
1569       if(!config->url_get)
1570         config->url_get = config->url_list;
1571 
1572       if(config->url_get) {
1573         /* there's a node here, if it already is filled-in continue to find
1574            an "empty" node */
1575         while(config->url_get && (config->url_get->flags & GETOUT_URL))
1576           config->url_get = config->url_get->next;
1577       }
1578 
1579       /* now there might or might not be an available node to fill in! */
1580 
1581       if(config->url_get)
1582         /* existing node */
1583         url = config->url_get;
1584       else
1585         /* there was no free node, create one! */
1586         config->url_get = url = new_getout(config);
1587 
1588       if(!url)
1589         err = PARAM_NO_MEM;
1590       else {
1591         /* fill in the URL */
1592         err = getstr(&url->url, nextarg, DENY_BLANK);
1593         url->flags |= GETOUT_URL;
1594       }
1595       break;
1596     case C_SSL: /* --ssl */
1597       if(toggle && !feature_ssl)
1598         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1599       else {
1600         config->ftp_ssl = toggle;
1601         if(config->ftp_ssl)
1602           warnf(global,
1603                 "--ssl is an insecure option, consider --ssl-reqd instead");
1604       }
1605       break;
1606     case C_FTP_PASV: /* --ftp-pasv */
1607       Curl_safefree(config->ftpport);
1608       break;
1609     case C_SOCKS5: /* --socks5 */
1610       /*  socks5 proxy to use, and resolves the name locally and passes on the
1611           resolved address */
1612       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1613       config->proxyver = CURLPROXY_SOCKS5;
1614       break;
1615     case C_SOCKS4: /* --socks4 */
1616       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1617       config->proxyver = CURLPROXY_SOCKS4;
1618       break;
1619     case C_SOCKS4A: /* --socks4a */
1620       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1621       config->proxyver = CURLPROXY_SOCKS4A;
1622       break;
1623     case C_SOCKS5_HOSTNAME: /* --socks5-hostname */
1624       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1625       config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
1626       break;
1627     case C_TCP_NODELAY: /* --tcp-nodelay */
1628       config->tcp_nodelay = toggle;
1629       break;
1630     case C_PROXY_DIGEST: /* --proxy-digest */
1631       config->proxydigest = toggle;
1632       break;
1633     case C_PROXY_BASIC: /* --proxy-basic */
1634       config->proxybasic = toggle;
1635       break;
1636     case C_RETRY: /* --retry */
1637       err = str2unum(&config->req_retry, nextarg);
1638       break;
1639     case C_RETRY_CONNREFUSED: /* --retry-connrefused */
1640       config->retry_connrefused = toggle;
1641       break;
1642     case C_RETRY_DELAY: /* --retry-delay */
1643       err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
1644       break;
1645     case C_RETRY_MAX_TIME: /* --retry-max-time */
1646       err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
1647       break;
1648     case C_RETRY_ALL_ERRORS: /* --retry-all-errors */
1649       config->retry_all_errors = toggle;
1650       break;
1651     case C_PROXY_NEGOTIATE: /* --proxy-negotiate */
1652       if(!feature_spnego)
1653         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1654       else
1655         config->proxynegotiate = toggle;
1656       break;
1657     case C_FORM_ESCAPE: /* --form-escape */
1658       config->mime_options &= ~CURLMIMEOPT_FORMESCAPE;
1659       if(toggle)
1660         config->mime_options |= CURLMIMEOPT_FORMESCAPE;
1661       break;
1662     case C_FTP_ACCOUNT: /* --ftp-account */
1663       err = getstr(&config->ftp_account, nextarg, DENY_BLANK);
1664       break;
1665     case C_PROXY_ANYAUTH: /* --proxy-anyauth */
1666       config->proxyanyauth = toggle;
1667       break;
1668     case C_TRACE_TIME: /* --trace-time */
1669       global->tracetime = toggle;
1670       break;
1671     case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */
1672       config->ignorecl = toggle;
1673       break;
1674     case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */
1675       config->ftp_skip_ip = toggle;
1676       break;
1677     case C_FTP_METHOD: /* --ftp-method */
1678       config->ftp_filemethod = ftpfilemethod(config, nextarg);
1679       break;
1680     case C_LOCAL_PORT: { /* --local-port */
1681       /* 16bit base 10 is 5 digits, but we allow 6 so that this catches
1682          overflows, not just truncates */
1683       char lrange[7]="";
1684       char *p = nextarg;
1685       while(ISDIGIT(*p))
1686         p++;
1687       if(*p) {
1688         /* if there's anything more than a plain decimal number */
1689         rc = sscanf(p, " - %6s", lrange);
1690         *p = 0; /* null-terminate to make str2unum() work below */
1691       }
1692       else
1693         rc = 0;
1694 
1695       err = str2unum(&config->localport, nextarg);
1696       if(err || (config->localport > 65535)) {
1697         err = PARAM_BAD_USE;
1698         break;
1699       }
1700       if(!rc)
1701         config->localportrange = 1; /* default number of ports to try */
1702       else {
1703         err = str2unum(&config->localportrange, lrange);
1704         if(err || (config->localportrange > 65535))
1705           err = PARAM_BAD_USE;
1706         else {
1707           config->localportrange -= (config->localport-1);
1708           if(config->localportrange < 1)
1709             err = PARAM_BAD_USE;
1710         }
1711       }
1712       break;
1713     }
1714     case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */
1715       err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK);
1716       break;
1717     case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */
1718     case C_SSL_REQD: /* --ssl-reqd */
1719       if(toggle && !feature_ssl) {
1720         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1721         break;
1722       }
1723       config->ftp_ssl_reqd = toggle;
1724       break;
1725     case C_SESSIONID: /* --sessionid */
1726       config->disable_sessionid = (!toggle)?TRUE:FALSE;
1727       break;
1728     case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */
1729       if(toggle && !feature_ssl)
1730         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1731       else
1732         config->ftp_ssl_control = toggle;
1733       break;
1734     case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */
1735       config->ftp_ssl_ccc = toggle;
1736       if(!config->ftp_ssl_ccc_mode)
1737         config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
1738       break;
1739     case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */
1740       config->ftp_ssl_ccc = TRUE;
1741       config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
1742       break;
1743     case C_LIBCURL: /* --libcurl */
1744 #ifdef CURL_DISABLE_LIBCURL_OPTION
1745       warnf(global,
1746             "--libcurl option was disabled at build-time");
1747       err = PARAM_OPTION_UNKNOWN;
1748 #else
1749       err = getstr(&global->libcurl, nextarg, DENY_BLANK);
1750 #endif
1751       break;
1752     case C_RAW: /* --raw */
1753       config->raw = toggle;
1754       break;
1755     case C_KEEPALIVE: /* --keepalive */
1756       config->nokeepalive = (!toggle)?TRUE:FALSE;
1757       break;
1758     case C_KEEPALIVE_TIME: /* --keepalive-time */
1759       err = str2unum(&config->alivetime, nextarg);
1760       break;
1761     case C_POST301: /* --post301 */
1762       config->post301 = toggle;
1763       break;
1764     case C_POST302: /* --post302 */
1765       config->post302 = toggle;
1766       break;
1767     case C_POST303: /* --post303 */
1768       config->post303 = toggle;
1769       break;
1770     case C_NOPROXY: /* --noproxy */
1771       /* This specifies the noproxy list */
1772       err = getstr(&config->noproxy, nextarg, ALLOW_BLANK);
1773       break;
1774     case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */
1775       config->socks5_gssapi_nec = toggle;
1776       break;
1777     case C_PROXY1_0: /* --proxy1.0 */
1778       /* http 1.0 proxy */
1779       err = getstr(&config->proxy, nextarg, DENY_BLANK);
1780       config->proxyver = CURLPROXY_HTTP_1_0;
1781       break;
1782     case C_TFTP_BLKSIZE: /* --tftp-blksize */
1783       err = str2unum(&config->tftp_blksize, nextarg);
1784       break;
1785     case C_MAIL_FROM: /* --mail-from */
1786       err = getstr(&config->mail_from, nextarg, DENY_BLANK);
1787       break;
1788     case C_MAIL_RCPT: /* --mail-rcpt */
1789       /* append receiver to a list */
1790       err = add2list(&config->mail_rcpt, nextarg);
1791       break;
1792     case C_FTP_PRET: /* --ftp-pret */
1793       config->ftp_pret = toggle;
1794       break;
1795     case C_PROTO: /* --proto */
1796       config->proto_present = TRUE;
1797       err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
1798       break;
1799     case C_PROTO_REDIR: /* --proto-redir */
1800       config->proto_redir_present = TRUE;
1801       if(proto2num(config, redir_protos, &config->proto_redir_str,
1802                    nextarg))
1803         err = PARAM_BAD_USE;
1804       break;
1805     case C_RESOLVE: /* --resolve */
1806       err = add2list(&config->resolve, nextarg);
1807       break;
1808     case C_DELEGATION: /* --delegation */
1809       config->gssapi_delegation = delegation(config, nextarg);
1810       break;
1811     case C_MAIL_AUTH: /* --mail-auth */
1812       err = getstr(&config->mail_auth, nextarg, DENY_BLANK);
1813       break;
1814     case C_METALINK: /* --metalink */
1815       errorf(global, "--metalink is disabled");
1816       err = PARAM_BAD_USE;
1817       break;
1818     case C_SASL_AUTHZID: /* --sasl-authzid */
1819       err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK);
1820       break;
1821     case C_SASL_IR: /* --sasl-ir */
1822       config->sasl_ir = toggle;
1823       break;
1824     case C_TEST_EVENT: /* --test-event */
1825 #ifdef CURLDEBUG
1826       global->test_event_based = toggle;
1827 #else
1828       warnf(global, "--test-event is ignored unless a debug build");
1829 #endif
1830       break;
1831     case C_UNIX_SOCKET: /* --unix-socket */
1832       config->abstract_unix_socket = FALSE;
1833       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
1834       break;
1835     case C_PATH_AS_IS: /* --path-as-is */
1836       config->path_as_is = toggle;
1837       break;
1838     case C_PROXY_SERVICE_NAME: /* --proxy-service-name */
1839       err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK);
1840       break;
1841     case C_SERVICE_NAME: /* --service-name */
1842       err = getstr(&config->service_name, nextarg, DENY_BLANK);
1843       break;
1844     case C_PROTO_DEFAULT: /* --proto-default */
1845       err = getstr(&config->proto_default, nextarg, DENY_BLANK);
1846       if(!err)
1847         err = check_protocol(config->proto_default);
1848       break;
1849     case C_EXPECT100_TIMEOUT: /* --expect100-timeout */
1850       err = secs2ms(&config->expect100timeout_ms, nextarg);
1851       break;
1852     case C_TFTP_NO_OPTIONS: /* --tftp-no-options */
1853       config->tftp_no_options = toggle;
1854       break;
1855     case C_CONNECT_TO: /* --connect-to */
1856       err = add2list(&config->connect_to, nextarg);
1857       break;
1858     case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */
1859       config->abstract_unix_socket = TRUE;
1860       err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
1861       break;
1862     case C_TLS_MAX: /* --tls-max */
1863       err = str2tls_max(&config->ssl_version_max, nextarg);
1864       break;
1865     case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */
1866       config->suppress_connect_headers = toggle;
1867       break;
1868     case C_COMPRESSED_SSH: /* --compressed-ssh */
1869       config->ssh_compression = toggle;
1870       break;
1871     case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
1872       err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
1873       /* 0 is a valid value for this timeout */
1874       break;
1875     case C_TRACE_IDS: /* --trace-ids */
1876       global->traceids = toggle;
1877       break;
1878     case C_TRACE_CONFIG: /* --trace-config */
1879       if(set_trace_config(global, nextarg))
1880         err = PARAM_NO_MEM;
1881       break;
1882     case C_PROGRESS_METER: /* --progress-meter */
1883       global->noprogress = !toggle;
1884       break;
1885     case C_PROGRESS_BAR: /* --progress-bar */
1886       global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
1887       break;
1888     case C_VARIABLE: /* --Variable */
1889       err = setvariable(global, nextarg);
1890       break;
1891     case C_NEXT: /* --next */
1892       err = PARAM_NEXT_OPERATION;
1893       break;
1894     case C_HTTP1_0: /* --http1.0 */
1895       /* HTTP version 1.0 */
1896       sethttpver(global, config, CURL_HTTP_VERSION_1_0);
1897       break;
1898     case C_HTTP1_1: /* --http1.1 */
1899       /* HTTP version 1.1 */
1900       sethttpver(global, config, CURL_HTTP_VERSION_1_1);
1901       break;
1902     case C_HTTP2: /* --http2 */
1903       /* HTTP version 2.0 */
1904       if(!feature_http2)
1905         return PARAM_LIBCURL_DOESNT_SUPPORT;
1906       sethttpver(global, config, CURL_HTTP_VERSION_2_0);
1907       break;
1908     case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */
1909       /* HTTP version 2.0 over clean TCP */
1910       if(!feature_http2)
1911         return PARAM_LIBCURL_DOESNT_SUPPORT;
1912       sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
1913       break;
1914     case C_HTTP3: /* --http3: */
1915       /* Try HTTP/3, allow fallback */
1916       if(!feature_http3)
1917         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1918       else
1919         sethttpver(global, config, CURL_HTTP_VERSION_3);
1920       break;
1921     case C_HTTP3_ONLY: /* --http3-only */
1922       /* Try HTTP/3 without fallback */
1923       if(!feature_http3)
1924         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1925       else
1926         sethttpver(global, config, CURL_HTTP_VERSION_3ONLY);
1927       break;
1928     case C_HTTP0_9: /* --http0.9 */
1929       /* Allow HTTP/0.9 responses! */
1930       config->http09_allowed = toggle;
1931       break;
1932     case C_PROXY_HTTP2: /* --proxy-http2 */
1933       if(!feature_httpsproxy || !feature_http2)
1934         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1935       else
1936         config->proxyver = CURLPROXY_HTTPS2;
1937       break;
1938     case C_TLSV1: /* --tlsv1 */
1939       config->ssl_version = CURL_SSLVERSION_TLSv1;
1940       break;
1941     case C_TLSV1_0: /* --tlsv1.0 */
1942       config->ssl_version = CURL_SSLVERSION_TLSv1_0;
1943       break;
1944     case C_TLSV1_1: /* --tlsv1.1 */
1945       config->ssl_version = CURL_SSLVERSION_TLSv1_1;
1946       break;
1947     case C_TLSV1_2: /* --tlsv1.2 */
1948       config->ssl_version = CURL_SSLVERSION_TLSv1_2;
1949       break;
1950     case C_TLSV1_3: /* --tlsv1.3 */
1951       config->ssl_version = CURL_SSLVERSION_TLSv1_3;
1952       break;
1953     case C_TLS13_CIPHERS: /* --tls13-ciphers */
1954       err = getstr(&config->cipher13_list, nextarg, DENY_BLANK);
1955       break;
1956     case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */
1957       err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK);
1958       break;
1959     case C_SSLV2: /* --sslv2 */
1960       warnf(global, "Ignores instruction to use SSLv2");
1961       break;
1962     case C_SSLV3: /* --sslv3 */
1963       warnf(global, "Ignores instruction to use SSLv3");
1964       break;
1965     case C_IPV4: /* --ipv4 */
1966       config->ip_version = CURL_IPRESOLVE_V4;
1967       break;
1968     case C_IPV6: /* --ipv6 */
1969       config->ip_version = CURL_IPRESOLVE_V6;
1970       break;
1971     case C_APPEND: /* --append */
1972       /* This makes the FTP sessions use APPE instead of STOR */
1973       config->ftp_append = toggle;
1974       break;
1975     case C_USER_AGENT: /* --user-agent */
1976       err = getstr(&config->useragent, nextarg, ALLOW_BLANK);
1977       break;
1978     case C_ALT_SVC: /* --alt-svc */
1979       if(!feature_altsvc)
1980         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1981       else
1982         err = getstr(&config->altsvc, nextarg, ALLOW_BLANK);
1983       break;
1984     case C_HSTS: /* --hsts */
1985       if(!feature_hsts)
1986         err = PARAM_LIBCURL_DOESNT_SUPPORT;
1987       else
1988         err = getstr(&config->hsts, nextarg, ALLOW_BLANK);
1989       break;
1990     case C_COOKIE: /* --cookie */
1991       if(strchr(nextarg, '=')) {
1992         /* A cookie string must have a =-letter */
1993         err = add2list(&config->cookies, nextarg);
1994         break;
1995       }
1996       else {
1997         /* We have a cookie file to read from! */
1998         err = add2list(&config->cookiefiles, nextarg);
1999       }
2000       break;
2001     case C_USE_ASCII: /* --use-ascii */
2002       config->use_ascii = toggle;
2003       break;
2004     case C_COOKIE_JAR: /* --cookie-jar */
2005       err = getstr(&config->cookiejar, nextarg, DENY_BLANK);
2006       break;
2007     case C_CONTINUE_AT: /* --continue-at */
2008       /* This makes us continue an ftp transfer at given position */
2009       if(strcmp(nextarg, "-")) {
2010         err = str2offset(&config->resume_from, nextarg);
2011         config->resume_from_current = FALSE;
2012       }
2013       else {
2014         config->resume_from_current = TRUE;
2015         config->resume_from = 0;
2016       }
2017       config->use_resume = TRUE;
2018       break;
2019     case C_DATA: /* --data */
2020     case C_DATA_ASCII:  /* --data-ascii */
2021     case C_DATA_BINARY:  /* --data-binary */
2022     case C_DATA_URLENCODE:  /* --data-urlencode */
2023     case C_JSON:  /* --json */
2024     case C_DATA_RAW:  /* --data-raw */
2025       err = set_data(cmd, nextarg, global, config);
2026       break;
2027     case C_URL_QUERY:  /* --url-query */
2028       err = url_query(nextarg, global, config);
2029       break;
2030     case C_DUMP_HEADER: /* --dump-header */
2031       err = getstr(&config->headerfile, nextarg, DENY_BLANK);
2032       break;
2033     case C_REFERER: { /* --referer */
2034       char *ptr = strstr(nextarg, ";auto");
2035       if(ptr) {
2036         /* Automatic referer requested, this may be combined with a
2037            set initial one */
2038         config->autoreferer = TRUE;
2039         *ptr = 0; /* null-terminate here */
2040       }
2041       else
2042         config->autoreferer = FALSE;
2043       ptr = *nextarg ? nextarg : NULL;
2044       err = getstr(&config->referer, ptr, ALLOW_BLANK);
2045     }
2046       break;
2047     case C_CERT: /* --cert */
2048       cleanarg(clearthis);
2049       GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
2050       break;
2051     case C_CACERT: /* --cacert */
2052       err = getstr(&config->cacert, nextarg, DENY_BLANK);
2053       break;
2054     case C_CA_NATIVE: /* --ca-native */
2055       config->native_ca_store = toggle;
2056       break;
2057     case C_PROXY_CA_NATIVE: /* --proxy-ca-native */
2058       config->proxy_native_ca_store = toggle;
2059       break;
2060     case C_CERT_TYPE: /* --cert-type */
2061       err = getstr(&config->cert_type, nextarg, DENY_BLANK);
2062       break;
2063     case C_KEY: /* --key */
2064       err = getstr(&config->key, nextarg, DENY_BLANK);
2065       break;
2066     case C_KEY_TYPE: /* --key-type */
2067       err = getstr(&config->key_type, nextarg, DENY_BLANK);
2068       break;
2069     case C_PASS: /* --pass */
2070       err = getstr(&config->key_passwd, nextarg, DENY_BLANK);
2071       cleanarg(clearthis);
2072       break;
2073     case C_ENGINE: /* --engine */
2074       err = getstr(&config->engine, nextarg, DENY_BLANK);
2075       if(!err &&
2076          config->engine && !strcmp(config->engine, "list")) {
2077         err = PARAM_ENGINES_REQUESTED;
2078       }
2079       break;
2080     case C_CAPATH: /* --capath */
2081       err = getstr(&config->capath, nextarg, DENY_BLANK);
2082       break;
2083     case C_PUBKEY: /* --pubkey */
2084       err = getstr(&config->pubkey, nextarg, DENY_BLANK);
2085       break;
2086     case C_HOSTPUBMD5: /* --hostpubmd5 */
2087       err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK);
2088       if(!err) {
2089         if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
2090           err = PARAM_BAD_USE;
2091       }
2092       break;
2093     case C_HOSTPUBSHA256: /* --hostpubsha256 */
2094       err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
2095       break;
2096     case C_CRLFILE: /* --crlfile */
2097       err = getstr(&config->crlfile, nextarg, DENY_BLANK);
2098       break;
2099     case C_TLSUSER: /* --tlsuser */
2100       if(!feature_tls_srp)
2101         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2102       else
2103         err = getstr(&config->tls_username, nextarg, DENY_BLANK);
2104       cleanarg(clearthis);
2105       break;
2106     case C_TLSPASSWORD: /* --tlspassword */
2107       if(!feature_tls_srp)
2108         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2109       else
2110         err = getstr(&config->tls_password, nextarg, ALLOW_BLANK);
2111       cleanarg(clearthis);
2112       break;
2113     case C_TLSAUTHTYPE: /* --tlsauthtype */
2114       if(!feature_tls_srp)
2115         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2116       else {
2117         err = getstr(&config->tls_authtype, nextarg, DENY_BLANK);
2118         if(!err && strcmp(config->tls_authtype, "SRP"))
2119           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2120       }
2121       break;
2122     case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */
2123       if(feature_ssl)
2124         config->ssl_allow_beast = toggle;
2125       break;
2126     case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */
2127       if(feature_ssl)
2128         config->ssl_auto_client_cert = toggle;
2129       break;
2130     case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */
2131       if(feature_ssl)
2132         config->proxy_ssl_auto_client_cert = toggle;
2133       break;
2134     case C_PINNEDPUBKEY: /* --pinnedpubkey */
2135       err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK);
2136       break;
2137     case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */
2138       err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK);
2139       break;
2140     case C_CERT_STATUS: /* --cert-status */
2141       config->verifystatus = TRUE;
2142       break;
2143     case C_DOH_CERT_STATUS: /* --doh-cert-status */
2144       config->doh_verifystatus = TRUE;
2145       break;
2146     case C_FALSE_START: /* --false-start */
2147       config->falsestart = TRUE;
2148       break;
2149     case C_SSL_NO_REVOKE: /* --ssl-no-revoke */
2150       if(feature_ssl)
2151         config->ssl_no_revoke = TRUE;
2152       break;
2153     case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */
2154       if(feature_ssl)
2155         config->ssl_revoke_best_effort = TRUE;
2156       break;
2157     case C_TCP_FASTOPEN: /* --tcp-fastopen */
2158       config->tcp_fastopen = TRUE;
2159       break;
2160     case C_PROXY_TLSUSER: /* --proxy-tlsuser */
2161       cleanarg(clearthis);
2162       if(!feature_tls_srp)
2163         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2164       else
2165         err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK);
2166       break;
2167     case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */
2168       cleanarg(clearthis);
2169       if(!feature_tls_srp)
2170         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2171       else
2172         err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK);
2173       break;
2174     case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */
2175       if(!feature_tls_srp)
2176         err = PARAM_LIBCURL_DOESNT_SUPPORT;
2177       else {
2178         err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK);
2179         if(!err && strcmp(config->proxy_tls_authtype, "SRP"))
2180           err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2181       }
2182       break;
2183     case C_PROXY_CERT: /* --proxy-cert */
2184       cleanarg(clearthis);
2185       GetFileAndPassword(nextarg, &config->proxy_cert,
2186                          &config->proxy_key_passwd);
2187       break;
2188     case C_PROXY_CERT_TYPE: /* --proxy-cert-type */
2189       err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK);
2190       break;
2191     case C_PROXY_KEY: /* --proxy-key */
2192       err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK);
2193       break;
2194     case C_PROXY_KEY_TYPE: /* --proxy-key-type */
2195       err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK);
2196       break;
2197     case C_PROXY_PASS: /* --proxy-pass */
2198       err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK);
2199       cleanarg(clearthis);
2200       break;
2201     case C_PROXY_CIPHERS: /* --proxy-ciphers */
2202       err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK);
2203       break;
2204     case C_PROXY_CRLFILE: /* --proxy-crlfile */
2205       err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK);
2206       break;
2207     case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */
2208       if(feature_ssl)
2209         config->proxy_ssl_allow_beast = toggle;
2210       break;
2211     case C_LOGIN_OPTIONS: /* --login-options */
2212       err = getstr(&config->login_options, nextarg, ALLOW_BLANK);
2213       break;
2214     case C_PROXY_CACERT: /* --proxy-cacert */
2215       err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK);
2216       break;
2217     case C_PROXY_CAPATH: /* --proxy-capath */
2218       err = getstr(&config->proxy_capath, nextarg, DENY_BLANK);
2219       break;
2220     case C_PROXY_INSECURE: /* --proxy-insecure */
2221       config->proxy_insecure_ok = toggle;
2222       break;
2223     case C_PROXY_TLSV1: /* --proxy-tlsv1 */
2224       /* TLS version 1 for proxy */
2225       config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
2226       break;
2227     case C_SOCKS5_BASIC: /* --socks5-basic */
2228       if(toggle)
2229         config->socks5_auth |= CURLAUTH_BASIC;
2230       else
2231         config->socks5_auth &= ~CURLAUTH_BASIC;
2232       break;
2233     case C_SOCKS5_GSSAPI: /* --socks5-gssapi */
2234       if(toggle)
2235         config->socks5_auth |= CURLAUTH_GSSAPI;
2236       else
2237         config->socks5_auth &= ~CURLAUTH_GSSAPI;
2238       break;
2239     case C_ETAG_SAVE: /* --etag-save */
2240       err = getstr(&config->etag_save_file, nextarg, DENY_BLANK);
2241       break;
2242     case C_ETAG_COMPARE: /* --etag-compare */
2243       err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK);
2244       break;
2245     case C_CURVES: /* --curves */
2246       err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK);
2247       break;
2248     case C_FAIL_EARLY: /* --fail-early */
2249       global->fail_early = toggle;
2250       break;
2251     case C_STYLED_OUTPUT: /* --styled-output */
2252       global->styled_output = toggle;
2253       break;
2254     case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */
2255       config->mail_rcpt_allowfails = toggle;
2256       break;
2257     case C_FAIL_WITH_BODY: /* --fail-with-body */
2258       config->failwithbody = toggle;
2259       if(config->failonerror && config->failwithbody) {
2260         errorf(config->global, "You must select either --fail or "
2261                "--fail-with-body, not both.");
2262         err = PARAM_BAD_USE;
2263       }
2264       break;
2265     case C_REMOVE_ON_ERROR: /* --remove-on-error */
2266       config->rm_partial = toggle;
2267       break;
2268     case C_FAIL: /* --fail */
2269       config->failonerror = toggle;
2270       if(config->failonerror && config->failwithbody) {
2271         errorf(config->global, "You must select either --fail or "
2272                "--fail-with-body, not both.");
2273         err = PARAM_BAD_USE;
2274       }
2275       break;
2276     case C_FORM: /* --form */
2277     case C_FORM_STRING: /* --form-string */
2278       /* "form data" simulation, this is a little advanced so lets do our best
2279          to sort this out slowly and carefully */
2280       if(formparse(config,
2281                    nextarg,
2282                    &config->mimeroot,
2283                    &config->mimecurrent,
2284                    (cmd == C_FORM_STRING)?TRUE:FALSE)) /* literal string */
2285         err = PARAM_BAD_USE;
2286       else if(SetHTTPrequest(config, HTTPREQ_MIMEPOST, &config->httpreq))
2287         err = PARAM_BAD_USE;
2288       break;
2289     case C_GLOBOFF: /* --globoff */
2290       config->globoff = toggle;
2291       break;
2292     case C_GET: /* --get */
2293       config->use_httpget = toggle;
2294       break;
2295     case C_REQUEST_TARGET: /* --request-target */
2296       err = getstr(&config->request_target, nextarg, DENY_BLANK);
2297       break;
2298     case C_HELP: /* --help */
2299       if(toggle) {
2300         if(*nextarg) {
2301           global->help_category = strdup(nextarg);
2302           if(!global->help_category) {
2303             err = PARAM_NO_MEM;
2304             break;
2305           }
2306         }
2307         err = PARAM_HELP_REQUESTED;
2308       }
2309       /* we now actually support --no-help too! */
2310       break;
2311     case C_HEADER: /* --header */
2312     case C_PROXY_HEADER: /* --proxy-header */
2313       /* A custom header to append to a list */
2314       if(nextarg[0] == '@') {
2315         /* read many headers from a file or stdin */
2316         char *string;
2317         size_t len;
2318         bool use_stdin = !strcmp(&nextarg[1], "-");
2319         FILE *file = use_stdin?stdin:fopen(&nextarg[1], FOPEN_READTEXT);
2320         if(!file) {
2321           errorf(global, "Failed to open %s", &nextarg[1]);
2322           err = PARAM_READ_ERROR;
2323         }
2324         else {
2325           err = file2memory(&string, &len, file);
2326           if(!err && string) {
2327             /* Allow strtok() here since this isn't used threaded */
2328             /* !checksrc! disable BANNEDFUNC 2 */
2329             char *h = strtok(string, "\r\n");
2330             while(h) {
2331               if(cmd == C_PROXY_HEADER) /* --proxy-header */
2332                 err = add2list(&config->proxyheaders, h);
2333               else
2334                 err = add2list(&config->headers, h);
2335               if(err)
2336                 break;
2337               h = strtok(NULL, "\r\n");
2338             }
2339             free(string);
2340           }
2341           if(!use_stdin)
2342             fclose(file);
2343         }
2344       }
2345       else {
2346         if(cmd == C_PROXY_HEADER) /* --proxy-header */
2347           err = add2list(&config->proxyheaders, nextarg);
2348         else
2349           err = add2list(&config->headers, nextarg);
2350       }
2351       break;
2352     case C_INCLUDE: /* --include */
2353       config->show_headers = toggle; /* show the headers as well in the
2354                                         general output stream */
2355       break;
2356     case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */
2357       config->cookiesession = toggle;
2358       break;
2359     case C_HEAD: /* --head */
2360       config->no_body = toggle;
2361       config->show_headers = toggle;
2362       if(SetHTTPrequest(config,
2363                         (config->no_body)?HTTPREQ_HEAD:HTTPREQ_GET,
2364                         &config->httpreq))
2365         err = PARAM_BAD_USE;
2366       break;
2367     case C_REMOTE_HEADER_NAME: /* --remote-header-name */
2368       config->content_disposition = toggle;
2369       break;
2370     case C_INSECURE: /* --insecure */
2371       config->insecure_ok = toggle;
2372       break;
2373     case C_DOH_INSECURE: /* --doh-insecure */
2374       config->doh_insecure_ok = toggle;
2375       break;
2376     case C_CONFIG: /* --config */
2377       if(parseconfig(nextarg, global)) {
2378         errorf(global, "cannot read config from '%s'", nextarg);
2379         err = PARAM_READ_ERROR;
2380       }
2381       break;
2382     case C_LIST_ONLY: /* --list-only */
2383       config->dirlistonly = toggle; /* only list the names of the FTP dir */
2384       break;
2385     case C_LOCATION_TRUSTED: /* --location-trusted */
2386       /* Continue to send authentication (user+password) when following
2387        * locations, even when hostname changed */
2388       config->unrestricted_auth = toggle;
2389       FALLTHROUGH();
2390     case C_LOCATION: /* --location */
2391       config->followlocation = toggle; /* Follow Location: HTTP headers */
2392       break;
2393     case C_MAX_TIME: /* --max-time */
2394       /* specified max time */
2395       err = secs2ms(&config->timeout_ms, nextarg);
2396       break;
2397     case C_MANUAL: /* --manual */
2398       if(toggle) { /* --no-manual shows no manual... */
2399 #ifndef USE_MANUAL
2400         warnf(global,
2401               "built-in manual was disabled at build-time");
2402 #endif
2403         err = PARAM_MANUAL_REQUESTED;
2404       }
2405       break;
2406     case C_NETRC_OPTIONAL: /* --netrc-optional */
2407       config->netrc_opt = toggle;
2408       break;
2409     case C_NETRC_FILE: /* --netrc-file */
2410       err = getstr(&config->netrc_file, nextarg, DENY_BLANK);
2411       break;
2412     case C_NETRC: /* --netrc */
2413       /* pick info from .netrc, if this is used for http, curl will
2414          automatically enforce user+password with the request */
2415       config->netrc = toggle;
2416       break;
2417     case C_BUFFER: /* --buffer */
2418       /* disable the output I/O buffering. note that the option is called
2419          --buffer but is mostly used in the negative form: --no-buffer */
2420       config->nobuffer = longopt ? !toggle : TRUE;
2421       break;
2422     case C_REMOTE_NAME_ALL: /* --remote-name-all */
2423       config->default_node_flags = toggle?GETOUT_USEREMOTE:0;
2424       break;
2425     case C_OUTPUT_DIR: /* --output-dir */
2426       err = getstr(&config->output_dir, nextarg, DENY_BLANK);
2427       break;
2428     case C_CLOBBER: /* --clobber */
2429       config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER;
2430       break;
2431     case C_OUTPUT: /* --output */
2432     case C_REMOTE_NAME: /* --remote-name */
2433       /* output file */
2434       if(!config->url_out)
2435         config->url_out = config->url_list;
2436       if(config->url_out) {
2437         /* there's a node here, if it already is filled-in continue to find
2438            an "empty" node */
2439         while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
2440           config->url_out = config->url_out->next;
2441       }
2442 
2443       /* now there might or might not be an available node to fill in! */
2444 
2445       if(config->url_out)
2446         /* existing node */
2447         url = config->url_out;
2448       else {
2449         if(!toggle && !config->default_node_flags)
2450           break;
2451         /* there was no free node, create one! */
2452         config->url_out = url = new_getout(config);
2453       }
2454 
2455       if(!url) {
2456         err = PARAM_NO_MEM;
2457         break;
2458       }
2459 
2460       /* fill in the outfile */
2461       if('o' == letter) {
2462         err = getstr(&url->outfile, nextarg, DENY_BLANK);
2463         url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2464       }
2465       else {
2466         url->outfile = NULL; /* leave it */
2467         if(toggle)
2468           url->flags |= GETOUT_USEREMOTE;  /* switch on */
2469         else
2470           url->flags &= ~GETOUT_USEREMOTE; /* switch off */
2471       }
2472       url->flags |= GETOUT_OUTFILE;
2473       break;
2474     case C_FTP_PORT: /* --ftp-port */
2475       /* This makes the FTP sessions use PORT instead of PASV */
2476       /* use <eth0> or <192.168.10.10> style addresses. Anything except
2477          this will make us try to get the "default" address.
2478          NOTE: this is a changed behavior since the released 4.1!
2479       */
2480       err = getstr(&config->ftpport, nextarg, DENY_BLANK);
2481       break;
2482     case C_PROXYTUNNEL: /* --proxytunnel */
2483       /* proxy tunnel for non-http protocols */
2484       config->proxytunnel = toggle;
2485       break;
2486 
2487     case C_DISABLE: /* --disable */
2488       /* if used first, already taken care of, we do it like this so we don't
2489          cause an error! */
2490       break;
2491     case C_QUOTE: /* --quote */
2492       /* QUOTE command to send to FTP server */
2493       switch(nextarg[0]) {
2494       case '-':
2495         /* prefixed with a dash makes it a POST TRANSFER one */
2496         nextarg++;
2497         err = add2list(&config->postquote, nextarg);
2498         break;
2499       case '+':
2500         /* prefixed with a plus makes it a just-before-transfer one */
2501         nextarg++;
2502         err = add2list(&config->prequote, nextarg);
2503         break;
2504       default:
2505         err = add2list(&config->quote, nextarg);
2506         break;
2507       }
2508       break;
2509     case C_RANGE: /* --range */
2510       /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
2511          (and won't actually be range by definition). The man page previously
2512          claimed that to be a good way, why this code is added to work-around
2513          it. */
2514       if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
2515         char buffer[32];
2516         if(curlx_strtoofft(nextarg, NULL, 10, &value)) {
2517           warnf(global, "unsupported range point");
2518           err = PARAM_BAD_USE;
2519         }
2520         else {
2521           warnf(global,
2522                 "A specified range MUST include at least one dash (-). "
2523                 "Appending one for you");
2524           msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
2525                     value);
2526           Curl_safefree(config->range);
2527           config->range = strdup(buffer);
2528           if(!config->range)
2529             err = PARAM_NO_MEM;
2530         }
2531       }
2532       else {
2533         /* byte range requested */
2534         const char *tmp_range = nextarg;
2535         while(*tmp_range) {
2536           if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
2537             warnf(global, "Invalid character is found in given range. "
2538                   "A specified range MUST have only digits in "
2539                   "\'start\'-\'stop\'. The server's response to this "
2540                   "request is uncertain.");
2541             break;
2542           }
2543           tmp_range++;
2544         }
2545         err = getstr(&config->range, nextarg, DENY_BLANK);
2546       }
2547       break;
2548     case C_REMOTE_TIME: /* --remote-time */
2549       /* use remote file's time */
2550       config->remote_time = toggle;
2551       break;
2552     case C_SILENT: /* --silent */
2553       global->silent = toggle;
2554       break;
2555     case C_SHOW_ERROR: /* --show-error */
2556       global->showerror = toggle;
2557       break;
2558     case C_TELNET_OPTION: /* --telnet-option */
2559       /* Telnet options */
2560       err = add2list(&config->telnet_options, nextarg);
2561       break;
2562     case C_UPLOAD_FILE: /* --upload-file */
2563       /* we are uploading */
2564       if(!config->url_ul)
2565         config->url_ul = config->url_list;
2566       if(config->url_ul) {
2567         /* there's a node here, if it already is filled-in continue to find
2568            an "empty" node */
2569         while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
2570           config->url_ul = config->url_ul->next;
2571       }
2572 
2573       /* now there might or might not be an available node to fill in! */
2574 
2575       if(config->url_ul)
2576         /* existing node */
2577         url = config->url_ul;
2578       else
2579         /* there was no free node, create one! */
2580         config->url_ul = url = new_getout(config);
2581 
2582       if(!url) {
2583         err = PARAM_NO_MEM;
2584         break;
2585       }
2586 
2587       url->flags |= GETOUT_UPLOAD; /* mark -T used */
2588       if(!*nextarg)
2589         url->flags |= GETOUT_NOUPLOAD;
2590       else {
2591         /* "-" equals stdin, but keep the string around for now */
2592         err = getstr(&url->infile, nextarg, DENY_BLANK);
2593       }
2594       break;
2595     case C_USER: /* --user */
2596       /* user:password  */
2597       err = getstr(&config->userpwd, nextarg, ALLOW_BLANK);
2598       cleanarg(clearthis);
2599       break;
2600     case C_PROXY_USER: /* --proxy-user */
2601       /* Proxy user:password  */
2602       err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK);
2603       cleanarg(clearthis);
2604       break;
2605     case C_VERBOSE: /* --verbose */
2606       if(toggle) {
2607         /* the '%' thing here will cause the trace get sent to stderr */
2608         Curl_safefree(global->trace_dump);
2609         global->trace_dump = strdup("%");
2610         if(!global->trace_dump)
2611           err = PARAM_NO_MEM;
2612         else {
2613           if(global->tracetype && (global->tracetype != TRACE_PLAIN))
2614             warnf(global,
2615                   "-v, --verbose overrides an earlier trace/verbose option");
2616           global->tracetype = TRACE_PLAIN;
2617         }
2618       }
2619       else
2620         /* verbose is disabled here */
2621         global->tracetype = TRACE_NONE;
2622       break;
2623     case C_VERSION: /* --version */
2624       if(toggle)    /* --no-version yields no output! */
2625         err = PARAM_VERSION_INFO_REQUESTED;
2626       break;
2627     case C_WRITE_OUT: /* --write-out */
2628       /* get the output string */
2629       if('@' == *nextarg) {
2630         /* the data begins with a '@' letter, it means that a file name
2631            or - (stdin) follows */
2632         FILE *file;
2633         const char *fname;
2634         nextarg++; /* pass the @ */
2635         if(!strcmp("-", nextarg)) {
2636           fname = "<stdin>";
2637           file = stdin;
2638         }
2639         else {
2640           fname = nextarg;
2641           file = fopen(fname, FOPEN_READTEXT);
2642           if(!file) {
2643             errorf(global, "Failed to open %s", fname);
2644             err = PARAM_READ_ERROR;
2645             break;
2646           }
2647         }
2648         Curl_safefree(config->writeout);
2649         err = file2string(&config->writeout, file);
2650         if(file && (file != stdin))
2651           fclose(file);
2652         if(err)
2653           break;
2654         if(!config->writeout)
2655           warnf(global, "Failed to read %s", fname);
2656       }
2657       else
2658         err = getstr(&config->writeout, nextarg, DENY_BLANK);
2659       break;
2660     case C_PREPROXY: /* --preproxy */
2661       err = getstr(&config->preproxy, nextarg, DENY_BLANK);
2662       break;
2663     case C_PROXY: /* --proxy */
2664       /* --proxy */
2665       err = getstr(&config->proxy, nextarg, ALLOW_BLANK);
2666       if(config->proxyver != CURLPROXY_HTTPS2)
2667         config->proxyver = CURLPROXY_HTTP;
2668       break;
2669     case C_REQUEST: /* --request */
2670       /* set custom request */
2671       err = getstr(&config->customrequest, nextarg, DENY_BLANK);
2672       break;
2673     case C_SPEED_TIME: /* --speed-time */
2674       /* low speed time */
2675       err = str2unum(&config->low_speed_time, nextarg);
2676       if(!err && !config->low_speed_limit)
2677         config->low_speed_limit = 1;
2678       break;
2679     case C_SPEED_LIMIT: /* --speed-limit */
2680       /* low speed limit */
2681       err = str2unum(&config->low_speed_limit, nextarg);
2682       if(!err && !config->low_speed_time)
2683         config->low_speed_time = 30;
2684       break;
2685     case C_PARALLEL: /* --parallel */
2686       global->parallel = toggle;
2687       break;
2688     case C_PARALLEL_MAX: {  /* --parallel-max */
2689       long val;
2690       err = str2unum(&val, nextarg);
2691       if(err)
2692         break;
2693       if(val > MAX_PARALLEL)
2694         global->parallel_max = MAX_PARALLEL;
2695       else if(val < 1)
2696         global->parallel_max = PARALLEL_DEFAULT;
2697       else
2698         global->parallel_max = (unsigned short)val;
2699       break;
2700     }
2701     case C_PARALLEL_IMMEDIATE:   /* --parallel-immediate */
2702       global->parallel_connect = toggle;
2703       break;
2704     case C_TIME_COND: /* --time-cond */
2705       switch(*nextarg) {
2706       case '+':
2707         nextarg++;
2708         FALLTHROUGH();
2709       default:
2710         /* If-Modified-Since: (section 14.28 in RFC2068) */
2711         config->timecond = CURL_TIMECOND_IFMODSINCE;
2712         break;
2713       case '-':
2714         /* If-Unmodified-Since:  (section 14.24 in RFC2068) */
2715         config->timecond = CURL_TIMECOND_IFUNMODSINCE;
2716         nextarg++;
2717         break;
2718       case '=':
2719         /* Last-Modified:  (section 14.29 in RFC2068) */
2720         config->timecond = CURL_TIMECOND_LASTMOD;
2721         nextarg++;
2722         break;
2723       }
2724       now = time(NULL);
2725       config->condtime = (curl_off_t)curl_getdate(nextarg, &now);
2726       if(-1 == config->condtime) {
2727         /* now let's see if it is a file name to get the time from instead! */
2728         rc = getfiletime(nextarg, global, &value);
2729         if(!rc)
2730           /* pull the time out from the file */
2731           config->condtime = value;
2732         else {
2733           /* failed, remove time condition */
2734           config->timecond = CURL_TIMECOND_NONE;
2735           warnf(global,
2736                 "Illegal date format for -z, --time-cond (and not "
2737                 "a file name). Disabling time condition. "
2738                 "See curl_getdate(3) for valid date syntax.");
2739         }
2740       }
2741       break;
2742     default: /* unknown flag */
2743       err = PARAM_OPTION_UNKNOWN;
2744       break;
2745     }
2746     a = NULL;
2747 
2748   } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
2749 
2750 error:
2751   if(nextalloc)
2752     free(nextarg);
2753   return err;
2754 }
2755 
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2756 ParameterError parse_args(struct GlobalConfig *global, int argc,
2757                           argv_item_t argv[])
2758 {
2759   int i;
2760   bool stillflags;
2761   char *orig_opt = NULL;
2762   ParameterError result = PARAM_OK;
2763   struct OperationConfig *config = global->first;
2764 
2765   for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2766     orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2767     if(!orig_opt)
2768       return PARAM_NO_MEM;
2769 
2770     if(stillflags && ('-' == orig_opt[0])) {
2771       bool passarg;
2772 
2773       if(!strcmp("--", orig_opt))
2774         /* This indicates the end of the flags and thus enables the
2775            following (URL) argument to start with -. */
2776         stillflags = FALSE;
2777       else {
2778         char *nextarg = NULL;
2779         if(i < (argc - 1)) {
2780           nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]);
2781           if(!nextarg) {
2782             curlx_unicodefree(orig_opt);
2783             return PARAM_NO_MEM;
2784           }
2785         }
2786 
2787         result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg,
2788                               global, config);
2789 
2790         curlx_unicodefree(nextarg);
2791         config = global->last;
2792         if(result == PARAM_NEXT_OPERATION) {
2793           /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2794              returned from this function */
2795           result = PARAM_OK;
2796 
2797           if(config->url_list && config->url_list->url) {
2798             /* Allocate the next config */
2799             config->next = malloc(sizeof(struct OperationConfig));
2800             if(config->next) {
2801               /* Initialise the newly created config */
2802               config_init(config->next);
2803 
2804               /* Set the global config pointer */
2805               config->next->global = global;
2806 
2807               /* Update the last config pointer */
2808               global->last = config->next;
2809 
2810               /* Move onto the new config */
2811               config->next->prev = config;
2812               config = config->next;
2813             }
2814             else
2815               result = PARAM_NO_MEM;
2816           }
2817           else {
2818             errorf(global, "missing URL before --next");
2819             result = PARAM_BAD_USE;
2820           }
2821         }
2822         else if(!result && passarg)
2823           i++; /* we're supposed to skip this */
2824       }
2825     }
2826     else {
2827       bool used;
2828 
2829       /* Just add the URL please */
2830       result = getparameter("--url", orig_opt, argv[i], &used, global, config);
2831     }
2832 
2833     if(!result)
2834       curlx_unicodefree(orig_opt);
2835   }
2836 
2837   if(!result && config->content_disposition) {
2838     if(config->show_headers)
2839       result = PARAM_CONTDISP_SHOW_HEADER;
2840     else if(config->resume_from_current)
2841       result = PARAM_CONTDISP_RESUME_FROM;
2842   }
2843 
2844   if(result && result != PARAM_HELP_REQUESTED &&
2845      result != PARAM_MANUAL_REQUESTED &&
2846      result != PARAM_VERSION_INFO_REQUESTED &&
2847      result != PARAM_ENGINES_REQUESTED) {
2848     const char *reason = param2text(result);
2849 
2850     if(orig_opt && strcmp(":", orig_opt))
2851       helpf(tool_stderr, "option %s: %s", orig_opt, reason);
2852     else
2853       helpf(tool_stderr, "%s", reason);
2854   }
2855 
2856   curlx_unicodefree(orig_opt);
2857   return result;
2858 }
2859