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-max", ARG_STRG, ' ', C_TLS_MAX},
320 {"tls13-ciphers", ARG_STRG, ' ', C_TLS13_CIPHERS},
321 {"tlsauthtype", ARG_STRG, ' ', C_TLSAUTHTYPE},
322 {"tlspassword", ARG_STRG, ' ', C_TLSPASSWORD},
323 {"tlsuser", ARG_STRG, ' ', C_TLSUSER},
324 {"tlsv1", ARG_NONE, '1', C_TLSV1},
325 {"tlsv1.0", ARG_NONE, ' ', C_TLSV1_0},
326 {"tlsv1.1", ARG_NONE, ' ', C_TLSV1_1},
327 {"tlsv1.2", ARG_NONE, ' ', C_TLSV1_2},
328 {"tlsv1.3", ARG_NONE, ' ', C_TLSV1_3},
329 {"tr-encoding", ARG_BOOL, ' ', C_TR_ENCODING},
330 {"trace", ARG_FILE, ' ', C_TRACE},
331 {"trace-ascii", ARG_FILE, ' ', C_TRACE_ASCII},
332 {"trace-config", ARG_STRG, ' ', C_TRACE_CONFIG},
333 {"trace-ids", ARG_BOOL, ' ', C_TRACE_IDS},
334 {"trace-time", ARG_BOOL, ' ', C_TRACE_TIME},
335 {"unix-socket", ARG_FILE, ' ', C_UNIX_SOCKET},
336 {"upload-file", ARG_FILE, 'T', C_UPLOAD_FILE},
337 {"url", ARG_STRG, ' ', C_URL},
338 {"url-query", ARG_STRG, ' ', C_URL_QUERY},
339 {"use-ascii", ARG_BOOL, 'B', C_USE_ASCII},
340 {"user", ARG_STRG, 'u', C_USER},
341 {"user-agent", ARG_STRG, 'A', C_USER_AGENT},
342 {"variable", ARG_STRG, ' ', C_VARIABLE},
343 {"verbose", ARG_BOOL, 'v', C_VERBOSE},
344 {"version", ARG_BOOL, 'V', C_VERSION},
345 {"vlan-priority", ARG_STRG, ' ', C_VLAN_PRIORITY},
346 #ifdef USE_WATT32
347 {"wdebug", ARG_BOOL, ' ', C_WDEBUG},
348 #endif
349 {"write-out", ARG_STRG, 'w', C_WRITE_OUT},
350 {"xattr", ARG_BOOL, ' ', C_XATTR},
351 };
352
353 /* Split the argument of -E to 'certname' and 'passphrase' separated by colon.
354 * We allow ':' and '\' to be escaped by '\' so that we can use certificate
355 * nicknames containing ':'. See <https://sourceforge.net/p/curl/bugs/1196/>
356 * for details. */
357 #ifndef UNITTESTS
358 static
359 #endif
parse_cert_parameter(const char * cert_parameter,char ** certname,char ** passphrase)360 void parse_cert_parameter(const char *cert_parameter,
361 char **certname,
362 char **passphrase)
363 {
364 size_t param_length = strlen(cert_parameter);
365 size_t span;
366 const char *param_place = NULL;
367 char *certname_place = NULL;
368 *certname = NULL;
369 *passphrase = NULL;
370
371 /* most trivial assumption: cert_parameter is empty */
372 if(param_length == 0)
373 return;
374
375 /* next less trivial: cert_parameter starts 'pkcs11:' and thus
376 * looks like a RFC7512 PKCS#11 URI which can be used as-is.
377 * Also if cert_parameter contains no colon nor backslash, this
378 * means no passphrase was given and no characters escaped */
379 if(curl_strnequal(cert_parameter, "pkcs11:", 7) ||
380 !strpbrk(cert_parameter, ":\\")) {
381 *certname = strdup(cert_parameter);
382 return;
383 }
384 /* deal with escaped chars; find unescaped colon if it exists */
385 certname_place = malloc(param_length + 1);
386 if(!certname_place)
387 return;
388
389 *certname = certname_place;
390 param_place = cert_parameter;
391 while(*param_place) {
392 span = strcspn(param_place, ":\\");
393 strncpy(certname_place, param_place, span);
394 param_place += span;
395 certname_place += span;
396 /* we just ate all the non-special chars. now we are on either a special
397 * char or the end of the string. */
398 switch(*param_place) {
399 case '\0':
400 break;
401 case '\\':
402 param_place++;
403 switch(*param_place) {
404 case '\0':
405 *certname_place++ = '\\';
406 break;
407 case '\\':
408 *certname_place++ = '\\';
409 param_place++;
410 break;
411 case ':':
412 *certname_place++ = ':';
413 param_place++;
414 break;
415 default:
416 *certname_place++ = '\\';
417 *certname_place++ = *param_place;
418 param_place++;
419 break;
420 }
421 break;
422 case ':':
423 /* Since we live in a world of weirdness and confusion, the Windows
424 dudes can use : when using drive letters and thus c:\file:password
425 needs to work. In order not to break compatibility, we still use : as
426 separator, but we try to detect when it is used for a filename! On
427 Windows. */
428 #ifdef _WIN32
429 if((param_place == &cert_parameter[1]) &&
430 (cert_parameter[2] == '\\' || cert_parameter[2] == '/') &&
431 (ISALPHA(cert_parameter[0])) ) {
432 /* colon in the second column, followed by a backslash, and the
433 first character is an alphabetic letter:
434
435 this is a drive letter colon */
436 *certname_place++ = ':';
437 param_place++;
438 break;
439 }
440 #endif
441 /* escaped colons and Windows drive letter colons were handled
442 * above; if we are still here, this is a separating colon */
443 param_place++;
444 if(*param_place) {
445 *passphrase = strdup(param_place);
446 }
447 goto done;
448 }
449 }
450 done:
451 *certname_place = '\0';
452 }
453
454 /* Replace (in-place) '%20' by '+' according to RFC1866 */
replace_url_encoded_space_by_plus(char * url)455 static size_t replace_url_encoded_space_by_plus(char *url)
456 {
457 size_t orig_len = strlen(url);
458 size_t orig_index = 0;
459 size_t new_index = 0;
460
461 while(orig_index < orig_len) {
462 if((url[orig_index] == '%') &&
463 (url[orig_index + 1] == '2') &&
464 (url[orig_index + 2] == '0')) {
465 url[new_index] = '+';
466 orig_index += 3;
467 }
468 else{
469 if(new_index != orig_index) {
470 url[new_index] = url[orig_index];
471 }
472 orig_index++;
473 }
474 new_index++;
475 }
476
477 url[new_index] = 0; /* terminate string */
478
479 return new_index; /* new size */
480 }
481
482 static void
GetFileAndPassword(char * nextarg,char ** file,char ** password)483 GetFileAndPassword(char *nextarg, char **file, char **password)
484 {
485 char *certname, *passphrase;
486 if(nextarg) {
487 parse_cert_parameter(nextarg, &certname, &passphrase);
488 Curl_safefree(*file);
489 *file = certname;
490 if(passphrase) {
491 Curl_safefree(*password);
492 *password = passphrase;
493 }
494 }
495 }
496
497 /* Get a size parameter for '--limit-rate' or '--max-filesize'.
498 * We support a 'G', 'M' or 'K' suffix too.
499 */
GetSizeParameter(struct GlobalConfig * global,const char * arg,const char * which,curl_off_t * value_out)500 static ParameterError GetSizeParameter(struct GlobalConfig *global,
501 const char *arg,
502 const char *which,
503 curl_off_t *value_out)
504 {
505 char *unit;
506 curl_off_t value;
507
508 if(curlx_strtoofft(arg, &unit, 10, &value)) {
509 warnf(global, "invalid number specified for %s", which);
510 return PARAM_BAD_USE;
511 }
512
513 if(!*unit)
514 unit = (char *)"b";
515 else if(strlen(unit) > 1)
516 unit = (char *)"w"; /* unsupported */
517
518 switch(*unit) {
519 case 'G':
520 case 'g':
521 if(value > (CURL_OFF_T_MAX / (1024*1024*1024)))
522 return PARAM_NUMBER_TOO_LARGE;
523 value *= 1024*1024*1024;
524 break;
525 case 'M':
526 case 'm':
527 if(value > (CURL_OFF_T_MAX / (1024*1024)))
528 return PARAM_NUMBER_TOO_LARGE;
529 value *= 1024*1024;
530 break;
531 case 'K':
532 case 'k':
533 if(value > (CURL_OFF_T_MAX / 1024))
534 return PARAM_NUMBER_TOO_LARGE;
535 value *= 1024;
536 break;
537 case 'b':
538 case 'B':
539 /* for plain bytes, leave as-is */
540 break;
541 default:
542 warnf(global, "unsupported %s unit. Use G, M, K or B", which);
543 return PARAM_BAD_USE;
544 }
545 *value_out = value;
546 return PARAM_OK;
547 }
548
549 #ifdef HAVE_WRITABLE_ARGV
cleanarg(argv_item_t str)550 static void cleanarg(argv_item_t str)
551 {
552 /* now that getstr has copied the contents of nextarg, wipe the next
553 * argument out so that the username:password is not displayed in the
554 * system process list */
555 if(str) {
556 size_t len = strlen(str);
557 memset(str, ' ', len);
558 }
559 }
560 #else
561 #define cleanarg(x)
562 #endif
563
564 /* the maximum size we allow the dynbuf generated string */
565 #define MAX_DATAURLENCODE (500*1024*1024)
566
567 /* --data-urlencode */
data_urlencode(struct GlobalConfig * global,char * nextarg,char ** postp,size_t * lenp)568 static ParameterError data_urlencode(struct GlobalConfig *global,
569 char *nextarg,
570 char **postp,
571 size_t *lenp)
572 {
573 /* [name]=[content], we encode the content part only
574 * [name]@[filename]
575 *
576 * Case 2: we first load the file using that name and then encode
577 * the content.
578 */
579 ParameterError err;
580 const char *p = strchr(nextarg, '=');
581 size_t nlen;
582 char is_file;
583 char *postdata = NULL;
584 size_t size = 0;
585 if(!p)
586 /* there was no '=' letter, check for a '@' instead */
587 p = strchr(nextarg, '@');
588 if(p) {
589 nlen = p - nextarg; /* length of the name part */
590 is_file = *p++; /* pass the separator */
591 }
592 else {
593 /* neither @ nor =, so no name and it is not a file */
594 nlen = 0;
595 is_file = 0;
596 p = nextarg;
597 }
598 if('@' == is_file) {
599 FILE *file;
600 /* a '@' letter, it means that a filename or - (stdin) follows */
601 if(!strcmp("-", p)) {
602 file = stdin;
603 set_binmode(stdin);
604 }
605 else {
606 file = fopen(p, "rb");
607 if(!file) {
608 errorf(global, "Failed to open %s", p);
609 return PARAM_READ_ERROR;
610 }
611 }
612
613 err = file2memory(&postdata, &size, file);
614
615 if(file && (file != stdin))
616 fclose(file);
617 if(err)
618 return err;
619 }
620 else {
621 err = getstr(&postdata, p, ALLOW_BLANK);
622 if(err)
623 goto error;
624 size = strlen(postdata);
625 }
626
627 if(!postdata) {
628 /* no data from the file, point to a zero byte string to make this
629 get sent as a POST anyway */
630 postdata = strdup("");
631 if(!postdata)
632 return PARAM_NO_MEM;
633 size = 0;
634 }
635 else {
636 char *enc = curl_easy_escape(NULL, postdata, (int)size);
637 Curl_safefree(postdata); /* no matter if it worked or not */
638 if(enc) {
639 char *n;
640 replace_url_encoded_space_by_plus(enc);
641 if(nlen > 0) { /* only append '=' if we have a name */
642 struct curlx_dynbuf dyn;
643 curlx_dyn_init(&dyn, MAX_DATAURLENCODE);
644 if(curlx_dyn_addn(&dyn, nextarg, nlen) ||
645 curlx_dyn_addn(&dyn, "=", 1) ||
646 curlx_dyn_add(&dyn, enc)) {
647 curl_free(enc);
648 return PARAM_NO_MEM;
649 }
650 curl_free(enc);
651 n = curlx_dyn_ptr(&dyn);
652 size = curlx_dyn_len(&dyn);
653 }
654 else {
655 n = enc;
656 size = strlen(n);
657 }
658 postdata = n;
659 }
660 else
661 return PARAM_NO_MEM;
662 }
663 *postp = postdata;
664 *lenp = size;
665 return PARAM_OK;
666 error:
667 return err;
668 }
669
sethttpver(struct GlobalConfig * global,struct OperationConfig * config,long httpversion)670 static void sethttpver(struct GlobalConfig *global,
671 struct OperationConfig *config,
672 long httpversion)
673 {
674 if(config->httpversion &&
675 (config->httpversion != httpversion))
676 warnf(global, "Overrides previous HTTP version option");
677
678 config->httpversion = httpversion;
679 }
680
set_trace_config(struct GlobalConfig * global,const char * config)681 static CURLcode set_trace_config(struct GlobalConfig *global,
682 const char *config)
683 {
684 CURLcode result = CURLE_OK;
685 char *token, *tmp, *name;
686 bool toggle;
687
688 tmp = strdup(config);
689 if(!tmp)
690 return CURLE_OUT_OF_MEMORY;
691
692 /* Allow strtok() here since this is not used threaded */
693 /* !checksrc! disable BANNEDFUNC 2 */
694 token = strtok(tmp, ", ");
695 while(token) {
696 switch(*token) {
697 case '-':
698 toggle = FALSE;
699 name = token + 1;
700 break;
701 case '+':
702 toggle = TRUE;
703 name = token + 1;
704 break;
705 default:
706 toggle = TRUE;
707 name = token;
708 break;
709 }
710
711 if(strcasecompare(name, "all")) {
712 global->traceids = toggle;
713 global->tracetime = toggle;
714 result = curl_global_trace(token);
715 if(result)
716 goto out;
717 }
718 else if(strcasecompare(name, "ids")) {
719 global->traceids = toggle;
720 }
721 else if(strcasecompare(name, "time")) {
722 global->tracetime = toggle;
723 }
724 else {
725 result = curl_global_trace(token);
726 if(result)
727 goto out;
728 }
729 token = strtok(NULL, ", ");
730 }
731 out:
732 free(tmp);
733 return result;
734 }
735
findarg(const void * a,const void * b)736 static int findarg(const void *a, const void *b)
737 {
738 const struct LongShort *aa = a;
739 const struct LongShort *bb = b;
740 return strcmp(aa->lname, bb->lname);
741 }
742
findshortopt(char letter)743 const struct LongShort *findshortopt(char letter)
744 {
745 static const struct LongShort *singles[128 - ' ']; /* ASCII => pointer */
746 static bool singles_done = FALSE;
747 if((letter >= 127) || (letter <= ' '))
748 return NULL;
749
750 if(!singles_done) {
751 unsigned int j;
752 for(j = 0; j < sizeof(aliases)/sizeof(aliases[0]); j++) {
753 if(aliases[j].letter != ' ') {
754 unsigned char l = (unsigned char)aliases[j].letter;
755 singles[l - ' '] = &aliases[j];
756 }
757 }
758 singles_done = TRUE;
759 }
760 return singles[letter - ' '];
761 }
762
763 struct TOSEntry {
764 const char *name;
765 unsigned char value;
766 };
767
768 static const struct TOSEntry tos_entries[] = {
769 {"AF11", 0x28},
770 {"AF12", 0x30},
771 {"AF13", 0x38},
772 {"AF21", 0x48},
773 {"AF22", 0x50},
774 {"AF23", 0x58},
775 {"AF31", 0x68},
776 {"AF32", 0x70},
777 {"AF33", 0x78},
778 {"AF41", 0x88},
779 {"AF42", 0x90},
780 {"AF43", 0x98},
781 {"CE", 0x03},
782 {"CS0", 0x00},
783 {"CS1", 0x20},
784 {"CS2", 0x40},
785 {"CS3", 0x60},
786 {"CS4", 0x80},
787 {"CS5", 0xa0},
788 {"CS6", 0xc0},
789 {"CS7", 0xe0},
790 {"ECT0", 0x02},
791 {"ECT1", 0x01},
792 {"EF", 0xb8},
793 {"LE", 0x04},
794 {"LOWCOST", 0x02},
795 {"LOWDELAY", 0x10},
796 {"MINCOST", 0x02},
797 {"RELIABILITY", 0x04},
798 {"THROUGHPUT", 0x08},
799 {"VOICE-ADMIT", 0xb0}
800 };
801
find_tos(const void * a,const void * b)802 static int find_tos(const void *a, const void *b)
803 {
804 const struct TOSEntry *aa = a;
805 const struct TOSEntry *bb = b;
806 return strcmp(aa->name, bb->name);
807 }
808
809 #define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */
url_query(char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)810 static ParameterError url_query(char *nextarg,
811 struct GlobalConfig *global,
812 struct OperationConfig *config)
813 {
814 size_t size = 0;
815 ParameterError err = PARAM_OK;
816 char *query;
817 struct curlx_dynbuf dyn;
818 curlx_dyn_init(&dyn, MAX_QUERY_LEN);
819
820 if(*nextarg == '+') {
821 /* use without encoding */
822 query = strdup(&nextarg[1]);
823 if(!query)
824 err = PARAM_NO_MEM;
825 }
826 else
827 err = data_urlencode(global, nextarg, &query, &size);
828
829 if(!err) {
830 if(config->query) {
831 CURLcode result = curlx_dyn_addf(&dyn, "%s&%s", config->query, query);
832 free(query);
833 if(result)
834 err = PARAM_NO_MEM;
835 else {
836 free(config->query);
837 config->query = curlx_dyn_ptr(&dyn);
838 }
839 }
840 else
841 config->query = query;
842 }
843 return err;
844 }
845
set_data(cmdline_t cmd,char * nextarg,struct GlobalConfig * global,struct OperationConfig * config)846 static ParameterError set_data(cmdline_t cmd,
847 char *nextarg,
848 struct GlobalConfig *global,
849 struct OperationConfig *config)
850 {
851 char *postdata = NULL;
852 FILE *file;
853 size_t size = 0;
854 ParameterError err = PARAM_OK;
855
856 if(cmd == C_DATA_URLENCODE) { /* --data-urlencode */
857 err = data_urlencode(global, nextarg, &postdata, &size);
858 if(err)
859 return err;
860 }
861 else if('@' == *nextarg && (cmd != C_DATA_RAW)) {
862 /* the data begins with a '@' letter, it means that a filename
863 or - (stdin) follows */
864 nextarg++; /* pass the @ */
865
866 if(!strcmp("-", nextarg)) {
867 file = stdin;
868 if(cmd == C_DATA_BINARY) /* forced data-binary */
869 set_binmode(stdin);
870 }
871 else {
872 file = fopen(nextarg, "rb");
873 if(!file) {
874 errorf(global, "Failed to open %s", nextarg);
875 return PARAM_READ_ERROR;
876 }
877 }
878
879 if((cmd == C_DATA_BINARY) || /* --data-binary */
880 (cmd == C_JSON) /* --json */)
881 /* forced binary */
882 err = file2memory(&postdata, &size, file);
883 else {
884 err = file2string(&postdata, file);
885 if(postdata)
886 size = strlen(postdata);
887 }
888
889 if(file && (file != stdin))
890 fclose(file);
891 if(err)
892 return err;
893
894 if(!postdata) {
895 /* no data from the file, point to a zero byte string to make this
896 get sent as a POST anyway */
897 postdata = strdup("");
898 if(!postdata)
899 return PARAM_NO_MEM;
900 }
901 }
902 else {
903 err = getstr(&postdata, nextarg, ALLOW_BLANK);
904 if(err)
905 return err;
906 size = strlen(postdata);
907 }
908 if(cmd == C_JSON)
909 config->jsoned = TRUE;
910
911 if(curlx_dyn_len(&config->postdata)) {
912 /* skip separator append for --json */
913 if(!err && (cmd != C_JSON) &&
914 curlx_dyn_addn(&config->postdata, "&", 1))
915 err = PARAM_NO_MEM;
916 }
917
918 if(!err && curlx_dyn_addn(&config->postdata, postdata, size))
919 err = PARAM_NO_MEM;
920
921 Curl_safefree(postdata);
922
923 config->postfields = curlx_dyn_ptr(&config->postdata);
924 return err;
925 }
926
set_rate(struct GlobalConfig * global,char * nextarg)927 static ParameterError set_rate(struct GlobalConfig *global,
928 char *nextarg)
929 {
930 /* --rate */
931 /* support a few different suffixes, extract the suffix first, then
932 get the number and convert to per hour.
933 /s == per second
934 /m == per minute
935 /h == per hour (default)
936 /d == per day (24 hours)
937 */
938 ParameterError err = PARAM_OK;
939 char *div = strchr(nextarg, '/');
940 char number[26];
941 long denominator;
942 long numerator = 60*60*1000; /* default per hour */
943 size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg);
944 if(numlen > sizeof(number) -1)
945 return PARAM_NUMBER_TOO_LARGE;
946
947 strncpy(number, nextarg, numlen);
948 number[numlen] = 0;
949 err = str2unum(&denominator, number);
950 if(err)
951 return err;
952
953 if(denominator < 1)
954 return PARAM_BAD_USE;
955
956 if(div) {
957 char unit = div[1];
958 curl_off_t numunits;
959 char *endp;
960
961 if(curlx_strtoofft(&div[1], &endp, 10, &numunits)) {
962 /* if it fails, there is no legit number specified */
963 if(endp == &div[1])
964 /* if endp did not move, accept it as a 1 */
965 numunits = 1;
966 else
967 return PARAM_BAD_USE;
968 }
969 else
970 unit = *endp;
971
972 switch(unit) {
973 case 's': /* per second */
974 numerator = 1000;
975 break;
976 case 'm': /* per minute */
977 numerator = 60*1000;
978 break;
979 case 'h': /* per hour */
980 break;
981 case 'd': /* per day */
982 numerator = 24*60*60*1000;
983 break;
984 default:
985 errorf(global, "unsupported --rate unit");
986 err = PARAM_BAD_USE;
987 break;
988 }
989
990 if((LONG_MAX / numerator) < numunits) {
991 /* overflow, too large number */
992 errorf(global, "too large --rate unit");
993 err = PARAM_NUMBER_TOO_LARGE;
994 }
995 /* this typecast is okay based on the check above */
996 numerator *= (long)numunits;
997 }
998
999 if(err)
1000 ;
1001 else if(denominator > numerator)
1002 err = PARAM_NUMBER_TOO_LARGE;
1003 else
1004 global->ms_per_transfer = numerator/denominator;
1005
1006 return err;
1007 }
1008
findlongopt(const char * opt)1009 const struct LongShort *findlongopt(const char *opt)
1010 {
1011 struct LongShort key;
1012 key.lname = opt;
1013
1014 return bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]),
1015 sizeof(aliases[0]), findarg);
1016 }
1017
1018
getparameter(const char * flag,char * nextarg,argv_item_t cleararg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)1019 ParameterError getparameter(const char *flag, /* f or -long-flag */
1020 char *nextarg, /* NULL if unset */
1021 argv_item_t cleararg,
1022 bool *usedarg, /* set to TRUE if the arg
1023 has been used */
1024 struct GlobalConfig *global,
1025 struct OperationConfig *config)
1026 {
1027 int rc;
1028 const char *parse = NULL;
1029 time_t now;
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) ? TRUE : FALSE;
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) ? TRUE : FALSE;
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) ? TRUE : FALSE;
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) ? TRUE : FALSE;
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) ? TRUE : FALSE;
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_MAX: /* --tls-max */
1695 err = str2tls_max(&config->ssl_version_max, nextarg);
1696 break;
1697 case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */
1698 config->suppress_connect_headers = toggle;
1699 break;
1700 case C_COMPRESSED_SSH: /* --compressed-ssh */
1701 config->ssh_compression = toggle;
1702 break;
1703 case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
1704 err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
1705 /* 0 is a valid value for this timeout */
1706 break;
1707 case C_TRACE_IDS: /* --trace-ids */
1708 global->traceids = toggle;
1709 break;
1710 case C_TRACE_CONFIG: /* --trace-config */
1711 if(set_trace_config(global, nextarg))
1712 err = PARAM_NO_MEM;
1713 break;
1714 case C_PROGRESS_METER: /* --progress-meter */
1715 global->noprogress = !toggle;
1716 break;
1717 case C_PROGRESS_BAR: /* --progress-bar */
1718 global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
1719 break;
1720 case C_VARIABLE: /* --variable */
1721 err = setvariable(global, nextarg);
1722 break;
1723 case C_NEXT: /* --next */
1724 err = PARAM_NEXT_OPERATION;
1725 break;
1726 case C_HTTP1_0: /* --http1.0 */
1727 /* HTTP version 1.0 */
1728 sethttpver(global, config, CURL_HTTP_VERSION_1_0);
1729 break;
1730 case C_HTTP1_1: /* --http1.1 */
1731 /* HTTP version 1.1 */
1732 sethttpver(global, config, CURL_HTTP_VERSION_1_1);
1733 break;
1734 case C_HTTP2: /* --http2 */
1735 /* HTTP version 2.0 */
1736 if(!feature_http2)
1737 return PARAM_LIBCURL_DOESNT_SUPPORT;
1738 sethttpver(global, config, CURL_HTTP_VERSION_2_0);
1739 break;
1740 case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */
1741 /* HTTP version 2.0 over clean TCP */
1742 if(!feature_http2)
1743 return PARAM_LIBCURL_DOESNT_SUPPORT;
1744 sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
1745 break;
1746 case C_HTTP3: /* --http3: */
1747 /* Try HTTP/3, allow fallback */
1748 if(!feature_http3)
1749 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1750 else
1751 sethttpver(global, config, CURL_HTTP_VERSION_3);
1752 break;
1753 case C_HTTP3_ONLY: /* --http3-only */
1754 /* Try HTTP/3 without fallback */
1755 if(!feature_http3)
1756 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1757 else
1758 sethttpver(global, config, CURL_HTTP_VERSION_3ONLY);
1759 break;
1760 case C_HTTP0_9: /* --http0.9 */
1761 /* Allow HTTP/0.9 responses! */
1762 config->http09_allowed = toggle;
1763 break;
1764 case C_PROXY_HTTP2: /* --proxy-http2 */
1765 if(!feature_httpsproxy || !feature_http2)
1766 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1767 else
1768 config->proxyver = CURLPROXY_HTTPS2;
1769 break;
1770 case C_TLSV1: /* --tlsv1 */
1771 config->ssl_version = CURL_SSLVERSION_TLSv1;
1772 break;
1773 case C_TLSV1_0: /* --tlsv1.0 */
1774 config->ssl_version = CURL_SSLVERSION_TLSv1_0;
1775 break;
1776 case C_TLSV1_1: /* --tlsv1.1 */
1777 config->ssl_version = CURL_SSLVERSION_TLSv1_1;
1778 break;
1779 case C_TLSV1_2: /* --tlsv1.2 */
1780 config->ssl_version = CURL_SSLVERSION_TLSv1_2;
1781 break;
1782 case C_TLSV1_3: /* --tlsv1.3 */
1783 config->ssl_version = CURL_SSLVERSION_TLSv1_3;
1784 break;
1785 case C_TLS13_CIPHERS: /* --tls13-ciphers */
1786 err = getstr(&config->cipher13_list, nextarg, DENY_BLANK);
1787 break;
1788 case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */
1789 err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK);
1790 break;
1791 case C_SSLV2: /* --sslv2 */
1792 warnf(global, "Ignores instruction to use SSLv2");
1793 break;
1794 case C_SSLV3: /* --sslv3 */
1795 warnf(global, "Ignores instruction to use SSLv3");
1796 break;
1797 case C_IPV4: /* --ipv4 */
1798 config->ip_version = CURL_IPRESOLVE_V4;
1799 break;
1800 case C_IPV6: /* --ipv6 */
1801 config->ip_version = CURL_IPRESOLVE_V6;
1802 break;
1803 case C_APPEND: /* --append */
1804 /* This makes the FTP sessions use APPE instead of STOR */
1805 config->ftp_append = toggle;
1806 break;
1807 case C_USER_AGENT: /* --user-agent */
1808 err = getstr(&config->useragent, nextarg, ALLOW_BLANK);
1809 break;
1810 case C_ALT_SVC: /* --alt-svc */
1811 if(!feature_altsvc)
1812 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1813 else
1814 err = getstr(&config->altsvc, nextarg, ALLOW_BLANK);
1815 break;
1816 case C_HSTS: /* --hsts */
1817 if(!feature_hsts)
1818 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1819 else
1820 err = getstr(&config->hsts, nextarg, ALLOW_BLANK);
1821 break;
1822 case C_COOKIE: /* --cookie */
1823 if(strchr(nextarg, '=')) {
1824 /* A cookie string must have a =-letter */
1825 err = add2list(&config->cookies, nextarg);
1826 break;
1827 }
1828 else {
1829 /* We have a cookie file to read from! */
1830 err = add2list(&config->cookiefiles, nextarg);
1831 }
1832 break;
1833 case C_USE_ASCII: /* --use-ascii */
1834 config->use_ascii = toggle;
1835 break;
1836 case C_COOKIE_JAR: /* --cookie-jar */
1837 err = getstr(&config->cookiejar, nextarg, DENY_BLANK);
1838 break;
1839 case C_CONTINUE_AT: /* --continue-at */
1840 /* This makes us continue an ftp transfer at given position */
1841 if(strcmp(nextarg, "-")) {
1842 err = str2offset(&config->resume_from, nextarg);
1843 config->resume_from_current = FALSE;
1844 }
1845 else {
1846 config->resume_from_current = TRUE;
1847 config->resume_from = 0;
1848 }
1849 config->use_resume = TRUE;
1850 break;
1851 case C_DATA: /* --data */
1852 case C_DATA_ASCII: /* --data-ascii */
1853 case C_DATA_BINARY: /* --data-binary */
1854 case C_DATA_URLENCODE: /* --data-urlencode */
1855 case C_JSON: /* --json */
1856 case C_DATA_RAW: /* --data-raw */
1857 err = set_data(cmd, nextarg, global, config);
1858 break;
1859 case C_URL_QUERY: /* --url-query */
1860 err = url_query(nextarg, global, config);
1861 break;
1862 case C_DUMP_CA_EMBED: /* --dump-ca-embed */
1863 err = PARAM_CA_EMBED_REQUESTED;
1864 break;
1865 case C_DUMP_HEADER: /* --dump-header */
1866 err = getstr(&config->headerfile, nextarg, DENY_BLANK);
1867 break;
1868 case C_REFERER: { /* --referer */
1869 char *ptr = strstr(nextarg, ";auto");
1870 if(ptr) {
1871 /* Automatic referer requested, this may be combined with a
1872 set initial one */
1873 config->autoreferer = TRUE;
1874 *ptr = 0; /* null-terminate here */
1875 }
1876 else
1877 config->autoreferer = FALSE;
1878 ptr = *nextarg ? nextarg : NULL;
1879 err = getstr(&config->referer, ptr, ALLOW_BLANK);
1880 }
1881 break;
1882 case C_CERT: /* --cert */
1883 cleanarg(clearthis);
1884 GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
1885 break;
1886 case C_CACERT: /* --cacert */
1887 err = getstr(&config->cacert, nextarg, DENY_BLANK);
1888 break;
1889 case C_CA_NATIVE: /* --ca-native */
1890 config->native_ca_store = toggle;
1891 break;
1892 case C_PROXY_CA_NATIVE: /* --proxy-ca-native */
1893 config->proxy_native_ca_store = toggle;
1894 break;
1895 case C_CERT_TYPE: /* --cert-type */
1896 err = getstr(&config->cert_type, nextarg, DENY_BLANK);
1897 break;
1898 case C_KEY: /* --key */
1899 err = getstr(&config->key, nextarg, DENY_BLANK);
1900 break;
1901 case C_KEY_TYPE: /* --key-type */
1902 err = getstr(&config->key_type, nextarg, DENY_BLANK);
1903 break;
1904 case C_PASS: /* --pass */
1905 err = getstr(&config->key_passwd, nextarg, DENY_BLANK);
1906 cleanarg(clearthis);
1907 break;
1908 case C_ENGINE: /* --engine */
1909 err = getstr(&config->engine, nextarg, DENY_BLANK);
1910 if(!err &&
1911 config->engine && !strcmp(config->engine, "list")) {
1912 err = PARAM_ENGINES_REQUESTED;
1913 }
1914 break;
1915 #ifndef USE_ECH
1916 case C_ECH: /* --ech, not implemented by default */
1917 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1918 break;
1919 #else
1920 case C_ECH: /* --ech */
1921 if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
1922 /* a public_name */
1923 err = getstr(&config->ech_public, nextarg, DENY_BLANK);
1924 }
1925 else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
1926 /* an ECHConfigList */
1927 if('@' != *(nextarg + 4)) {
1928 err = getstr(&config->ech_config, nextarg, DENY_BLANK);
1929 }
1930 else {
1931 /* Indirect case: @filename or @- for stdin */
1932 char *tmpcfg = NULL;
1933 FILE *file;
1934
1935 nextarg++; /* skip over '@' */
1936 if(!strcmp("-", nextarg)) {
1937 file = stdin;
1938 }
1939 else {
1940 file = fopen(nextarg, FOPEN_READTEXT);
1941 }
1942 if(!file) {
1943 warnf(global,
1944 "Couldn't read file \"%s\" "
1945 "specified for \"--ech ecl:\" option",
1946 nextarg);
1947 return PARAM_BAD_USE; /* */
1948 }
1949 err = file2string(&tmpcfg, file);
1950 if(file != stdin)
1951 fclose(file);
1952 if(err)
1953 return err;
1954 config->ech_config = aprintf("ecl:%s",tmpcfg);
1955 if(!config->ech_config)
1956 return PARAM_NO_MEM;
1957 free(tmpcfg);
1958 } /* file done */
1959 }
1960 else {
1961 /* Simple case: just a string, with a keyword */
1962 err = getstr(&config->ech, nextarg, DENY_BLANK);
1963 }
1964 break;
1965 #endif
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) ? TRUE : FALSE)) /* 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 now = time(NULL);
2650 config->condtime = (curl_off_t)curl_getdate(nextarg, &now);
2651 if(-1 == config->condtime) {
2652 /* now let's see if it is a filename to get the time from instead! */
2653 rc = getfiletime(nextarg, global, &value);
2654 if(!rc)
2655 /* pull the time out from the file */
2656 config->condtime = value;
2657 else {
2658 /* failed, remove time condition */
2659 config->timecond = CURL_TIMECOND_NONE;
2660 warnf(global,
2661 "Illegal date format for -z, --time-cond (and not "
2662 "a filename). Disabling time condition. "
2663 "See curl_getdate(3) for valid date syntax.");
2664 }
2665 }
2666 break;
2667 case C_MPTCP: /* --mptcp */
2668 config->mptcp = TRUE;
2669 break;
2670 default: /* unknown flag */
2671 err = PARAM_OPTION_UNKNOWN;
2672 break;
2673 }
2674 a = NULL;
2675 ++nopts; /* processed one option from `flag` input, loop for more */
2676 } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
2677
2678 error:
2679 if(nextalloc)
2680 free(nextarg);
2681 return err;
2682 }
2683
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2684 ParameterError parse_args(struct GlobalConfig *global, int argc,
2685 argv_item_t argv[])
2686 {
2687 int i;
2688 bool stillflags;
2689 char *orig_opt = NULL;
2690 ParameterError result = PARAM_OK;
2691 struct OperationConfig *config = global->first;
2692
2693 for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2694 orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2695 if(!orig_opt)
2696 return PARAM_NO_MEM;
2697
2698 if(stillflags && ('-' == orig_opt[0])) {
2699 bool passarg;
2700
2701 if(!strcmp("--", orig_opt))
2702 /* This indicates the end of the flags and thus enables the
2703 following (URL) argument to start with -. */
2704 stillflags = FALSE;
2705 else {
2706 char *nextarg = NULL;
2707 if(i < (argc - 1)) {
2708 nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]);
2709 if(!nextarg) {
2710 curlx_unicodefree(orig_opt);
2711 return PARAM_NO_MEM;
2712 }
2713 }
2714
2715 result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg,
2716 global, config);
2717
2718 curlx_unicodefree(nextarg);
2719 config = global->last;
2720 if(result == PARAM_NEXT_OPERATION) {
2721 /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2722 returned from this function */
2723 result = PARAM_OK;
2724
2725 if(config->url_list && config->url_list->url) {
2726 /* Allocate the next config */
2727 config->next = malloc(sizeof(struct OperationConfig));
2728 if(config->next) {
2729 /* Initialise the newly created config */
2730 config_init(config->next);
2731
2732 /* Set the global config pointer */
2733 config->next->global = global;
2734
2735 /* Update the last config pointer */
2736 global->last = config->next;
2737
2738 /* Move onto the new config */
2739 config->next->prev = config;
2740 config = config->next;
2741 }
2742 else
2743 result = PARAM_NO_MEM;
2744 }
2745 else {
2746 errorf(global, "missing URL before --next");
2747 result = PARAM_BAD_USE;
2748 }
2749 }
2750 else if(!result && passarg)
2751 i++; /* we are supposed to skip this */
2752 }
2753 }
2754 else {
2755 bool used;
2756
2757 /* Just add the URL please */
2758 result = getparameter("--url", orig_opt, argv[i], &used, global, config);
2759 }
2760
2761 if(!result)
2762 curlx_unicodefree(orig_opt);
2763 }
2764
2765 if(!result && config->content_disposition) {
2766 if(config->show_headers)
2767 result = PARAM_CONTDISP_SHOW_HEADER;
2768 else if(config->resume_from_current)
2769 result = PARAM_CONTDISP_RESUME_FROM;
2770 }
2771
2772 if(result && result != PARAM_HELP_REQUESTED &&
2773 result != PARAM_MANUAL_REQUESTED &&
2774 result != PARAM_VERSION_INFO_REQUESTED &&
2775 result != PARAM_ENGINES_REQUESTED &&
2776 result != PARAM_CA_EMBED_REQUESTED) {
2777 const char *reason = param2text(result);
2778
2779 if(orig_opt && strcmp(":", orig_opt))
2780 helpf(tool_stderr, "option %s: %s", orig_opt, reason);
2781 else
2782 helpf(tool_stderr, "%s", reason);
2783 }
2784
2785 curlx_unicodefree(orig_opt);
2786 return result;
2787 }
2788