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