blob: 6b534ce5df2293053b01142d0f10f1cccdc7d694 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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.haxx.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 ***************************************************************************/
22#include "tool_setup.h"
23
24#include "strcase.h"
25
26#define ENABLE_CURLX_PRINTF
27/* use our own printf() functions */
28#include "curlx.h"
29
30#include "tool_cfgable.h"
31#include "tool_getparam.h"
32#include "tool_getpass.h"
33#include "tool_homedir.h"
34#include "tool_msgs.h"
35#include "tool_paramhlp.h"
36#include "tool_version.h"
37
38#include "memdebug.h" /* keep this as LAST include */
39
40struct getout *new_getout(struct OperationConfig *config)
41{
42 struct getout *node = calloc(1, sizeof(struct getout));
43 struct getout *last = config->url_last;
44 if(node) {
45 /* append this new node last in the list */
46 if(last)
47 last->next = node;
48 else
49 config->url_list = node; /* first node */
50
51 /* move the last pointer */
52 config->url_last = node;
53
54 node->flags = config->default_node_flags;
55 }
56 return node;
57}
58
59ParameterError file2string(char **bufp, FILE *file)
60{
61 char buffer[256];
62 char *ptr;
63 char *string = NULL;
64 size_t stringlen = 0;
65 size_t buflen;
66
67 if(file) {
68 while(fgets(buffer, sizeof(buffer), file)) {
69 ptr = strchr(buffer, '\r');
70 if(ptr)
71 *ptr = '\0';
72 ptr = strchr(buffer, '\n');
73 if(ptr)
74 *ptr = '\0';
75 buflen = strlen(buffer);
76 ptr = realloc(string, stringlen+buflen+1);
77 if(!ptr) {
78 Curl_safefree(string);
79 return PARAM_NO_MEM;
80 }
81 string = ptr;
82 strcpy(string+stringlen, buffer);
83 stringlen += buflen;
84 }
85 }
86 *bufp = string;
87 return PARAM_OK;
88}
89
90ParameterError file2memory(char **bufp, size_t *size, FILE *file)
91{
92 char *newbuf;
93 char *buffer = NULL;
94 size_t alloc = 512;
95 size_t nused = 0;
96 size_t nread;
97
98 if(file) {
99 do {
100 if(!buffer || (alloc == nused)) {
101 /* size_t overflow detection for huge files */
102 if(alloc+1 > ((size_t)-1)/2) {
103 Curl_safefree(buffer);
104 return PARAM_NO_MEM;
105 }
106 alloc *= 2;
107 /* allocate an extra char, reserved space, for null termination */
108 newbuf = realloc(buffer, alloc+1);
109 if(!newbuf) {
110 Curl_safefree(buffer);
111 return PARAM_NO_MEM;
112 }
113 buffer = newbuf;
114 }
115 nread = fread(buffer+nused, 1, alloc-nused, file);
116 nused += nread;
117 } while(nread);
118 /* null terminate the buffer in case it's used as a string later */
119 buffer[nused] = '\0';
120 /* free trailing slack space, if possible */
121 if(alloc != nused) {
122 newbuf = realloc(buffer, nused+1);
123 if(!newbuf) {
124 Curl_safefree(buffer);
125 return PARAM_NO_MEM;
126 }
127 buffer = newbuf;
128 }
129 /* discard buffer if nothing was read */
130 if(!nused) {
131 Curl_safefree(buffer); /* no string */
132 }
133 }
134 *size = nused;
135 *bufp = buffer;
136 return PARAM_OK;
137}
138
139void cleanarg(char *str)
140{
141#ifdef HAVE_WRITABLE_ARGV
142 /* now that GetStr has copied the contents of nextarg, wipe the next
143 * argument out so that the username:password isn't displayed in the
144 * system process list */
145 if(str) {
146 size_t len = strlen(str);
147 memset(str, ' ', len);
148 }
149#else
150 (void)str;
151#endif
152}
153
154/*
155 * Parse the string and write the long in the given address. Return PARAM_OK
156 * on success, otherwise a parameter specific error enum.
157 *
158 * Since this function gets called with the 'nextarg' pointer from within the
159 * getparameter a lot, we must check it for NULL before accessing the str
160 * data.
161 */
162
163ParameterError str2num(long *val, const char *str)
164{
165 if(str) {
166 char *endptr;
167 long num = strtol(str, &endptr, 10);
168 if((endptr != str) && (endptr == str + strlen(str))) {
169 *val = num;
170 return PARAM_OK; /* Ok */
171 }
172 }
173 return PARAM_BAD_NUMERIC; /* badness */
174}
175
176/*
177 * Parse the string and write the long in the given address. Return PARAM_OK
178 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
179 *
180 * Since this function gets called with the 'nextarg' pointer from within the
181 * getparameter a lot, we must check it for NULL before accessing the str
182 * data.
183 */
184
185ParameterError str2unum(long *val, const char *str)
186{
187 ParameterError result = str2num(val, str);
188 if(result != PARAM_OK)
189 return result;
190 if(*val < 0)
191 return PARAM_NEGATIVE_NUMERIC;
192
193 return PARAM_OK;
194}
195
196/*
197 * Parse the string and write the double in the given address. Return PARAM_OK
198 * on success, otherwise a parameter specific error enum.
199 *
200 * Since this function gets called with the 'nextarg' pointer from within the
201 * getparameter a lot, we must check it for NULL before accessing the str
202 * data.
203 */
204
205ParameterError str2double(double *val, const char *str)
206{
207 if(str) {
208 char *endptr;
209 double num = strtod(str, &endptr);
210 if((endptr != str) && (endptr == str + strlen(str))) {
211 *val = num;
212 return PARAM_OK; /* Ok */
213 }
214 }
215 return PARAM_BAD_NUMERIC; /* badness */
216}
217
218/*
219 * Parse the string and write the double in the given address. Return PARAM_OK
220 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
221 *
222 * Since this function gets called with the 'nextarg' pointer from within the
223 * getparameter a lot, we must check it for NULL before accessing the str
224 * data.
225 */
226
227ParameterError str2udouble(double *val, const char *str)
228{
229 ParameterError result = str2double(val, str);
230 if(result != PARAM_OK)
231 return result;
232 if(*val < 0)
233 return PARAM_NEGATIVE_NUMERIC;
234
235 return PARAM_OK;
236}
237
238/*
239 * Parse the string and modify the long in the given address. Return
240 * non-zero on failure, zero on success.
241 *
242 * The string is a list of protocols
243 *
244 * Since this function gets called with the 'nextarg' pointer from within the
245 * getparameter a lot, we must check it for NULL before accessing the str
246 * data.
247 */
248
249long proto2num(struct OperationConfig *config, long *val, const char *str)
250{
251 char *buffer;
252 const char *sep = ",";
253 char *token;
254
255 static struct sprotos {
256 const char *name;
257 long bit;
258 } const protos[] = {
259 { "all", CURLPROTO_ALL },
260 { "http", CURLPROTO_HTTP },
261 { "https", CURLPROTO_HTTPS },
262 { "ftp", CURLPROTO_FTP },
263 { "ftps", CURLPROTO_FTPS },
264 { "scp", CURLPROTO_SCP },
265 { "sftp", CURLPROTO_SFTP },
266 { "telnet", CURLPROTO_TELNET },
267 { "ldap", CURLPROTO_LDAP },
268 { "ldaps", CURLPROTO_LDAPS },
269 { "dict", CURLPROTO_DICT },
270 { "file", CURLPROTO_FILE },
271 { "tftp", CURLPROTO_TFTP },
272 { "imap", CURLPROTO_IMAP },
273 { "imaps", CURLPROTO_IMAPS },
274 { "pop3", CURLPROTO_POP3 },
275 { "pop3s", CURLPROTO_POP3S },
276 { "smtp", CURLPROTO_SMTP },
277 { "smtps", CURLPROTO_SMTPS },
278 { "rtsp", CURLPROTO_RTSP },
279 { "gopher", CURLPROTO_GOPHER },
280 { "smb", CURLPROTO_SMB },
281 { "smbs", CURLPROTO_SMBS },
282 { NULL, 0 }
283 };
284
285 if(!str)
286 return 1;
287
288 buffer = strdup(str); /* because strtok corrupts it */
289 if(!buffer)
290 return 1;
291
292 /* Allow strtok() here since this isn't used threaded */
293 /* !checksrc! disable BANNEDFUNC 2 */
294 for(token = strtok(buffer, sep);
295 token;
296 token = strtok(NULL, sep)) {
297 enum e_action { allow, deny, set } action = allow;
298
299 struct sprotos const *pp;
300
301 /* Process token modifiers */
302 while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */
303 switch (*token++) {
304 case '=':
305 action = set;
306 break;
307 case '-':
308 action = deny;
309 break;
310 case '+':
311 action = allow;
312 break;
313 default: /* Includes case of terminating NULL */
314 Curl_safefree(buffer);
315 return 1;
316 }
317 }
318
319 for(pp=protos; pp->name; pp++) {
320 if(curl_strequal(token, pp->name)) {
321 switch(action) {
322 case deny:
323 *val &= ~(pp->bit);
324 break;
325 case allow:
326 *val |= pp->bit;
327 break;
328 case set:
329 *val = pp->bit;
330 break;
331 }
332 break;
333 }
334 }
335
336 if(!(pp->name)) { /* unknown protocol */
337 /* If they have specified only this protocol, we say treat it as
338 if no protocols are allowed */
339 if(action == set)
340 *val = 0;
341 warnf(config->global, "unrecognized protocol '%s'\n", token);
342 }
343 }
344 Curl_safefree(buffer);
345 return 0;
346}
347
348/**
349 * Check if the given string is a protocol supported by libcurl
350 *
351 * @param str the protocol name
352 * @return PARAM_OK protocol supported
353 * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL protocol not supported
354 * @return PARAM_REQUIRES_PARAMETER missing parameter
355 */
356int check_protocol(const char *str)
357{
358 const char * const *pp;
359 const curl_version_info_data *curlinfo = curl_version_info(CURLVERSION_NOW);
360 if(!str)
361 return PARAM_REQUIRES_PARAMETER;
362 for(pp = curlinfo->protocols; *pp; pp++) {
363 if(curl_strequal(*pp, str))
364 return PARAM_OK;
365 }
366 return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
367}
368
369/**
370 * Parses the given string looking for an offset (which may be a
371 * larger-than-integer value). The offset CANNOT be negative!
372 *
373 * @param val the offset to populate
374 * @param str the buffer containing the offset
375 * @return PARAM_OK if successful, a parameter specific error enum if failure.
376 */
377ParameterError str2offset(curl_off_t *val, const char *str)
378{
379 char *endptr;
380 if(str[0] == '-')
381 /* offsets aren't negative, this indicates weird input */
382 return PARAM_NEGATIVE_NUMERIC;
383
384#if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
385 *val = curlx_strtoofft(str, &endptr, 0);
386 if((*val == CURL_OFF_T_MAX || *val == CURL_OFF_T_MIN) && (ERRNO == ERANGE))
387 return PARAM_BAD_NUMERIC;
388#else
389 *val = strtol(str, &endptr, 0);
390 if((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
391 return PARAM_BAD_NUMERIC;
392#endif
393 if((endptr != str) && (endptr == str + strlen(str)))
394 return PARAM_OK;
395
396 return PARAM_BAD_NUMERIC;
397}
398
399static CURLcode checkpasswd(const char *kind, /* for what purpose */
400 const size_t i, /* operation index */
401 const bool last, /* TRUE if last operation */
402 char **userpwd) /* pointer to allocated string */
403{
404 char *psep;
405 char *osep;
406
407 if(!*userpwd)
408 return CURLE_OK;
409
410 /* Attempt to find the password separator */
411 psep = strchr(*userpwd, ':');
412
413 /* Attempt to find the options separator */
414 osep = strchr(*userpwd, ';');
415
416 if(!psep && **userpwd != ';') {
417 /* no password present, prompt for one */
418 char passwd[256] = "";
419 char prompt[256];
420 size_t passwdlen;
421 size_t userlen = strlen(*userpwd);
422 char *passptr;
423
424 if(osep)
425 *osep = '\0';
426
427 /* build a nice-looking prompt */
428 if(!i && last)
429 curlx_msnprintf(prompt, sizeof(prompt),
430 "Enter %s password for user '%s':",
431 kind, *userpwd);
432 else
433 curlx_msnprintf(prompt, sizeof(prompt),
434 "Enter %s password for user '%s' on URL #%"
435 CURL_FORMAT_CURL_OFF_TU ":",
436 kind, *userpwd, (curl_off_t) (i + 1));
437
438 /* get password */
439 getpass_r(prompt, passwd, sizeof(passwd));
440 passwdlen = strlen(passwd);
441
442 if(osep)
443 *osep = ';';
444
445 /* extend the allocated memory area to fit the password too */
446 passptr = realloc(*userpwd,
447 passwdlen + 1 + /* an extra for the colon */
448 userlen + 1); /* an extra for the zero */
449 if(!passptr)
450 return CURLE_OUT_OF_MEMORY;
451
452 /* append the password separated with a colon */
453 passptr[userlen] = ':';
454 memcpy(&passptr[userlen+1], passwd, passwdlen+1);
455 *userpwd = passptr;
456 }
457
458 return CURLE_OK;
459}
460
461ParameterError add2list(struct curl_slist **list, const char *ptr)
462{
463 struct curl_slist *newlist = curl_slist_append(*list, ptr);
464 if(newlist)
465 *list = newlist;
466 else
467 return PARAM_NO_MEM;
468
469 return PARAM_OK;
470}
471
472int ftpfilemethod(struct OperationConfig *config, const char *str)
473{
474 if(curl_strequal("singlecwd", str))
475 return CURLFTPMETHOD_SINGLECWD;
476 if(curl_strequal("nocwd", str))
477 return CURLFTPMETHOD_NOCWD;
478 if(curl_strequal("multicwd", str))
479 return CURLFTPMETHOD_MULTICWD;
480
481 warnf(config->global, "unrecognized ftp file method '%s', using default\n",
482 str);
483
484 return CURLFTPMETHOD_MULTICWD;
485}
486
487int ftpcccmethod(struct OperationConfig *config, const char *str)
488{
489 if(curl_strequal("passive", str))
490 return CURLFTPSSL_CCC_PASSIVE;
491 if(curl_strequal("active", str))
492 return CURLFTPSSL_CCC_ACTIVE;
493
494 warnf(config->global, "unrecognized ftp CCC method '%s', using default\n",
495 str);
496
497 return CURLFTPSSL_CCC_PASSIVE;
498}
499
500long delegation(struct OperationConfig *config, char *str)
501{
502 if(curl_strequal("none", str))
503 return CURLGSSAPI_DELEGATION_NONE;
504 if(curl_strequal("policy", str))
505 return CURLGSSAPI_DELEGATION_POLICY_FLAG;
506 if(curl_strequal("always", str))
507 return CURLGSSAPI_DELEGATION_FLAG;
508
509 warnf(config->global, "unrecognized delegation method '%s', using none\n",
510 str);
511
512 return CURLGSSAPI_DELEGATION_NONE;
513}
514
515/*
516 * my_useragent: returns allocated string with default user agent
517 */
518static char *my_useragent(void)
519{
520 return strdup(CURL_NAME "/" CURL_VERSION);
521}
522
523CURLcode get_args(struct OperationConfig *config, const size_t i)
524{
525 CURLcode result = CURLE_OK;
526 bool last = (config->next ? FALSE : TRUE);
527
528 /* Check we have a password for the given host user */
529 if(config->userpwd && !config->oauth_bearer) {
530 result = checkpasswd("host", i, last, &config->userpwd);
531 if(result)
532 return result;
533 }
534
535 /* Check we have a password for the given proxy user */
536 if(config->proxyuserpwd) {
537 result = checkpasswd("proxy", i, last, &config->proxyuserpwd);
538 if(result)
539 return result;
540 }
541
542 /* Check we have a user agent */
543 if(!config->useragent) {
544 config->useragent = my_useragent();
545 if(!config->useragent) {
546 helpf(config->global->errors, "out of memory\n");
547 result = CURLE_OUT_OF_MEMORY;
548 }
549 }
550
551 return result;
552}
553
554/*
555 * Parse the string and modify ssl_version in the val argument. Return PARAM_OK
556 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
557 *
558 * Since this function gets called with the 'nextarg' pointer from within the
559 * getparameter a lot, we must check it for NULL before accessing the str
560 * data.
561 */
562
563ParameterError str2tls_max(long *val, const char *str)
564{
565 static struct s_tls_max {
566 const char *tls_max_str;
567 long tls_max;
568 } const tls_max_array[] = {
569 { "default", CURL_SSLVERSION_MAX_DEFAULT },
570 { "1.0", CURL_SSLVERSION_MAX_TLSv1_0 },
571 { "1.1", CURL_SSLVERSION_MAX_TLSv1_1 },
572 { "1.2", CURL_SSLVERSION_MAX_TLSv1_2 },
573 { "1.3", CURL_SSLVERSION_MAX_TLSv1_3 }
574 };
575 size_t i = 0;
576 if(!str)
577 return PARAM_REQUIRES_PARAMETER;
578 for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) {
579 if(!strcmp(str, tls_max_array[i].tls_max_str)) {
580 *val = tls_max_array[i].tls_max;
581 return PARAM_OK;
582 }
583 }
584 return PARAM_BAD_USE;
585}