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 #define ALLOW_BLANK TRUE
49 #define DENY_BLANK FALSE
50
getstr(char ** str,const char * val,bool allowblank)51 static ParameterError getstr(char **str, const char *val, bool allowblank)
52 {
53 if(*str) {
54 free(*str);
55 *str = NULL;
56 }
57 if(val) {
58 if(!allowblank && !val[0])
59 return PARAM_BLANK_STRING;
60
61 *str = strdup(val);
62 if(!*str)
63 return PARAM_NO_MEM;
64 }
65 return PARAM_OK;
66 }
67
68 /* this array MUST be alphasorted based on the 'lname' */
69 static const struct LongShort aliases[]= {
70 {"abstract-unix-socket", ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET},
71 {"alpn", ARG_BOOL|ARG_NO, ' ', C_ALPN},
72 {"alt-svc", ARG_STRG, ' ', C_ALT_SVC},
73 {"anyauth", ARG_BOOL, ' ', C_ANYAUTH},
74 {"append", ARG_BOOL, 'a', C_APPEND},
75 {"aws-sigv4", ARG_STRG, ' ', C_AWS_SIGV4},
76 {"basic", ARG_BOOL, ' ', C_BASIC},
77 {"buffer", ARG_BOOL|ARG_NO, 'N', C_BUFFER},
78 {"ca-native", ARG_BOOL, ' ', C_CA_NATIVE},
79 {"cacert", ARG_FILE, ' ', C_CACERT},
80 {"capath", ARG_FILE, ' ', C_CAPATH},
81 {"cert", ARG_FILE, 'E', C_CERT},
82 {"cert-status", ARG_BOOL, ' ', C_CERT_STATUS},
83 {"cert-type", ARG_STRG, ' ', C_CERT_TYPE},
84 {"ciphers", ARG_STRG, ' ', C_CIPHERS},
85 {"clobber", ARG_BOOL|ARG_NO, ' ', C_CLOBBER},
86 {"compressed", ARG_BOOL, ' ', C_COMPRESSED},
87 {"compressed-ssh", ARG_BOOL, ' ', C_COMPRESSED_SSH},
88 {"config", ARG_FILE, 'K', C_CONFIG},
89 {"connect-timeout", ARG_STRG, ' ', C_CONNECT_TIMEOUT},
90 {"connect-to", ARG_STRG, ' ', C_CONNECT_TO},
91 {"continue-at", ARG_STRG, 'C', C_CONTINUE_AT},
92 {"cookie", ARG_STRG, 'b', C_COOKIE},
93 {"cookie-jar", ARG_STRG, 'c', C_COOKIE_JAR},
94 {"create-dirs", ARG_BOOL, ' ', C_CREATE_DIRS},
95 {"create-file-mode", ARG_STRG, ' ', C_CREATE_FILE_MODE},
96 {"crlf", ARG_BOOL, ' ', C_CRLF},
97 {"crlfile", ARG_FILE, ' ', C_CRLFILE},
98 {"curves", ARG_STRG, ' ', C_CURVES},
99 {"data", ARG_STRG, 'd', C_DATA},
100 {"data-ascii", ARG_STRG, ' ', C_DATA_ASCII},
101 {"data-binary", ARG_STRG, ' ', C_DATA_BINARY},
102 {"data-raw", ARG_STRG, ' ', C_DATA_RAW},
103 {"data-urlencode", ARG_STRG, ' ', C_DATA_URLENCODE},
104 {"delegation", ARG_STRG, ' ', C_DELEGATION},
105 {"digest", ARG_BOOL, ' ', C_DIGEST},
106 {"disable", ARG_BOOL, 'q', C_DISABLE},
107 {"disable-eprt", ARG_BOOL, ' ', C_DISABLE_EPRT},
108 {"disable-epsv", ARG_BOOL, ' ', C_DISABLE_EPSV},
109 {"disallow-username-in-url", ARG_BOOL, ' ', C_DISALLOW_USERNAME_IN_URL},
110 {"dns-interface", ARG_STRG, ' ', C_DNS_INTERFACE},
111 {"dns-ipv4-addr", ARG_STRG, ' ', C_DNS_IPV4_ADDR},
112 {"dns-ipv6-addr", ARG_STRG, ' ', C_DNS_IPV6_ADDR},
113 {"dns-servers", ARG_STRG, ' ', C_DNS_SERVERS},
114 {"doh-cert-status", ARG_BOOL, ' ', C_DOH_CERT_STATUS},
115 {"doh-insecure", ARG_BOOL, ' ', C_DOH_INSECURE},
116 {"doh-url" , ARG_STRG, ' ', C_DOH_URL},
117 {"dump-ca-embed", ARG_NONE, ' ', C_DUMP_CA_EMBED},
118 {"dump-header", ARG_FILE, 'D', C_DUMP_HEADER},
119 {"ech", ARG_STRG, ' ', C_ECH},
120 {"egd-file", ARG_STRG, ' ', C_EGD_FILE},
121 {"engine", ARG_STRG, ' ', C_ENGINE},
122 {"eprt", ARG_BOOL, ' ', C_EPRT},
123 {"epsv", ARG_BOOL, ' ', C_EPSV},
124 {"etag-compare", ARG_FILE, ' ', C_ETAG_COMPARE},
125 {"etag-save", ARG_FILE, ' ', C_ETAG_SAVE},
126 {"expect100-timeout", ARG_STRG, ' ', C_EXPECT100_TIMEOUT},
127 {"fail", ARG_BOOL, 'f', C_FAIL},
128 {"fail-early", ARG_BOOL, ' ', C_FAIL_EARLY},
129 {"fail-with-body", ARG_BOOL, ' ', C_FAIL_WITH_BODY},
130 {"false-start", ARG_BOOL, ' ', C_FALSE_START},
131 {"form", ARG_STRG, 'F', C_FORM},
132 {"form-escape", ARG_BOOL, ' ', C_FORM_ESCAPE},
133 {"form-string", ARG_STRG, ' ', C_FORM_STRING},
134 {"ftp-account", ARG_STRG, ' ', C_FTP_ACCOUNT},
135 {"ftp-alternative-to-user", ARG_STRG, ' ', C_FTP_ALTERNATIVE_TO_USER},
136 {"ftp-create-dirs", ARG_BOOL, ' ', C_FTP_CREATE_DIRS},
137 {"ftp-method", ARG_STRG, ' ', C_FTP_METHOD},
138 {"ftp-pasv", ARG_BOOL, ' ', C_FTP_PASV},
139 {"ftp-port", ARG_STRG, 'P', C_FTP_PORT},
140 {"ftp-pret", ARG_BOOL, ' ', C_FTP_PRET},
141 {"ftp-skip-pasv-ip", ARG_BOOL, ' ', C_FTP_SKIP_PASV_IP},
142 {"ftp-ssl", ARG_BOOL, ' ', C_FTP_SSL},
143 {"ftp-ssl-ccc", ARG_BOOL, ' ', C_FTP_SSL_CCC},
144 {"ftp-ssl-ccc-mode", ARG_STRG, ' ', C_FTP_SSL_CCC_MODE},
145 {"ftp-ssl-control", ARG_BOOL, ' ', C_FTP_SSL_CONTROL},
146 {"ftp-ssl-reqd", ARG_BOOL, ' ', C_FTP_SSL_REQD},
147 {"get", ARG_BOOL, 'G', C_GET},
148 {"globoff", ARG_BOOL, 'g', C_GLOBOFF},
149 {"happy-eyeballs-timeout-ms", ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS},
150 {"haproxy-clientip", ARG_STRG, ' ', C_HAPROXY_CLIENTIP},
151 {"haproxy-protocol", ARG_BOOL, ' ', C_HAPROXY_PROTOCOL},
152 {"head", ARG_BOOL, 'I', C_HEAD},
153 {"header", ARG_STRG, 'H', C_HEADER},
154 {"help", ARG_BOOL, 'h', C_HELP},
155 {"hostpubmd5", ARG_STRG, ' ', C_HOSTPUBMD5},
156 {"hostpubsha256", ARG_STRG, ' ', C_HOSTPUBSHA256},
157 {"hsts", ARG_STRG, ' ', C_HSTS},
158 {"http0.9", ARG_BOOL, ' ', C_HTTP0_9},
159 {"http1.0", ARG_NONE, '0', C_HTTP1_0},
160 {"http1.1", ARG_NONE, ' ', C_HTTP1_1},
161 {"http2", ARG_NONE, ' ', C_HTTP2},
162 {"http2-prior-knowledge", ARG_NONE, ' ', C_HTTP2_PRIOR_KNOWLEDGE},
163 {"http3", ARG_NONE, ' ', C_HTTP3},
164 {"http3-only", ARG_NONE, ' ', C_HTTP3_ONLY},
165 {"ignore-content-length", ARG_BOOL, ' ', C_IGNORE_CONTENT_LENGTH},
166 {"include", ARG_BOOL, ' ', C_INCLUDE},
167 {"insecure", ARG_BOOL, 'k', C_INSECURE},
168 {"interface", ARG_STRG, ' ', C_INTERFACE},
169 {"ip-tos", ARG_STRG, ' ', C_IP_TOS},
170 #ifndef CURL_DISABLE_IPFS
171 {"ipfs-gateway", ARG_STRG, ' ', C_IPFS_GATEWAY},
172 #endif /* !CURL_DISABLE_IPFS */
173 {"ipv4", ARG_NONE, '4', C_IPV4},
174 {"ipv6", ARG_NONE, '6', C_IPV6},
175 {"json", ARG_STRG, ' ', C_JSON},
176 {"junk-session-cookies", ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES},
177 {"keepalive", ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE},
178 {"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT},
179 {"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME},
180 {"key", ARG_FILE, ' ', C_KEY},
181 {"key-type", ARG_STRG, ' ', C_KEY_TYPE},
182 {"krb", ARG_STRG, ' ', C_KRB},
183 {"krb4", ARG_STRG, ' ', C_KRB4},
184 {"libcurl", ARG_STRG, ' ', C_LIBCURL},
185 {"limit-rate", ARG_STRG, ' ', C_LIMIT_RATE},
186 {"list-only", ARG_BOOL, 'l', C_LIST_ONLY},
187 {"local-port", ARG_STRG, ' ', C_LOCAL_PORT},
188 {"location", ARG_BOOL, 'L', C_LOCATION},
189 {"location-trusted", ARG_BOOL, ' ', C_LOCATION_TRUSTED},
190 {"login-options", ARG_STRG, ' ', C_LOGIN_OPTIONS},
191 {"mail-auth", ARG_STRG, ' ', C_MAIL_AUTH},
192 {"mail-from", ARG_STRG, ' ', C_MAIL_FROM},
193 {"mail-rcpt", ARG_STRG, ' ', C_MAIL_RCPT},
194 {"mail-rcpt-allowfails", ARG_BOOL, ' ', C_MAIL_RCPT_ALLOWFAILS},
195 {"manual", ARG_BOOL, 'M', C_MANUAL},
196 {"max-filesize", ARG_STRG, ' ', C_MAX_FILESIZE},
197 {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS},
198 {"max-time", ARG_STRG, 'm', C_MAX_TIME},
199 {"metalink", ARG_BOOL, ' ', C_METALINK},
200 {"mptcp", ARG_BOOL, ' ', C_MPTCP},
201 {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE},
202 {"netrc", ARG_BOOL, 'n', C_NETRC},
203 {"netrc-file", ARG_FILE, ' ', C_NETRC_FILE},
204 {"netrc-optional", ARG_BOOL, ' ', C_NETRC_OPTIONAL},
205 {"next", ARG_NONE, ':', C_NEXT},
206 {"noproxy", ARG_STRG, ' ', C_NOPROXY},
207 {"npn", ARG_BOOL|ARG_NO, ' ', C_NPN},
208 {"ntlm", ARG_BOOL, ' ', C_NTLM},
209 {"ntlm-wb", ARG_BOOL, ' ', C_NTLM_WB},
210 {"oauth2-bearer", ARG_STRG, ' ', C_OAUTH2_BEARER},
211 {"output", ARG_FILE, 'o', C_OUTPUT},
212 {"output-dir", ARG_STRG, ' ', C_OUTPUT_DIR},
213 {"parallel", ARG_BOOL, 'Z', C_PARALLEL},
214 {"parallel-immediate", ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE},
215 {"parallel-max", ARG_STRG, ' ', C_PARALLEL_MAX},
216 {"pass", ARG_STRG, ' ', C_PASS},
217 {"path-as-is", ARG_BOOL, ' ', C_PATH_AS_IS},
218 {"pinnedpubkey", ARG_STRG, ' ', C_PINNEDPUBKEY},
219 {"post301", ARG_BOOL, ' ', C_POST301},
220 {"post302", ARG_BOOL, ' ', C_POST302},
221 {"post303", ARG_BOOL, ' ', C_POST303},
222 {"preproxy", ARG_STRG, ' ', C_PREPROXY},
223 {"progress-bar", ARG_BOOL, '#', C_PROGRESS_BAR},
224 {"progress-meter", ARG_BOOL|ARG_NO, ' ', C_PROGRESS_METER},
225 {"proto", ARG_STRG, ' ', C_PROTO},
226 {"proto-default", ARG_STRG, ' ', C_PROTO_DEFAULT},
227 {"proto-redir", ARG_STRG, ' ', C_PROTO_REDIR},
228 {"proxy", ARG_STRG, 'x', C_PROXY},
229 {"proxy-anyauth", ARG_BOOL, ' ', C_PROXY_ANYAUTH},
230 {"proxy-basic", ARG_BOOL, ' ', C_PROXY_BASIC},
231 {"proxy-ca-native", ARG_BOOL, ' ', C_PROXY_CA_NATIVE},
232 {"proxy-cacert", ARG_FILE, ' ', C_PROXY_CACERT},
233 {"proxy-capath", ARG_FILE, ' ', C_PROXY_CAPATH},
234 {"proxy-cert", ARG_FILE, ' ', C_PROXY_CERT},
235 {"proxy-cert-type", ARG_STRG, ' ', C_PROXY_CERT_TYPE},
236 {"proxy-ciphers", ARG_STRG, ' ', C_PROXY_CIPHERS},
237 {"proxy-crlfile", ARG_FILE, ' ', C_PROXY_CRLFILE},
238 {"proxy-digest", ARG_BOOL, ' ', C_PROXY_DIGEST},
239 {"proxy-header", ARG_STRG, ' ', C_PROXY_HEADER},
240 {"proxy-http2", ARG_BOOL, ' ', C_PROXY_HTTP2},
241 {"proxy-insecure", ARG_BOOL, ' ', C_PROXY_INSECURE},
242 {"proxy-key", ARG_FILE, ' ', C_PROXY_KEY},
243 {"proxy-key-type", ARG_STRG, ' ', C_PROXY_KEY_TYPE},
244 {"proxy-negotiate", ARG_BOOL, ' ', C_PROXY_NEGOTIATE},
245 {"proxy-ntlm", ARG_BOOL, ' ', C_PROXY_NTLM},
246 {"proxy-pass", ARG_STRG, ' ', C_PROXY_PASS},
247 {"proxy-pinnedpubkey", ARG_STRG, ' ', C_PROXY_PINNEDPUBKEY},
248 {"proxy-service-name", ARG_STRG, ' ', C_PROXY_SERVICE_NAME},
249 {"proxy-ssl-allow-beast", ARG_BOOL, ' ', C_PROXY_SSL_ALLOW_BEAST},
250 {"proxy-ssl-auto-client-cert", ARG_BOOL, ' ', C_PROXY_SSL_AUTO_CLIENT_CERT},
251 {"proxy-tls13-ciphers", ARG_STRG, ' ', C_PROXY_TLS13_CIPHERS},
252 {"proxy-tlsauthtype", ARG_STRG, ' ', C_PROXY_TLSAUTHTYPE},
253 {"proxy-tlspassword", ARG_STRG, ' ', C_PROXY_TLSPASSWORD},
254 {"proxy-tlsuser", ARG_STRG, ' ', C_PROXY_TLSUSER},
255 {"proxy-tlsv1", ARG_NONE, ' ', C_PROXY_TLSV1},
256 {"proxy-user", ARG_STRG, 'U', C_PROXY_USER},
257 {"proxy1.0", ARG_STRG, ' ', C_PROXY1_0},
258 {"proxytunnel", ARG_BOOL, 'p', C_PROXYTUNNEL},
259 {"pubkey", ARG_STRG, ' ', C_PUBKEY},
260 {"quote", ARG_STRG, 'Q', C_QUOTE},
261 {"random-file", ARG_FILE, ' ', C_RANDOM_FILE},
262 {"range", ARG_STRG, 'r', C_RANGE},
263 {"rate", ARG_STRG, ' ', C_RATE},
264 {"raw", ARG_BOOL, ' ', C_RAW},
265 {"referer", ARG_STRG, 'e', C_REFERER},
266 {"remote-header-name", ARG_BOOL, 'J', C_REMOTE_HEADER_NAME},
267 {"remote-name", ARG_BOOL, 'O', C_REMOTE_NAME},
268 {"remote-name-all", ARG_BOOL, ' ', C_REMOTE_NAME_ALL},
269 {"remote-time", ARG_BOOL, 'R', C_REMOTE_TIME},
270 {"remove-on-error", ARG_BOOL, ' ', C_REMOVE_ON_ERROR},
271 {"request", ARG_STRG, 'X', C_REQUEST},
272 {"request-target", ARG_STRG, ' ', C_REQUEST_TARGET},
273 {"resolve", ARG_STRG, ' ', C_RESOLVE},
274 {"retry", ARG_STRG, ' ', C_RETRY},
275 {"retry-all-errors", ARG_BOOL, ' ', C_RETRY_ALL_ERRORS},
276 {"retry-connrefused", ARG_BOOL, ' ', C_RETRY_CONNREFUSED},
277 {"retry-delay", ARG_STRG, ' ', C_RETRY_DELAY},
278 {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME},
279 {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID},
280 {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR},
281 {"service-name", ARG_STRG, ' ', C_SERVICE_NAME},
282 {"sessionid", ARG_BOOL|ARG_NO, ' ', C_SESSIONID},
283 {"show-error", ARG_BOOL, 'S', C_SHOW_ERROR},
284 {"show-headers", ARG_BOOL, 'i', C_SHOW_HEADERS},
285 {"silent", ARG_BOOL, 's', C_SILENT},
286 {"skip-existing", ARG_BOOL, ' ', C_SKIP_EXISTING},
287 {"socks4", ARG_STRG, ' ', C_SOCKS4},
288 {"socks4a", ARG_STRG, ' ', C_SOCKS4A},
289 {"socks5", ARG_STRG, ' ', C_SOCKS5},
290 {"socks5-basic", ARG_BOOL, ' ', C_SOCKS5_BASIC},
291 {"socks5-gssapi", ARG_BOOL, ' ', C_SOCKS5_GSSAPI},
292 {"socks5-gssapi-nec", ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC},
293 {"socks5-gssapi-service", ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE},
294 {"socks5-hostname", ARG_STRG, ' ', C_SOCKS5_HOSTNAME},
295 {"speed-limit", ARG_STRG, 'Y', C_SPEED_LIMIT},
296 {"speed-time", ARG_STRG, 'y', C_SPEED_TIME},
297 {"ssl", ARG_BOOL, ' ', C_SSL},
298 {"ssl-allow-beast", ARG_BOOL, ' ', C_SSL_ALLOW_BEAST},
299 {"ssl-auto-client-cert", ARG_BOOL, ' ', C_SSL_AUTO_CLIENT_CERT},
300 {"ssl-no-revoke", ARG_BOOL, ' ', C_SSL_NO_REVOKE},
301 {"ssl-reqd", ARG_BOOL, ' ', C_SSL_REQD},
302 {"ssl-revoke-best-effort", ARG_BOOL, ' ', C_SSL_REVOKE_BEST_EFFORT},
303 {"sslv2", ARG_NONE, '2', C_SSLV2},
304 {"sslv3", ARG_NONE, '3', C_SSLV3},
305 {"stderr", ARG_FILE, ' ', C_STDERR},
306 {"styled-output", ARG_BOOL, ' ', C_STYLED_OUTPUT},
307 {"suppress-connect-headers", ARG_BOOL, ' ', C_SUPPRESS_CONNECT_HEADERS},
308 {"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN},
309 {"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY},
310 {"telnet-option", ARG_STRG, 't', C_TELNET_OPTION},
311 #ifdef DEBUGBUILD
312 {"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE},
313 {"test-event", ARG_BOOL, ' ', C_TEST_EVENT},
314 #endif
315 {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE},
316 {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},
317 {"time-cond", ARG_STRG, 'z', C_TIME_COND},
318 {"tls-earlydata", ARG_BOOL, ' ', C_TLS_EARLYDATA},
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 memcpy(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 CURL_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 CURL_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 memcpy(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
parse_url(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1018 static ParameterError parse_url(struct GlobalConfig *global,
1019 struct OperationConfig *config,
1020 const char *nextarg)
1021 {
1022 ParameterError err = PARAM_OK;
1023 struct getout *url;
1024
1025 if(!config->url_get)
1026 config->url_get = config->url_list;
1027
1028 if(config->url_get) {
1029 /* there is a node here, if it already is filled-in continue to find
1030 an "empty" node */
1031 while(config->url_get && (config->url_get->flags & GETOUT_URL))
1032 config->url_get = config->url_get->next;
1033 }
1034
1035 /* now there might or might not be an available node to fill in! */
1036
1037 if(config->url_get)
1038 /* existing node */
1039 url = config->url_get;
1040 else
1041 /* there was no free node, create one! */
1042 config->url_get = url = new_getout(config);
1043
1044 if(!url)
1045 return PARAM_NO_MEM;
1046 else {
1047 /* fill in the URL */
1048 err = getstr(&url->url, nextarg, DENY_BLANK);
1049 url->flags |= GETOUT_URL;
1050 if(!err && (++config->num_urls > 1) && (config->etag_save_file ||
1051 config->etag_compare_file)) {
1052 errorf(global, "The etag options only work on a single URL");
1053 return PARAM_BAD_USE;
1054 }
1055 }
1056 return err;
1057 }
1058
parse_localport(struct OperationConfig * config,char * nextarg)1059 static ParameterError parse_localport(struct OperationConfig *config,
1060 char *nextarg)
1061 {
1062 char *pp = NULL;
1063 char *p = nextarg;
1064 while(ISDIGIT(*p))
1065 p++;
1066 if(*p) {
1067 pp = p;
1068 /* check for ' - [end]' */
1069 if(ISSPACE(*pp))
1070 pp++;
1071 if(*pp != '-')
1072 return PARAM_BAD_USE;
1073 pp++;
1074 if(*pp && ISSPACE(*pp))
1075 pp++;
1076 *p = 0; /* null-terminate to make str2unum() work below */
1077 }
1078
1079 if(str2unummax(&config->localport, nextarg, 65535))
1080 return PARAM_BAD_USE;
1081 if(!pp)
1082 config->localportrange = 1; /* default number of ports to try */
1083 else {
1084 if(str2unummax(&config->localportrange, pp, 65535))
1085 return PARAM_BAD_USE;
1086 config->localportrange -= (config->localport-1);
1087 if(config->localportrange < 1)
1088 return PARAM_BAD_USE;
1089 }
1090 return PARAM_OK;
1091 }
1092
parse_continue_at(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1093 static ParameterError parse_continue_at(struct GlobalConfig *global,
1094 struct OperationConfig *config,
1095 const char *nextarg)
1096 {
1097 ParameterError err = PARAM_OK;
1098 if(config->range) {
1099 errorf(global, "--continue-at is mutually exclusive with --range");
1100 return PARAM_BAD_USE;
1101 }
1102 if(config->rm_partial) {
1103 errorf(config->global,
1104 "--continue-at is mutually exclusive with --remove-on-error");
1105 return PARAM_BAD_USE;
1106 }
1107 if(config->file_clobber_mode == CLOBBER_NEVER) {
1108 errorf(config->global,
1109 "--continue-at is mutually exclusive with --no-clobber");
1110 return PARAM_BAD_USE;
1111 }
1112 /* This makes us continue an ftp transfer at given position */
1113 if(strcmp(nextarg, "-")) {
1114 err = str2offset(&config->resume_from, nextarg);
1115 config->resume_from_current = FALSE;
1116 }
1117 else {
1118 config->resume_from_current = TRUE;
1119 config->resume_from = 0;
1120 }
1121 config->use_resume = TRUE;
1122 return err;
1123 }
1124
parse_ech(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1125 static ParameterError parse_ech(struct GlobalConfig *global,
1126 struct OperationConfig *config,
1127 const char *nextarg)
1128 {
1129 ParameterError err = PARAM_OK;
1130 if(!feature_ech)
1131 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1132 else if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
1133 /* a public_name */
1134 nextarg += 3;
1135 err = getstr(&config->ech_public, nextarg, DENY_BLANK);
1136 }
1137 else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
1138 /* an ECHConfigList */
1139 nextarg += 4;
1140 if('@' != *nextarg) {
1141 err = getstr(&config->ech_config, nextarg, DENY_BLANK);
1142 }
1143 else {
1144 /* Indirect case: @filename or @- for stdin */
1145 char *tmpcfg = NULL;
1146 FILE *file;
1147
1148 nextarg++; /* skip over '@' */
1149 if(!strcmp("-", nextarg)) {
1150 file = stdin;
1151 }
1152 else {
1153 file = fopen(nextarg, FOPEN_READTEXT);
1154 }
1155 if(!file) {
1156 warnf(global,
1157 "Couldn't read file \"%s\" "
1158 "specified for \"--ech ecl:\" option",
1159 nextarg);
1160 return PARAM_BAD_USE; /* */
1161 }
1162 err = file2string(&tmpcfg, file);
1163 if(file != stdin)
1164 fclose(file);
1165 if(err)
1166 return err;
1167 config->ech_config = aprintf("ecl:%s",tmpcfg);
1168 free(tmpcfg);
1169 if(!config->ech_config)
1170 return PARAM_NO_MEM;
1171 } /* file done */
1172 }
1173 else {
1174 /* Simple case: just a string, with a keyword */
1175 err = getstr(&config->ech, nextarg, DENY_BLANK);
1176 }
1177 return err;
1178 }
1179
parse_header(struct GlobalConfig * global,struct OperationConfig * config,cmdline_t cmd,const char * nextarg)1180 static ParameterError parse_header(struct GlobalConfig *global,
1181 struct OperationConfig *config,
1182 cmdline_t cmd,
1183 const char *nextarg)
1184 {
1185 ParameterError err = PARAM_OK;
1186
1187 /* A custom header to append to a list */
1188 if(nextarg[0] == '@') {
1189 /* read many headers from a file or stdin */
1190 char *string;
1191 size_t len;
1192 bool use_stdin = !strcmp(&nextarg[1], "-");
1193 FILE *file = use_stdin ? stdin : fopen(&nextarg[1], FOPEN_READTEXT);
1194 if(!file) {
1195 errorf(global, "Failed to open %s", &nextarg[1]);
1196 err = PARAM_READ_ERROR;
1197 }
1198 else {
1199 err = file2memory(&string, &len, file);
1200 if(!err && string) {
1201 /* Allow strtok() here since this is not used threaded */
1202 /* !checksrc! disable BANNEDFUNC 2 */
1203 char *h = strtok(string, "\r\n");
1204 while(h) {
1205 if(cmd == C_PROXY_HEADER) /* --proxy-header */
1206 err = add2list(&config->proxyheaders, h);
1207 else
1208 err = add2list(&config->headers, h);
1209 if(err)
1210 break;
1211 h = strtok(NULL, "\r\n");
1212 }
1213 free(string);
1214 }
1215 if(!use_stdin)
1216 fclose(file);
1217 }
1218 }
1219 else {
1220 if(cmd == C_PROXY_HEADER) /* --proxy-header */
1221 err = add2list(&config->proxyheaders, nextarg);
1222 else
1223 err = add2list(&config->headers, nextarg);
1224 }
1225 return err;
1226 }
1227
parse_output(struct OperationConfig * config,const char * nextarg)1228 static ParameterError parse_output(struct OperationConfig *config,
1229 const char *nextarg)
1230 {
1231 ParameterError err = PARAM_OK;
1232 struct getout *url;
1233
1234 /* output file */
1235 if(!config->url_out)
1236 config->url_out = config->url_list;
1237 if(config->url_out) {
1238 /* there is a node here, if it already is filled-in continue to find
1239 an "empty" node */
1240 while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1241 config->url_out = config->url_out->next;
1242 }
1243
1244 /* now there might or might not be an available node to fill in! */
1245
1246 if(config->url_out)
1247 /* existing node */
1248 url = config->url_out;
1249 else {
1250 /* there was no free node, create one! */
1251 config->url_out = url = new_getout(config);
1252 }
1253
1254 if(!url)
1255 return PARAM_NO_MEM;
1256
1257 /* fill in the outfile */
1258 err = getstr(&url->outfile, nextarg, DENY_BLANK);
1259 url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1260 url->flags |= GETOUT_OUTFILE;
1261 return err;
1262 }
1263
parse_remote_name(struct OperationConfig * config,bool toggle)1264 static ParameterError parse_remote_name(struct OperationConfig *config,
1265 bool toggle)
1266 {
1267 ParameterError err = PARAM_OK;
1268 struct getout *url;
1269
1270 if(!toggle && !config->default_node_flags)
1271 return err; /* nothing to do */
1272
1273 /* output file */
1274 if(!config->url_out)
1275 config->url_out = config->url_list;
1276 if(config->url_out) {
1277 /* there is a node here, if it already is filled-in continue to find
1278 an "empty" node */
1279 while(config->url_out && (config->url_out->flags & GETOUT_OUTFILE))
1280 config->url_out = config->url_out->next;
1281 }
1282
1283 /* now there might or might not be an available node to fill in! */
1284
1285 if(config->url_out)
1286 /* existing node */
1287 url = config->url_out;
1288 else {
1289 /* there was no free node, create one! */
1290 config->url_out = url = new_getout(config);
1291 }
1292
1293 if(!url)
1294 return PARAM_NO_MEM;
1295
1296 url->outfile = NULL; /* leave it */
1297 if(toggle)
1298 url->flags |= GETOUT_USEREMOTE; /* switch on */
1299 else
1300 url->flags &= ~GETOUT_USEREMOTE; /* switch off */
1301 url->flags |= GETOUT_OUTFILE;
1302 return PARAM_OK;
1303 }
1304
parse_quote(struct OperationConfig * config,const char * nextarg)1305 static ParameterError parse_quote(struct OperationConfig *config,
1306 const char *nextarg)
1307 {
1308 ParameterError err = PARAM_OK;
1309
1310 /* QUOTE command to send to FTP server */
1311 switch(nextarg[0]) {
1312 case '-':
1313 /* prefixed with a dash makes it a POST TRANSFER one */
1314 nextarg++;
1315 err = add2list(&config->postquote, nextarg);
1316 break;
1317 case '+':
1318 /* prefixed with a plus makes it a just-before-transfer one */
1319 nextarg++;
1320 err = add2list(&config->prequote, nextarg);
1321 break;
1322 default:
1323 err = add2list(&config->quote, nextarg);
1324 break;
1325 }
1326 return err;
1327 }
1328
parse_range(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1329 static ParameterError parse_range(struct GlobalConfig *global,
1330 struct OperationConfig *config,
1331 const char *nextarg)
1332 {
1333 ParameterError err = PARAM_OK;
1334
1335 if(config->use_resume) {
1336 errorf(global, "--continue-at is mutually exclusive with --range");
1337 return PARAM_BAD_USE;
1338 }
1339 /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
1340 (and will not actually be range by definition). The manpage
1341 previously claimed that to be a good way, why this code is added to
1342 work-around it. */
1343 if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
1344 char buffer[32];
1345 curl_off_t value;
1346 if(curlx_strtoofft(nextarg, NULL, 10, &value)) {
1347 warnf(global, "unsupported range point");
1348 err = PARAM_BAD_USE;
1349 }
1350 else {
1351 warnf(global,
1352 "A specified range MUST include at least one dash (-). "
1353 "Appending one for you");
1354 msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
1355 value);
1356 Curl_safefree(config->range);
1357 config->range = strdup(buffer);
1358 if(!config->range)
1359 err = PARAM_NO_MEM;
1360 }
1361 }
1362 else {
1363 /* byte range requested */
1364 const char *tmp_range = nextarg;
1365 while(*tmp_range) {
1366 if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
1367 warnf(global, "Invalid character is found in given range. "
1368 "A specified range MUST have only digits in "
1369 "\'start\'-\'stop\'. The server's response to this "
1370 "request is uncertain.");
1371 break;
1372 }
1373 tmp_range++;
1374 }
1375 err = getstr(&config->range, nextarg, DENY_BLANK);
1376 }
1377 return err;
1378 }
1379
parse_upload_file(struct OperationConfig * config,const char * nextarg)1380 static ParameterError parse_upload_file(struct OperationConfig *config,
1381 const char *nextarg)
1382 {
1383 ParameterError err = PARAM_OK;
1384 struct getout *url;
1385
1386 /* we are uploading */
1387 if(!config->url_ul)
1388 config->url_ul = config->url_list;
1389 if(config->url_ul) {
1390 /* there is a node here, if it already is filled-in continue to find
1391 an "empty" node */
1392 while(config->url_ul && (config->url_ul->flags & GETOUT_UPLOAD))
1393 config->url_ul = config->url_ul->next;
1394 }
1395
1396 /* now there might or might not be an available node to fill in! */
1397
1398 if(config->url_ul)
1399 /* existing node */
1400 url = config->url_ul;
1401 else
1402 /* there was no free node, create one! */
1403 config->url_ul = url = new_getout(config);
1404
1405 if(!url)
1406 return PARAM_NO_MEM;
1407
1408 url->flags |= GETOUT_UPLOAD; /* mark -T used */
1409 if(!*nextarg)
1410 url->flags |= GETOUT_NOUPLOAD;
1411 else {
1412 /* "-" equals stdin, but keep the string around for now */
1413 err = getstr(&url->infile, nextarg, DENY_BLANK);
1414 }
1415 return err;
1416 }
1417
parse_verbose(struct GlobalConfig * global,bool toggle,size_t nopts)1418 static ParameterError parse_verbose(struct GlobalConfig *global,
1419 bool toggle,
1420 size_t nopts)
1421 {
1422 ParameterError err = PARAM_OK;
1423
1424 /* This option is a super-boolean with side effect when applied
1425 * more than once in the same argument flag, like `-vvv`. */
1426 if(!toggle) {
1427 global->verbosity = 0;
1428 if(set_trace_config(global, "-all"))
1429 err = PARAM_NO_MEM;
1430 global->tracetype = TRACE_NONE;
1431 return err;
1432 }
1433 else if(!nopts) {
1434 /* fist `-v` in an argument resets to base verbosity */
1435 global->verbosity = 0;
1436 if(set_trace_config(global, "-all"))
1437 return PARAM_NO_MEM;
1438 }
1439 /* the '%' thing here will cause the trace get sent to stderr */
1440 switch(global->verbosity) {
1441 case 0:
1442 global->verbosity = 1;
1443 Curl_safefree(global->trace_dump);
1444 global->trace_dump = strdup("%");
1445 if(!global->trace_dump)
1446 err = PARAM_NO_MEM;
1447 else {
1448 if(global->tracetype && (global->tracetype != TRACE_PLAIN))
1449 warnf(global,
1450 "-v, --verbose overrides an earlier trace option");
1451 global->tracetype = TRACE_PLAIN;
1452 }
1453 break;
1454 case 1:
1455 global->verbosity = 2;
1456 if(set_trace_config(global, "ids,time,protocol"))
1457 err = PARAM_NO_MEM;
1458 break;
1459 case 2:
1460 global->verbosity = 3;
1461 global->tracetype = TRACE_ASCII;
1462 if(set_trace_config(global, "ssl,read,write"))
1463 err = PARAM_NO_MEM;
1464 break;
1465 case 3:
1466 global->verbosity = 4;
1467 if(set_trace_config(global, "network"))
1468 err = PARAM_NO_MEM;
1469 break;
1470 default:
1471 /* no effect for now */
1472 break;
1473 }
1474 return err;
1475 }
1476
parse_writeout(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1477 static ParameterError parse_writeout(struct GlobalConfig *global,
1478 struct OperationConfig *config,
1479 const char *nextarg)
1480 {
1481 ParameterError err = PARAM_OK;
1482
1483 /* get the output string */
1484 if('@' == *nextarg) {
1485 /* the data begins with a '@' letter, it means that a filename
1486 or - (stdin) follows */
1487 FILE *file;
1488 const char *fname;
1489 nextarg++; /* pass the @ */
1490 if(!strcmp("-", nextarg)) {
1491 fname = "<stdin>";
1492 file = stdin;
1493 }
1494 else {
1495 fname = nextarg;
1496 file = fopen(fname, FOPEN_READTEXT);
1497 if(!file) {
1498 errorf(global, "Failed to open %s", fname);
1499 return PARAM_READ_ERROR;
1500 }
1501 }
1502 Curl_safefree(config->writeout);
1503 err = file2string(&config->writeout, file);
1504 if(file && (file != stdin))
1505 fclose(file);
1506 if(err)
1507 return err;
1508 if(!config->writeout)
1509 warnf(global, "Failed to read %s", fname);
1510 }
1511 else
1512 err = getstr(&config->writeout, nextarg, ALLOW_BLANK);
1513
1514 return err;
1515 }
1516
parse_time_cond(struct GlobalConfig * global,struct OperationConfig * config,const char * nextarg)1517 static ParameterError parse_time_cond(struct GlobalConfig *global,
1518 struct OperationConfig *config,
1519 const char *nextarg)
1520 {
1521 ParameterError err = PARAM_OK;
1522
1523 switch(*nextarg) {
1524 case '+':
1525 nextarg++;
1526 FALLTHROUGH();
1527 default:
1528 /* If-Modified-Since: (section 14.28 in RFC2068) */
1529 config->timecond = CURL_TIMECOND_IFMODSINCE;
1530 break;
1531 case '-':
1532 /* If-Unmodified-Since: (section 14.24 in RFC2068) */
1533 config->timecond = CURL_TIMECOND_IFUNMODSINCE;
1534 nextarg++;
1535 break;
1536 case '=':
1537 /* Last-Modified: (section 14.29 in RFC2068) */
1538 config->timecond = CURL_TIMECOND_LASTMOD;
1539 nextarg++;
1540 break;
1541 }
1542 config->condtime = (curl_off_t)curl_getdate(nextarg, NULL);
1543 if(-1 == config->condtime) {
1544 curl_off_t value;
1545 /* now let's see if it is a filename to get the time from instead! */
1546 int rc = getfiletime(nextarg, global, &value);
1547 if(!rc)
1548 /* pull the time out from the file */
1549 config->condtime = value;
1550 else {
1551 /* failed, remove time condition */
1552 config->timecond = CURL_TIMECOND_NONE;
1553 warnf(global,
1554 "Illegal date format for -z, --time-cond (and not "
1555 "a filename). Disabling time condition. "
1556 "See curl_getdate(3) for valid date syntax.");
1557 }
1558 }
1559 return err;
1560 }
1561
getparameter(const char * flag,char * nextarg,argv_item_t cleararg,bool * usedarg,struct GlobalConfig * global,struct OperationConfig * config)1562 ParameterError getparameter(const char *flag, /* f or -long-flag */
1563 char *nextarg, /* NULL if unset */
1564 argv_item_t cleararg,
1565 bool *usedarg, /* set to TRUE if the arg
1566 has been used */
1567 struct GlobalConfig *global,
1568 struct OperationConfig *config)
1569 {
1570 const char *parse = NULL;
1571 bool longopt = FALSE;
1572 bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
1573 size_t nopts = 0; /* options processed in `flag`*/
1574 ParameterError err = PARAM_OK;
1575 bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
1576 by using --OPTION or --no-OPTION */
1577 bool nextalloc = FALSE; /* if nextarg is allocated */
1578 static const char *redir_protos[] = {
1579 "http",
1580 "https",
1581 "ftp",
1582 "ftps",
1583 NULL
1584 };
1585 const struct LongShort *a = NULL;
1586 curl_off_t value;
1587 #ifdef HAVE_WRITABLE_ARGV
1588 argv_item_t clearthis = NULL;
1589 #else
1590 (void)cleararg;
1591 #endif
1592
1593 *usedarg = FALSE; /* default is that we do not use the arg */
1594
1595 if(('-' != flag[0]) || ('-' == flag[1])) {
1596 /* this should be a long name */
1597 const char *word = ('-' == flag[0]) ? flag + 2 : flag;
1598 bool noflagged = FALSE;
1599 bool expand = FALSE;
1600
1601 if(!strncmp(word, "no-", 3)) {
1602 /* disable this option but ignore the "no-" part when looking for it */
1603 word += 3;
1604 toggle = FALSE;
1605 noflagged = TRUE;
1606 }
1607 else if(!strncmp(word, "expand-", 7)) {
1608 /* variable expansions is to be done on the argument */
1609 word += 7;
1610 expand = TRUE;
1611 }
1612
1613 a = findlongopt(word);
1614 if(a) {
1615 longopt = TRUE;
1616 }
1617 else {
1618 err = PARAM_OPTION_UNKNOWN;
1619 goto error;
1620 }
1621 if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) {
1622 /* --no- prefixed an option that is not boolean! */
1623 err = PARAM_NO_NOT_BOOLEAN;
1624 goto error;
1625 }
1626 else if(expand && nextarg) {
1627 struct curlx_dynbuf nbuf;
1628 bool replaced;
1629
1630 if((ARGTYPE(a->desc) != ARG_STRG) &&
1631 (ARGTYPE(a->desc) != ARG_FILE)) {
1632 /* --expand on an option that is not a string or a filename */
1633 err = PARAM_EXPAND_ERROR;
1634 goto error;
1635 }
1636 err = varexpand(global, nextarg, &nbuf, &replaced);
1637 if(err) {
1638 curlx_dyn_free(&nbuf);
1639 goto error;
1640 }
1641 if(replaced) {
1642 nextarg = curlx_dyn_ptr(&nbuf);
1643 nextalloc = TRUE;
1644 }
1645 }
1646 }
1647 else {
1648 flag++; /* prefixed with one dash, pass it */
1649 parse = flag;
1650 }
1651
1652 do {
1653 /* we can loop here if we have multiple single-letters */
1654 cmdline_t cmd;
1655
1656 if(!longopt && !a) {
1657 a = findshortopt(*parse);
1658 if(!a) {
1659 err = PARAM_OPTION_UNKNOWN;
1660 break;
1661 }
1662 }
1663 cmd = (cmdline_t)a->cmd;
1664 if(ARGTYPE(a->desc) >= ARG_STRG) {
1665 /* this option requires an extra parameter */
1666 if(!longopt && parse[1]) {
1667 nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
1668 singleopt = TRUE; /* do not loop anymore after this */
1669 }
1670 else if(!nextarg) {
1671 err = PARAM_REQUIRES_PARAMETER;
1672 break;
1673 }
1674 else {
1675 #ifdef HAVE_WRITABLE_ARGV
1676 clearthis = cleararg;
1677 #endif
1678 *usedarg = TRUE; /* mark it as used */
1679 }
1680
1681 if((ARGTYPE(a->desc) == ARG_FILE) &&
1682 (nextarg[0] == '-') && nextarg[1]) {
1683 /* if the filename looks like a command line option */
1684 warnf(global, "The filename argument '%s' looks like a flag.",
1685 nextarg);
1686 }
1687 else if(!strncmp("\xe2\x80\x9c", nextarg, 3)) {
1688 warnf(global, "The argument '%s' starts with a Unicode quote where "
1689 "maybe an ASCII \" was intended?",
1690 nextarg);
1691 }
1692 }
1693 else if((ARGTYPE(a->desc) == ARG_NONE) && !toggle) {
1694 err = PARAM_NO_PREFIX;
1695 break;
1696 }
1697
1698 if(!nextarg)
1699 /* this is a precaution mostly to please scan-build, as all arguments
1700 that use nextarg should be marked as such and they will check that
1701 nextarg is set before continuing, but code analyzers are not always
1702 that aware of that state */
1703 nextarg = (char *)"";
1704
1705 switch(cmd) {
1706 case C_RANDOM_FILE: /* --random-file */
1707 case C_EGD_FILE: /* --egd-file */
1708 case C_NTLM_WB: /* --ntlm-wb */
1709 warnf(global, "--%s is deprecated and has no function anymore",
1710 a->lname);
1711 break;
1712 case C_DNS_IPV4_ADDR: /* --dns-ipv4-addr */
1713 if(!curlinfo->ares_num) /* c-ares is needed for this */
1714 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1715 else
1716 /* addr in dot notation */
1717 err = getstr(&config->dns_ipv4_addr, nextarg, DENY_BLANK);
1718 break;
1719 case C_DNS_IPV6_ADDR: /* --dns-ipv6-addr */
1720 if(!curlinfo->ares_num) /* c-ares is needed for this */
1721 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1722 else
1723 /* addr in dot notation */
1724 err = getstr(&config->dns_ipv6_addr, nextarg, DENY_BLANK);
1725 break;
1726 case C_OAUTH2_BEARER: /* --oauth2-bearer */
1727 err = getstr(&config->oauth_bearer, nextarg, DENY_BLANK);
1728 if(!err) {
1729 cleanarg(clearthis);
1730 config->authtype |= CURLAUTH_BEARER;
1731 }
1732 break;
1733 case C_CONNECT_TIMEOUT: /* --connect-timeout */
1734 err = secs2ms(&config->connecttimeout_ms, nextarg);
1735 break;
1736 case C_DOH_URL: /* --doh-url */
1737 err = getstr(&config->doh_url, nextarg, ALLOW_BLANK);
1738 if(!err && config->doh_url && !config->doh_url[0])
1739 /* if given a blank string, make it NULL again */
1740 Curl_safefree(config->doh_url);
1741 break;
1742 case C_CIPHERS: /* -- ciphers */
1743 err = getstr(&config->cipher_list, nextarg, DENY_BLANK);
1744 break;
1745 case C_DNS_INTERFACE: /* --dns-interface */
1746 if(!curlinfo->ares_num) /* c-ares is needed for this */
1747 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1748 else
1749 /* interface name */
1750 err = getstr(&config->dns_interface, nextarg, DENY_BLANK);
1751 break;
1752 case C_DISABLE_EPSV: /* --disable-epsv */
1753 config->disable_epsv = toggle;
1754 break;
1755 case C_DISALLOW_USERNAME_IN_URL: /* --disallow-username-in-url */
1756 config->disallow_username_in_url = toggle;
1757 break;
1758 case C_EPSV: /* --epsv */
1759 config->disable_epsv = !toggle;
1760 break;
1761 case C_DNS_SERVERS: /* --dns-servers */
1762 if(!curlinfo->ares_num) /* c-ares is needed for this */
1763 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1764 else
1765 /* IP addrs of DNS servers */
1766 err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
1767 break;
1768 case C_TRACE: /* --trace */
1769 err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1770 if(!err) {
1771 if(global->tracetype && (global->tracetype != TRACE_BIN))
1772 warnf(global, "--trace overrides an earlier trace/verbose option");
1773 global->tracetype = TRACE_BIN;
1774 }
1775 break;
1776 case C_NPN: /* --npn */
1777 warnf(global, "--npn is no longer supported");
1778 break;
1779 case C_TRACE_ASCII: /* --trace-ascii */
1780 err = getstr(&global->trace_dump, nextarg, DENY_BLANK);
1781 if(!err) {
1782 if(global->tracetype && (global->tracetype != TRACE_ASCII))
1783 warnf(global,
1784 "--trace-ascii overrides an earlier trace/verbose option");
1785 global->tracetype = TRACE_ASCII;
1786 }
1787 break;
1788 case C_ALPN: /* --alpn */
1789 config->noalpn = !toggle;
1790 break;
1791 case C_LIMIT_RATE: /* --limit-rate */
1792 err = GetSizeParameter(global, nextarg, "rate", &value);
1793 if(!err) {
1794 config->recvpersecond = value;
1795 config->sendpersecond = value;
1796 }
1797 break;
1798 case C_RATE:
1799 err = set_rate(global, nextarg);
1800 break;
1801 case C_COMPRESSED: /* --compressed */
1802 if(toggle && !(feature_libz || feature_brotli || feature_zstd))
1803 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1804 else
1805 config->encoding = toggle;
1806 break;
1807 case C_TR_ENCODING: /* --tr-encoding */
1808 config->tr_encoding = toggle;
1809 break;
1810 case C_DIGEST: /* --digest */
1811 if(toggle)
1812 config->authtype |= CURLAUTH_DIGEST;
1813 else
1814 config->authtype &= ~CURLAUTH_DIGEST;
1815 break;
1816 case C_NEGOTIATE: /* --negotiate */
1817 if(!toggle)
1818 config->authtype &= ~CURLAUTH_NEGOTIATE;
1819 else if(feature_spnego)
1820 config->authtype |= CURLAUTH_NEGOTIATE;
1821 else
1822 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1823 break;
1824 case C_NTLM: /* --ntlm */
1825 if(!toggle)
1826 config->authtype &= ~CURLAUTH_NTLM;
1827 else if(feature_ntlm)
1828 config->authtype |= CURLAUTH_NTLM;
1829 else
1830 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1831 break;
1832 case C_BASIC: /* --basic */
1833 if(toggle)
1834 config->authtype |= CURLAUTH_BASIC;
1835 else
1836 config->authtype &= ~CURLAUTH_BASIC;
1837 break;
1838 case C_ANYAUTH: /* --anyauth */
1839 if(toggle)
1840 config->authtype = CURLAUTH_ANY;
1841 /* --no-anyauth simply does not touch it */
1842 break;
1843 #ifdef USE_WATT32
1844 case C_WDEBUG: /* --wdebug */
1845 dbug_init();
1846 break;
1847 #endif
1848 case C_FTP_CREATE_DIRS: /* --ftp-create-dirs */
1849 config->ftp_create_dirs = toggle;
1850 break;
1851 case C_CREATE_DIRS: /* --create-dirs */
1852 config->create_dirs = toggle;
1853 break;
1854 case C_CREATE_FILE_MODE: /* --create-file-mode */
1855 err = oct2nummax(&config->create_file_mode, nextarg, 0777);
1856 break;
1857 case C_MAX_REDIRS: /* --max-redirs */
1858 /* specified max no of redirects (http(s)), this accepts -1 as a
1859 special condition */
1860 err = str2num(&config->maxredirs, nextarg);
1861 if(!err && (config->maxredirs < -1))
1862 err = PARAM_BAD_NUMERIC;
1863 break;
1864 #ifndef CURL_DISABLE_IPFS
1865 case C_IPFS_GATEWAY: /* --ipfs-gateway */
1866 err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK);
1867 break;
1868 #endif /* !CURL_DISABLE_IPFS */
1869 case C_PROXY_NTLM: /* --proxy-ntlm */
1870 if(!feature_ntlm)
1871 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1872 else
1873 config->proxyntlm = toggle;
1874 break;
1875 case C_CRLF: /* --crlf */
1876 /* LF -> CRLF conversion? */
1877 config->crlf = toggle;
1878 break;
1879 case C_AWS_SIGV4: /* --aws-sigv4 */
1880 config->authtype |= CURLAUTH_AWS_SIGV4;
1881 err = getstr(&config->aws_sigv4, nextarg, DENY_BLANK);
1882 break;
1883 case C_STDERR: /* --stderr */
1884 tool_set_stderr_file(global, nextarg);
1885 break;
1886 case C_INTERFACE: /* --interface */
1887 /* interface */
1888 err = getstr(&config->iface, nextarg, DENY_BLANK);
1889 break;
1890 case C_KRB: /* --krb */
1891 /* kerberos level string */
1892 if(!feature_spnego)
1893 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1894 else
1895 err = getstr(&config->krblevel, nextarg, DENY_BLANK);
1896 break;
1897 case C_HAPROXY_PROTOCOL: /* --haproxy-protocol */
1898 config->haproxy_protocol = toggle;
1899 break;
1900 case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */
1901 err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
1902 break;
1903 case C_MAX_FILESIZE: /* --max-filesize */
1904 err = GetSizeParameter(global, nextarg, "max-filesize", &value);
1905 if(!err)
1906 config->max_filesize = value;
1907 break;
1908 case C_DISABLE_EPRT: /* --disable-eprt */
1909 config->disable_eprt = toggle;
1910 break;
1911 case C_EPRT: /* --eprt */
1912 config->disable_eprt = !toggle;
1913 break;
1914 case C_XATTR: /* --xattr */
1915 config->xattr = toggle;
1916 break;
1917 case C_URL: /* --url */
1918 err = parse_url(global, config, nextarg);
1919 break;
1920 case C_FTP_SSL: /* --ftp-ssl */
1921 case C_SSL: /* --ssl */
1922 if(toggle && !feature_ssl)
1923 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1924 else {
1925 config->ftp_ssl = toggle;
1926 if(config->ftp_ssl)
1927 warnf(global,
1928 "--%s is an insecure option, consider --ssl-reqd instead",
1929 a->lname);
1930 }
1931 break;
1932 case C_FTP_PASV: /* --ftp-pasv */
1933 Curl_safefree(config->ftpport);
1934 break;
1935 case C_SOCKS5: /* --socks5 */
1936 /* socks5 proxy to use, and resolves the name locally and passes on the
1937 resolved address */
1938 err = getstr(&config->proxy, nextarg, DENY_BLANK);
1939 config->proxyver = CURLPROXY_SOCKS5;
1940 break;
1941 case C_SOCKS4: /* --socks4 */
1942 err = getstr(&config->proxy, nextarg, DENY_BLANK);
1943 config->proxyver = CURLPROXY_SOCKS4;
1944 break;
1945 case C_SOCKS4A: /* --socks4a */
1946 err = getstr(&config->proxy, nextarg, DENY_BLANK);
1947 config->proxyver = CURLPROXY_SOCKS4A;
1948 break;
1949 case C_SOCKS5_HOSTNAME: /* --socks5-hostname */
1950 err = getstr(&config->proxy, nextarg, DENY_BLANK);
1951 config->proxyver = CURLPROXY_SOCKS5_HOSTNAME;
1952 break;
1953 case C_TCP_NODELAY: /* --tcp-nodelay */
1954 config->tcp_nodelay = toggle;
1955 break;
1956 case C_IP_TOS: { /* --ip-tos */
1957 struct TOSEntry find;
1958 const struct TOSEntry *entry;
1959 find.name = nextarg;
1960 entry = bsearch(&find, tos_entries,
1961 sizeof(tos_entries)/sizeof(*tos_entries),
1962 sizeof(*tos_entries), find_tos);
1963 if(entry)
1964 config->ip_tos = entry->value;
1965 else /* numeric tos value */
1966 err = str2unummax(&config->ip_tos, nextarg, 0xFF);
1967 break;
1968 }
1969 case C_VLAN_PRIORITY: /* --vlan-priority */
1970 err = str2unummax(&config->vlan_priority, nextarg, 7);
1971 break;
1972 case C_PROXY_DIGEST: /* --proxy-digest */
1973 config->proxydigest = toggle;
1974 break;
1975 case C_PROXY_BASIC: /* --proxy-basic */
1976 config->proxybasic = toggle;
1977 break;
1978 case C_RETRY: /* --retry */
1979 err = str2unum(&config->req_retry, nextarg);
1980 break;
1981 case C_RETRY_CONNREFUSED: /* --retry-connrefused */
1982 config->retry_connrefused = toggle;
1983 break;
1984 case C_RETRY_DELAY: /* --retry-delay */
1985 err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
1986 break;
1987 case C_RETRY_MAX_TIME: /* --retry-max-time */
1988 err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
1989 break;
1990 case C_RETRY_ALL_ERRORS: /* --retry-all-errors */
1991 config->retry_all_errors = toggle;
1992 break;
1993 case C_PROXY_NEGOTIATE: /* --proxy-negotiate */
1994 if(!feature_spnego)
1995 err = PARAM_LIBCURL_DOESNT_SUPPORT;
1996 else
1997 config->proxynegotiate = toggle;
1998 break;
1999 case C_FORM_ESCAPE: /* --form-escape */
2000 config->mime_options &= ~CURLMIMEOPT_FORMESCAPE;
2001 if(toggle)
2002 config->mime_options |= CURLMIMEOPT_FORMESCAPE;
2003 break;
2004 case C_FTP_ACCOUNT: /* --ftp-account */
2005 err = getstr(&config->ftp_account, nextarg, DENY_BLANK);
2006 break;
2007 case C_PROXY_ANYAUTH: /* --proxy-anyauth */
2008 config->proxyanyauth = toggle;
2009 break;
2010 case C_TRACE_TIME: /* --trace-time */
2011 global->tracetime = toggle;
2012 break;
2013 case C_IGNORE_CONTENT_LENGTH: /* --ignore-content-length */
2014 config->ignorecl = toggle;
2015 break;
2016 case C_FTP_SKIP_PASV_IP: /* --ftp-skip-pasv-ip */
2017 config->ftp_skip_ip = toggle;
2018 break;
2019 case C_FTP_METHOD: /* --ftp-method */
2020 config->ftp_filemethod = ftpfilemethod(config, nextarg);
2021 break;
2022 case C_LOCAL_PORT: /* --local-port */
2023 err = parse_localport(config, nextarg);
2024 break;
2025 case C_FTP_ALTERNATIVE_TO_USER: /* --ftp-alternative-to-user */
2026 err = getstr(&config->ftp_alternative_to_user, nextarg, DENY_BLANK);
2027 break;
2028 case C_FTP_SSL_REQD: /* --ftp-ssl-reqd */
2029 case C_SSL_REQD: /* --ssl-reqd */
2030 if(toggle && !feature_ssl) {
2031 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2032 break;
2033 }
2034 config->ftp_ssl_reqd = toggle;
2035 break;
2036 case C_SESSIONID: /* --sessionid */
2037 config->disable_sessionid = !toggle;
2038 break;
2039 case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */
2040 if(toggle && !feature_ssl)
2041 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2042 else
2043 config->ftp_ssl_control = toggle;
2044 break;
2045 case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */
2046 config->ftp_ssl_ccc = toggle;
2047 if(!config->ftp_ssl_ccc_mode)
2048 config->ftp_ssl_ccc_mode = CURLFTPSSL_CCC_PASSIVE;
2049 break;
2050 case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */
2051 config->ftp_ssl_ccc = TRUE;
2052 config->ftp_ssl_ccc_mode = ftpcccmethod(config, nextarg);
2053 break;
2054 case C_LIBCURL: /* --libcurl */
2055 #ifdef CURL_DISABLE_LIBCURL_OPTION
2056 warnf(global,
2057 "--libcurl option was disabled at build-time");
2058 err = PARAM_OPTION_UNKNOWN;
2059 #else
2060 err = getstr(&global->libcurl, nextarg, DENY_BLANK);
2061 #endif
2062 break;
2063 case C_RAW: /* --raw */
2064 config->raw = toggle;
2065 break;
2066 case C_KEEPALIVE: /* --keepalive */
2067 config->nokeepalive = !toggle;
2068 break;
2069 case C_KEEPALIVE_TIME: /* --keepalive-time */
2070 err = str2unum(&config->alivetime, nextarg);
2071 break;
2072 case C_KEEPALIVE_CNT: /* --keepalive-cnt */
2073 err = str2unum(&config->alivecnt, nextarg);
2074 break;
2075 case C_POST301: /* --post301 */
2076 config->post301 = toggle;
2077 break;
2078 case C_POST302: /* --post302 */
2079 config->post302 = toggle;
2080 break;
2081 case C_POST303: /* --post303 */
2082 config->post303 = toggle;
2083 break;
2084 case C_NOPROXY: /* --noproxy */
2085 /* This specifies the noproxy list */
2086 err = getstr(&config->noproxy, nextarg, ALLOW_BLANK);
2087 break;
2088 case C_SOCKS5_GSSAPI_NEC: /* --socks5-gssapi-nec */
2089 config->socks5_gssapi_nec = toggle;
2090 break;
2091 case C_PROXY1_0: /* --proxy1.0 */
2092 /* http 1.0 proxy */
2093 err = getstr(&config->proxy, nextarg, DENY_BLANK);
2094 config->proxyver = CURLPROXY_HTTP_1_0;
2095 break;
2096 case C_TFTP_BLKSIZE: /* --tftp-blksize */
2097 err = str2unum(&config->tftp_blksize, nextarg);
2098 break;
2099 case C_MAIL_FROM: /* --mail-from */
2100 err = getstr(&config->mail_from, nextarg, DENY_BLANK);
2101 break;
2102 case C_MAIL_RCPT: /* --mail-rcpt */
2103 /* append receiver to a list */
2104 err = add2list(&config->mail_rcpt, nextarg);
2105 break;
2106 case C_FTP_PRET: /* --ftp-pret */
2107 config->ftp_pret = toggle;
2108 break;
2109 case C_PROTO: /* --proto */
2110 config->proto_present = TRUE;
2111 err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
2112 break;
2113 case C_PROTO_REDIR: /* --proto-redir */
2114 config->proto_redir_present = TRUE;
2115 if(proto2num(config, redir_protos, &config->proto_redir_str,
2116 nextarg))
2117 err = PARAM_BAD_USE;
2118 break;
2119 case C_RESOLVE: /* --resolve */
2120 err = add2list(&config->resolve, nextarg);
2121 break;
2122 case C_DELEGATION: /* --delegation */
2123 config->gssapi_delegation = delegation(config, nextarg);
2124 break;
2125 case C_MAIL_AUTH: /* --mail-auth */
2126 err = getstr(&config->mail_auth, nextarg, DENY_BLANK);
2127 break;
2128 case C_METALINK: /* --metalink */
2129 errorf(global, "--metalink is disabled");
2130 err = PARAM_BAD_USE;
2131 break;
2132 case C_SASL_AUTHZID: /* --sasl-authzid */
2133 err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK);
2134 break;
2135 case C_SASL_IR: /* --sasl-ir */
2136 config->sasl_ir = toggle;
2137 break;
2138 #ifdef DEBUGBUILD
2139 case C_TEST_DUPHANDLE: /* --test-duphandle */
2140 global->test_duphandle = toggle;
2141 break;
2142 case C_TEST_EVENT: /* --test-event */
2143 global->test_event_based = toggle;
2144 break;
2145 #endif
2146 case C_UNIX_SOCKET: /* --unix-socket */
2147 config->abstract_unix_socket = FALSE;
2148 err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
2149 break;
2150 case C_PATH_AS_IS: /* --path-as-is */
2151 config->path_as_is = toggle;
2152 break;
2153 case C_PROXY_SERVICE_NAME: /* --proxy-service-name */
2154 err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK);
2155 break;
2156 case C_SERVICE_NAME: /* --service-name */
2157 err = getstr(&config->service_name, nextarg, DENY_BLANK);
2158 break;
2159 case C_PROTO_DEFAULT: /* --proto-default */
2160 err = getstr(&config->proto_default, nextarg, DENY_BLANK);
2161 if(!err)
2162 err = check_protocol(config->proto_default);
2163 break;
2164 case C_EXPECT100_TIMEOUT: /* --expect100-timeout */
2165 err = secs2ms(&config->expect100timeout_ms, nextarg);
2166 break;
2167 case C_TFTP_NO_OPTIONS: /* --tftp-no-options */
2168 config->tftp_no_options = toggle;
2169 break;
2170 case C_CONNECT_TO: /* --connect-to */
2171 err = add2list(&config->connect_to, nextarg);
2172 break;
2173 case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */
2174 config->abstract_unix_socket = TRUE;
2175 err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);
2176 break;
2177 case C_TLS_EARLYDATA: /* --tls-earlydata */
2178 if(feature_ssl)
2179 config->ssl_allow_earlydata = toggle;
2180 break;
2181 case C_TLS_MAX: /* --tls-max */
2182 err = str2tls_max(&config->ssl_version_max, nextarg);
2183 break;
2184 case C_SUPPRESS_CONNECT_HEADERS: /* --suppress-connect-headers */
2185 config->suppress_connect_headers = toggle;
2186 break;
2187 case C_COMPRESSED_SSH: /* --compressed-ssh */
2188 config->ssh_compression = toggle;
2189 break;
2190 case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */
2191 err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg);
2192 /* 0 is a valid value for this timeout */
2193 break;
2194 case C_TRACE_IDS: /* --trace-ids */
2195 global->traceids = toggle;
2196 break;
2197 case C_TRACE_CONFIG: /* --trace-config */
2198 if(set_trace_config(global, nextarg))
2199 err = PARAM_NO_MEM;
2200 break;
2201 case C_PROGRESS_METER: /* --progress-meter */
2202 global->noprogress = !toggle;
2203 break;
2204 case C_PROGRESS_BAR: /* --progress-bar */
2205 global->progressmode = toggle ? CURL_PROGRESS_BAR : CURL_PROGRESS_STATS;
2206 break;
2207 case C_VARIABLE: /* --variable */
2208 err = setvariable(global, nextarg);
2209 break;
2210 case C_NEXT: /* --next */
2211 err = PARAM_NEXT_OPERATION;
2212 break;
2213 case C_HTTP1_0: /* --http1.0 */
2214 /* HTTP version 1.0 */
2215 sethttpver(global, config, CURL_HTTP_VERSION_1_0);
2216 break;
2217 case C_HTTP1_1: /* --http1.1 */
2218 /* HTTP version 1.1 */
2219 sethttpver(global, config, CURL_HTTP_VERSION_1_1);
2220 break;
2221 case C_HTTP2: /* --http2 */
2222 /* HTTP version 2.0 */
2223 if(!feature_http2)
2224 return PARAM_LIBCURL_DOESNT_SUPPORT;
2225 sethttpver(global, config, CURL_HTTP_VERSION_2_0);
2226 break;
2227 case C_HTTP2_PRIOR_KNOWLEDGE: /* --http2-prior-knowledge */
2228 /* HTTP version 2.0 over clean TCP */
2229 if(!feature_http2)
2230 return PARAM_LIBCURL_DOESNT_SUPPORT;
2231 sethttpver(global, config, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
2232 break;
2233 case C_HTTP3: /* --http3: */
2234 /* Try HTTP/3, allow fallback */
2235 if(!feature_http3)
2236 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2237 else
2238 sethttpver(global, config, CURL_HTTP_VERSION_3);
2239 break;
2240 case C_HTTP3_ONLY: /* --http3-only */
2241 /* Try HTTP/3 without fallback */
2242 if(!feature_http3)
2243 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2244 else
2245 sethttpver(global, config, CURL_HTTP_VERSION_3ONLY);
2246 break;
2247 case C_HTTP0_9: /* --http0.9 */
2248 /* Allow HTTP/0.9 responses! */
2249 config->http09_allowed = toggle;
2250 break;
2251 case C_PROXY_HTTP2: /* --proxy-http2 */
2252 if(!feature_httpsproxy || !feature_http2)
2253 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2254 else
2255 config->proxyver = CURLPROXY_HTTPS2;
2256 break;
2257 case C_TLSV1: /* --tlsv1 */
2258 config->ssl_version = CURL_SSLVERSION_TLSv1;
2259 break;
2260 case C_TLSV1_0: /* --tlsv1.0 */
2261 config->ssl_version = CURL_SSLVERSION_TLSv1_0;
2262 break;
2263 case C_TLSV1_1: /* --tlsv1.1 */
2264 config->ssl_version = CURL_SSLVERSION_TLSv1_1;
2265 break;
2266 case C_TLSV1_2: /* --tlsv1.2 */
2267 config->ssl_version = CURL_SSLVERSION_TLSv1_2;
2268 break;
2269 case C_TLSV1_3: /* --tlsv1.3 */
2270 config->ssl_version = CURL_SSLVERSION_TLSv1_3;
2271 break;
2272 case C_TLS13_CIPHERS: /* --tls13-ciphers */
2273 err = getstr(&config->cipher13_list, nextarg, DENY_BLANK);
2274 break;
2275 case C_PROXY_TLS13_CIPHERS: /* --proxy-tls13-ciphers */
2276 err = getstr(&config->proxy_cipher13_list, nextarg, DENY_BLANK);
2277 break;
2278 case C_SSLV2: /* --sslv2 */
2279 warnf(global, "Ignores instruction to use SSLv2");
2280 break;
2281 case C_SSLV3: /* --sslv3 */
2282 warnf(global, "Ignores instruction to use SSLv3");
2283 break;
2284 case C_IPV4: /* --ipv4 */
2285 config->ip_version = CURL_IPRESOLVE_V4;
2286 break;
2287 case C_IPV6: /* --ipv6 */
2288 config->ip_version = CURL_IPRESOLVE_V6;
2289 break;
2290 case C_APPEND: /* --append */
2291 /* This makes the FTP sessions use APPE instead of STOR */
2292 config->ftp_append = toggle;
2293 break;
2294 case C_USER_AGENT: /* --user-agent */
2295 err = getstr(&config->useragent, nextarg, ALLOW_BLANK);
2296 break;
2297 case C_ALT_SVC: /* --alt-svc */
2298 if(!feature_altsvc)
2299 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2300 else
2301 err = getstr(&config->altsvc, nextarg, ALLOW_BLANK);
2302 break;
2303 case C_HSTS: /* --hsts */
2304 if(!feature_hsts)
2305 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2306 else
2307 err = getstr(&config->hsts, nextarg, ALLOW_BLANK);
2308 break;
2309 case C_COOKIE: /* --cookie */
2310 if(strchr(nextarg, '=')) {
2311 /* A cookie string must have a =-letter */
2312 err = add2list(&config->cookies, nextarg);
2313 break;
2314 }
2315 else {
2316 /* We have a cookie file to read from! */
2317 err = add2list(&config->cookiefiles, nextarg);
2318 }
2319 break;
2320 case C_USE_ASCII: /* --use-ascii */
2321 config->use_ascii = toggle;
2322 break;
2323 case C_COOKIE_JAR: /* --cookie-jar */
2324 err = getstr(&config->cookiejar, nextarg, DENY_BLANK);
2325 break;
2326 case C_CONTINUE_AT: /* --continue-at */
2327 err = parse_continue_at(global, config, nextarg);
2328 break;
2329 case C_DATA: /* --data */
2330 case C_DATA_ASCII: /* --data-ascii */
2331 case C_DATA_BINARY: /* --data-binary */
2332 case C_DATA_URLENCODE: /* --data-urlencode */
2333 case C_JSON: /* --json */
2334 case C_DATA_RAW: /* --data-raw */
2335 err = set_data(cmd, nextarg, global, config);
2336 break;
2337 case C_URL_QUERY: /* --url-query */
2338 err = url_query(nextarg, global, config);
2339 break;
2340 case C_DUMP_CA_EMBED: /* --dump-ca-embed */
2341 err = PARAM_CA_EMBED_REQUESTED;
2342 break;
2343 case C_DUMP_HEADER: /* --dump-header */
2344 err = getstr(&config->headerfile, nextarg, DENY_BLANK);
2345 break;
2346 case C_REFERER: { /* --referer */
2347 char *ptr = strstr(nextarg, ";auto");
2348 if(ptr) {
2349 /* Automatic referer requested, this may be combined with a
2350 set initial one */
2351 config->autoreferer = TRUE;
2352 *ptr = 0; /* null-terminate here */
2353 }
2354 else
2355 config->autoreferer = FALSE;
2356 ptr = *nextarg ? nextarg : NULL;
2357 err = getstr(&config->referer, ptr, ALLOW_BLANK);
2358 }
2359 break;
2360 case C_CERT: /* --cert */
2361 cleanarg(clearthis);
2362 GetFileAndPassword(nextarg, &config->cert, &config->key_passwd);
2363 break;
2364 case C_CACERT: /* --cacert */
2365 err = getstr(&config->cacert, nextarg, DENY_BLANK);
2366 break;
2367 case C_CA_NATIVE: /* --ca-native */
2368 config->native_ca_store = toggle;
2369 break;
2370 case C_PROXY_CA_NATIVE: /* --proxy-ca-native */
2371 config->proxy_native_ca_store = toggle;
2372 break;
2373 case C_CERT_TYPE: /* --cert-type */
2374 err = getstr(&config->cert_type, nextarg, DENY_BLANK);
2375 break;
2376 case C_KEY: /* --key */
2377 err = getstr(&config->key, nextarg, DENY_BLANK);
2378 break;
2379 case C_KEY_TYPE: /* --key-type */
2380 err = getstr(&config->key_type, nextarg, DENY_BLANK);
2381 break;
2382 case C_PASS: /* --pass */
2383 err = getstr(&config->key_passwd, nextarg, DENY_BLANK);
2384 cleanarg(clearthis);
2385 break;
2386 case C_ENGINE: /* --engine */
2387 err = getstr(&config->engine, nextarg, DENY_BLANK);
2388 if(!err &&
2389 config->engine && !strcmp(config->engine, "list")) {
2390 err = PARAM_ENGINES_REQUESTED;
2391 }
2392 break;
2393 case C_ECH: /* --ech */
2394 err = parse_ech(global, config, nextarg);
2395 break;
2396 case C_CAPATH: /* --capath */
2397 err = getstr(&config->capath, nextarg, DENY_BLANK);
2398 break;
2399 case C_PUBKEY: /* --pubkey */
2400 err = getstr(&config->pubkey, nextarg, DENY_BLANK);
2401 break;
2402 case C_HOSTPUBMD5: /* --hostpubmd5 */
2403 err = getstr(&config->hostpubmd5, nextarg, DENY_BLANK);
2404 if(!err) {
2405 if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
2406 err = PARAM_BAD_USE;
2407 }
2408 break;
2409 case C_HOSTPUBSHA256: /* --hostpubsha256 */
2410 err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
2411 break;
2412 case C_CRLFILE: /* --crlfile */
2413 err = getstr(&config->crlfile, nextarg, DENY_BLANK);
2414 break;
2415 case C_TLSUSER: /* --tlsuser */
2416 if(!feature_tls_srp)
2417 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2418 else
2419 err = getstr(&config->tls_username, nextarg, DENY_BLANK);
2420 cleanarg(clearthis);
2421 break;
2422 case C_TLSPASSWORD: /* --tlspassword */
2423 if(!feature_tls_srp)
2424 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2425 else
2426 err = getstr(&config->tls_password, nextarg, ALLOW_BLANK);
2427 cleanarg(clearthis);
2428 break;
2429 case C_TLSAUTHTYPE: /* --tlsauthtype */
2430 if(!feature_tls_srp)
2431 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2432 else {
2433 err = getstr(&config->tls_authtype, nextarg, DENY_BLANK);
2434 if(!err && strcmp(config->tls_authtype, "SRP"))
2435 err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2436 }
2437 break;
2438 case C_SSL_ALLOW_BEAST: /* --ssl-allow-beast */
2439 if(feature_ssl)
2440 config->ssl_allow_beast = toggle;
2441 break;
2442 case C_SSL_AUTO_CLIENT_CERT: /* --ssl-auto-client-cert */
2443 if(feature_ssl)
2444 config->ssl_auto_client_cert = toggle;
2445 break;
2446 case C_PROXY_SSL_AUTO_CLIENT_CERT: /* --proxy-ssl-auto-client-cert */
2447 if(feature_ssl)
2448 config->proxy_ssl_auto_client_cert = toggle;
2449 break;
2450 case C_PINNEDPUBKEY: /* --pinnedpubkey */
2451 err = getstr(&config->pinnedpubkey, nextarg, DENY_BLANK);
2452 break;
2453 case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */
2454 err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK);
2455 break;
2456 case C_CERT_STATUS: /* --cert-status */
2457 config->verifystatus = TRUE;
2458 break;
2459 case C_DOH_CERT_STATUS: /* --doh-cert-status */
2460 config->doh_verifystatus = TRUE;
2461 break;
2462 case C_FALSE_START: /* --false-start */
2463 config->falsestart = TRUE;
2464 break;
2465 case C_SSL_NO_REVOKE: /* --ssl-no-revoke */
2466 if(feature_ssl)
2467 config->ssl_no_revoke = TRUE;
2468 break;
2469 case C_SSL_REVOKE_BEST_EFFORT: /* --ssl-revoke-best-effort */
2470 if(feature_ssl)
2471 config->ssl_revoke_best_effort = TRUE;
2472 break;
2473 case C_TCP_FASTOPEN: /* --tcp-fastopen */
2474 config->tcp_fastopen = TRUE;
2475 break;
2476 case C_PROXY_TLSUSER: /* --proxy-tlsuser */
2477 cleanarg(clearthis);
2478 if(!feature_tls_srp)
2479 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2480 else
2481 err = getstr(&config->proxy_tls_username, nextarg, ALLOW_BLANK);
2482 break;
2483 case C_PROXY_TLSPASSWORD: /* --proxy-tlspassword */
2484 cleanarg(clearthis);
2485 if(!feature_tls_srp)
2486 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2487 else
2488 err = getstr(&config->proxy_tls_password, nextarg, DENY_BLANK);
2489 break;
2490 case C_PROXY_TLSAUTHTYPE: /* --proxy-tlsauthtype */
2491 if(!feature_tls_srp)
2492 err = PARAM_LIBCURL_DOESNT_SUPPORT;
2493 else {
2494 err = getstr(&config->proxy_tls_authtype, nextarg, DENY_BLANK);
2495 if(!err && strcmp(config->proxy_tls_authtype, "SRP"))
2496 err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */
2497 }
2498 break;
2499 case C_PROXY_CERT: /* --proxy-cert */
2500 cleanarg(clearthis);
2501 GetFileAndPassword(nextarg, &config->proxy_cert,
2502 &config->proxy_key_passwd);
2503 break;
2504 case C_PROXY_CERT_TYPE: /* --proxy-cert-type */
2505 err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK);
2506 break;
2507 case C_PROXY_KEY: /* --proxy-key */
2508 err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK);
2509 break;
2510 case C_PROXY_KEY_TYPE: /* --proxy-key-type */
2511 err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK);
2512 break;
2513 case C_PROXY_PASS: /* --proxy-pass */
2514 err = getstr(&config->proxy_key_passwd, nextarg, ALLOW_BLANK);
2515 cleanarg(clearthis);
2516 break;
2517 case C_PROXY_CIPHERS: /* --proxy-ciphers */
2518 err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK);
2519 break;
2520 case C_PROXY_CRLFILE: /* --proxy-crlfile */
2521 err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK);
2522 break;
2523 case C_PROXY_SSL_ALLOW_BEAST: /* --proxy-ssl-allow-beast */
2524 if(feature_ssl)
2525 config->proxy_ssl_allow_beast = toggle;
2526 break;
2527 case C_LOGIN_OPTIONS: /* --login-options */
2528 err = getstr(&config->login_options, nextarg, ALLOW_BLANK);
2529 break;
2530 case C_PROXY_CACERT: /* --proxy-cacert */
2531 err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK);
2532 break;
2533 case C_PROXY_CAPATH: /* --proxy-capath */
2534 err = getstr(&config->proxy_capath, nextarg, DENY_BLANK);
2535 break;
2536 case C_PROXY_INSECURE: /* --proxy-insecure */
2537 config->proxy_insecure_ok = toggle;
2538 break;
2539 case C_PROXY_TLSV1: /* --proxy-tlsv1 */
2540 /* TLS version 1 for proxy */
2541 config->proxy_ssl_version = CURL_SSLVERSION_TLSv1;
2542 break;
2543 case C_SOCKS5_BASIC: /* --socks5-basic */
2544 if(toggle)
2545 config->socks5_auth |= CURLAUTH_BASIC;
2546 else
2547 config->socks5_auth &= ~CURLAUTH_BASIC;
2548 break;
2549 case C_SOCKS5_GSSAPI: /* --socks5-gssapi */
2550 if(toggle)
2551 config->socks5_auth |= CURLAUTH_GSSAPI;
2552 else
2553 config->socks5_auth &= ~CURLAUTH_GSSAPI;
2554 break;
2555 case C_ETAG_SAVE: /* --etag-save */
2556 if(config->num_urls > 1) {
2557 errorf(global, "The etag options only work on a single URL");
2558 err = PARAM_BAD_USE;
2559 }
2560 else
2561 err = getstr(&config->etag_save_file, nextarg, DENY_BLANK);
2562 break;
2563 case C_ETAG_COMPARE: /* --etag-compare */
2564 if(config->num_urls > 1) {
2565 errorf(global, "The etag options only work on a single URL");
2566 err = PARAM_BAD_USE;
2567 }
2568 else
2569 err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK);
2570 break;
2571 case C_CURVES: /* --curves */
2572 err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK);
2573 break;
2574 case C_FAIL_EARLY: /* --fail-early */
2575 global->fail_early = toggle;
2576 break;
2577 case C_STYLED_OUTPUT: /* --styled-output */
2578 global->styled_output = toggle;
2579 break;
2580 case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */
2581 config->mail_rcpt_allowfails = toggle;
2582 break;
2583 case C_FAIL_WITH_BODY: /* --fail-with-body */
2584 config->failwithbody = toggle;
2585 if(config->failonerror && config->failwithbody) {
2586 errorf(config->global, "You must select either --fail or "
2587 "--fail-with-body, not both.");
2588 err = PARAM_BAD_USE;
2589 }
2590 break;
2591 case C_REMOVE_ON_ERROR: /* --remove-on-error */
2592 if(config->use_resume && toggle) {
2593 errorf(config->global,
2594 "--continue-at is mutually exclusive with --remove-on-error");
2595 return PARAM_BAD_USE;
2596 }
2597 config->rm_partial = toggle;
2598 break;
2599 case C_FAIL: /* --fail */
2600 config->failonerror = toggle;
2601 if(config->failonerror && config->failwithbody) {
2602 errorf(config->global, "You must select either --fail or "
2603 "--fail-with-body, not both.");
2604 err = PARAM_BAD_USE;
2605 }
2606 break;
2607 case C_FORM: /* --form */
2608 case C_FORM_STRING: /* --form-string */
2609 /* "form data" simulation, this is a little advanced so lets do our best
2610 to sort this out slowly and carefully */
2611 if(formparse(config,
2612 nextarg,
2613 &config->mimeroot,
2614 &config->mimecurrent,
2615 (cmd == C_FORM_STRING))) /* literal string */
2616 err = PARAM_BAD_USE;
2617 else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq))
2618 err = PARAM_BAD_USE;
2619 break;
2620 case C_GLOBOFF: /* --globoff */
2621 config->globoff = toggle;
2622 break;
2623 case C_GET: /* --get */
2624 config->use_httpget = toggle;
2625 break;
2626 case C_REQUEST_TARGET: /* --request-target */
2627 err = getstr(&config->request_target, nextarg, DENY_BLANK);
2628 break;
2629 case C_HELP: /* --help */
2630 if(toggle) {
2631 if(*nextarg) {
2632 global->help_category = strdup(nextarg);
2633 if(!global->help_category) {
2634 err = PARAM_NO_MEM;
2635 break;
2636 }
2637 }
2638 err = PARAM_HELP_REQUESTED;
2639 }
2640 /* we now actually support --no-help too! */
2641 break;
2642 case C_HEADER: /* --header */
2643 case C_PROXY_HEADER: /* --proxy-header */
2644 err = parse_header(global, config, cmd, nextarg);
2645 break;
2646 case C_INCLUDE: /* --include */
2647 case C_SHOW_HEADERS: /* --show-headers */
2648 config->show_headers = toggle; /* show the headers as well in the
2649 general output stream */
2650 break;
2651 case C_JUNK_SESSION_COOKIES: /* --junk-session-cookies */
2652 config->cookiesession = toggle;
2653 break;
2654 case C_HEAD: /* --head */
2655 config->no_body = toggle;
2656 config->show_headers = toggle;
2657 if(SetHTTPrequest(config, (config->no_body) ? TOOL_HTTPREQ_HEAD :
2658 TOOL_HTTPREQ_GET, &config->httpreq))
2659 err = PARAM_BAD_USE;
2660 break;
2661 case C_REMOTE_HEADER_NAME: /* --remote-header-name */
2662 config->content_disposition = toggle;
2663 break;
2664 case C_INSECURE: /* --insecure */
2665 config->insecure_ok = toggle;
2666 break;
2667 case C_DOH_INSECURE: /* --doh-insecure */
2668 config->doh_insecure_ok = toggle;
2669 break;
2670 case C_CONFIG: /* --config */
2671 if(parseconfig(nextarg, global)) {
2672 errorf(global, "cannot read config from '%s'", nextarg);
2673 err = PARAM_READ_ERROR;
2674 }
2675 break;
2676 case C_LIST_ONLY: /* --list-only */
2677 config->dirlistonly = toggle; /* only list the names of the FTP dir */
2678 break;
2679 case C_LOCATION_TRUSTED: /* --location-trusted */
2680 /* Continue to send authentication (user+password) when following
2681 * locations, even when hostname changed */
2682 config->unrestricted_auth = toggle;
2683 FALLTHROUGH();
2684 case C_LOCATION: /* --location */
2685 config->followlocation = toggle; /* Follow Location: HTTP headers */
2686 break;
2687 case C_MAX_TIME: /* --max-time */
2688 /* specified max time */
2689 err = secs2ms(&config->timeout_ms, nextarg);
2690 break;
2691 case C_MANUAL: /* --manual */
2692 if(toggle) { /* --no-manual shows no manual... */
2693 #ifndef USE_MANUAL
2694 warnf(global,
2695 "built-in manual was disabled at build-time");
2696 #endif
2697 err = PARAM_MANUAL_REQUESTED;
2698 }
2699 break;
2700 case C_NETRC_OPTIONAL: /* --netrc-optional */
2701 config->netrc_opt = toggle;
2702 break;
2703 case C_NETRC_FILE: /* --netrc-file */
2704 err = getstr(&config->netrc_file, nextarg, DENY_BLANK);
2705 break;
2706 case C_NETRC: /* --netrc */
2707 /* pick info from .netrc, if this is used for http, curl will
2708 automatically enforce user+password with the request */
2709 config->netrc = toggle;
2710 break;
2711 case C_BUFFER: /* --buffer */
2712 /* disable the output I/O buffering. note that the option is called
2713 --buffer but is mostly used in the negative form: --no-buffer */
2714 config->nobuffer = longopt ? !toggle : TRUE;
2715 break;
2716 case C_REMOTE_NAME_ALL: /* --remote-name-all */
2717 config->default_node_flags = toggle ? GETOUT_USEREMOTE : 0;
2718 break;
2719 case C_OUTPUT_DIR: /* --output-dir */
2720 err = getstr(&config->output_dir, nextarg, DENY_BLANK);
2721 break;
2722 case C_CLOBBER: /* --clobber */
2723 if(config->use_resume && !toggle) {
2724 errorf(config->global,
2725 "--continue-at is mutually exclusive with --no-clobber");
2726 return PARAM_BAD_USE;
2727 }
2728 config->file_clobber_mode = toggle ? CLOBBER_ALWAYS : CLOBBER_NEVER;
2729 break;
2730 case C_OUTPUT: /* --output */
2731 err = parse_output(config, nextarg);
2732 break;
2733 case C_REMOTE_NAME: /* --remote-name */
2734 err = parse_remote_name(config, toggle);
2735 break;
2736 case C_FTP_PORT: /* --ftp-port */
2737 /* This makes the FTP sessions use PORT instead of PASV */
2738 /* use <eth0> or <192.168.10.10> style addresses. Anything except
2739 this will make us try to get the "default" address.
2740 NOTE: this is a changed behavior since the released 4.1!
2741 */
2742 err = getstr(&config->ftpport, nextarg, DENY_BLANK);
2743 break;
2744 case C_PROXYTUNNEL: /* --proxytunnel */
2745 /* proxy tunnel for non-http protocols */
2746 config->proxytunnel = toggle;
2747 break;
2748
2749 case C_DISABLE: /* --disable */
2750 /* if used first, already taken care of, we do it like this so we do not
2751 cause an error! */
2752 break;
2753 case C_QUOTE: /* --quote */
2754 err = parse_quote(config, nextarg);
2755 break;
2756 case C_RANGE: /* --range */
2757 err = parse_range(global, config, nextarg);
2758 break;
2759 case C_REMOTE_TIME: /* --remote-time */
2760 /* use remote file's time */
2761 config->remote_time = toggle;
2762 break;
2763 case C_SILENT: /* --silent */
2764 global->silent = toggle;
2765 break;
2766 case C_SKIP_EXISTING: /* --skip-existing */
2767 config->skip_existing = toggle;
2768 break;
2769 case C_SHOW_ERROR: /* --show-error */
2770 global->showerror = toggle;
2771 break;
2772 case C_TELNET_OPTION: /* --telnet-option */
2773 /* Telnet options */
2774 err = add2list(&config->telnet_options, nextarg);
2775 break;
2776 case C_UPLOAD_FILE: /* --upload-file */
2777 err = parse_upload_file(config, nextarg);
2778 break;
2779 case C_USER: /* --user */
2780 /* user:password */
2781 err = getstr(&config->userpwd, nextarg, ALLOW_BLANK);
2782 cleanarg(clearthis);
2783 break;
2784 case C_PROXY_USER: /* --proxy-user */
2785 /* Proxy user:password */
2786 err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK);
2787 cleanarg(clearthis);
2788 break;
2789 case C_VERBOSE: /* --verbose */
2790 err = parse_verbose(global, toggle, nopts);
2791 break;
2792 case C_VERSION: /* --version */
2793 if(toggle) /* --no-version yields no output! */
2794 err = PARAM_VERSION_INFO_REQUESTED;
2795 break;
2796 case C_WRITE_OUT: /* --write-out */
2797 err = parse_writeout(global, config, nextarg);
2798 break;
2799 case C_PREPROXY: /* --preproxy */
2800 err = getstr(&config->preproxy, nextarg, DENY_BLANK);
2801 break;
2802 case C_PROXY: /* --proxy */
2803 /* --proxy */
2804 err = getstr(&config->proxy, nextarg, ALLOW_BLANK);
2805 if(config->proxyver != CURLPROXY_HTTPS2)
2806 config->proxyver = CURLPROXY_HTTP;
2807 break;
2808 case C_REQUEST: /* --request */
2809 /* set custom request */
2810 err = getstr(&config->customrequest, nextarg, DENY_BLANK);
2811 break;
2812 case C_SPEED_TIME: /* --speed-time */
2813 /* low speed time */
2814 err = str2unum(&config->low_speed_time, nextarg);
2815 if(!err && !config->low_speed_limit)
2816 config->low_speed_limit = 1;
2817 break;
2818 case C_SPEED_LIMIT: /* --speed-limit */
2819 /* low speed limit */
2820 err = str2unum(&config->low_speed_limit, nextarg);
2821 if(!err && !config->low_speed_time)
2822 config->low_speed_time = 30;
2823 break;
2824 case C_PARALLEL: /* --parallel */
2825 global->parallel = toggle;
2826 break;
2827 case C_PARALLEL_MAX: { /* --parallel-max */
2828 long val;
2829 err = str2unum(&val, nextarg);
2830 if(err)
2831 break;
2832 if(val > MAX_PARALLEL)
2833 global->parallel_max = MAX_PARALLEL;
2834 else if(val < 1)
2835 global->parallel_max = PARALLEL_DEFAULT;
2836 else
2837 global->parallel_max = (unsigned short)val;
2838 break;
2839 }
2840 case C_PARALLEL_IMMEDIATE: /* --parallel-immediate */
2841 global->parallel_connect = toggle;
2842 break;
2843 case C_TIME_COND: /* --time-cond */
2844 err = parse_time_cond(global, config, nextarg);
2845 break;
2846 case C_MPTCP: /* --mptcp */
2847 config->mptcp = TRUE;
2848 break;
2849 default: /* unknown flag */
2850 err = PARAM_OPTION_UNKNOWN;
2851 break;
2852 }
2853 a = NULL;
2854 ++nopts; /* processed one option from `flag` input, loop for more */
2855 } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
2856
2857 error:
2858 if(nextalloc)
2859 free(nextarg);
2860 return err;
2861 }
2862
parse_args(struct GlobalConfig * global,int argc,argv_item_t argv[])2863 ParameterError parse_args(struct GlobalConfig *global, int argc,
2864 argv_item_t argv[])
2865 {
2866 int i;
2867 bool stillflags;
2868 char *orig_opt = NULL;
2869 ParameterError result = PARAM_OK;
2870 struct OperationConfig *config = global->first;
2871
2872 for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
2873 orig_opt = curlx_convert_tchar_to_UTF8(argv[i]);
2874 if(!orig_opt)
2875 return PARAM_NO_MEM;
2876
2877 if(stillflags && ('-' == orig_opt[0])) {
2878 bool passarg;
2879
2880 if(!strcmp("--", orig_opt))
2881 /* This indicates the end of the flags and thus enables the
2882 following (URL) argument to start with -. */
2883 stillflags = FALSE;
2884 else {
2885 char *nextarg = NULL;
2886 if(i < (argc - 1)) {
2887 nextarg = curlx_convert_tchar_to_UTF8(argv[i + 1]);
2888 if(!nextarg) {
2889 curlx_unicodefree(orig_opt);
2890 return PARAM_NO_MEM;
2891 }
2892 }
2893
2894 result = getparameter(orig_opt, nextarg, argv[i + 1], &passarg,
2895 global, config);
2896
2897 curlx_unicodefree(nextarg);
2898 config = global->last;
2899 if(result == PARAM_NEXT_OPERATION) {
2900 /* Reset result as PARAM_NEXT_OPERATION is only used here and not
2901 returned from this function */
2902 result = PARAM_OK;
2903
2904 if(config->url_list && config->url_list->url) {
2905 /* Allocate the next config */
2906 config->next = malloc(sizeof(struct OperationConfig));
2907 if(config->next) {
2908 /* Initialise the newly created config */
2909 config_init(config->next);
2910
2911 /* Set the global config pointer */
2912 config->next->global = global;
2913
2914 /* Update the last config pointer */
2915 global->last = config->next;
2916
2917 /* Move onto the new config */
2918 config->next->prev = config;
2919 config = config->next;
2920 }
2921 else
2922 result = PARAM_NO_MEM;
2923 }
2924 else {
2925 errorf(global, "missing URL before --next");
2926 result = PARAM_BAD_USE;
2927 }
2928 }
2929 else if(!result && passarg)
2930 i++; /* we are supposed to skip this */
2931 }
2932 }
2933 else {
2934 bool used;
2935
2936 /* Just add the URL please */
2937 result = getparameter("--url", orig_opt, argv[i], &used, global, config);
2938 }
2939
2940 if(!result)
2941 curlx_unicodefree(orig_opt);
2942 }
2943
2944 if(!result && config->content_disposition) {
2945 if(config->resume_from_current)
2946 result = PARAM_CONTDISP_RESUME_FROM;
2947 }
2948
2949 if(result && result != PARAM_HELP_REQUESTED &&
2950 result != PARAM_MANUAL_REQUESTED &&
2951 result != PARAM_VERSION_INFO_REQUESTED &&
2952 result != PARAM_ENGINES_REQUESTED &&
2953 result != PARAM_CA_EMBED_REQUESTED) {
2954 const char *reason = param2text(result);
2955
2956 if(orig_opt && strcmp(":", orig_opt))
2957 helpf(tool_stderr, "option %s: %s", orig_opt, reason);
2958 else
2959 helpf(tool_stderr, "%s", reason);
2960 }
2961
2962 curlx_unicodefree(orig_opt);
2963 return result;
2964 }
2965