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