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