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