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