xref: /third_party/curl/src/tool_paramhlp.c (revision 13498266)
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#define ENABLE_CURLX_PRINTF
29/* use our own printf() functions */
30#include "curlx.h"
31
32#include "tool_cfgable.h"
33#include "tool_getparam.h"
34#include "tool_getpass.h"
35#include "tool_msgs.h"
36#include "tool_paramhlp.h"
37#include "tool_libinfo.h"
38#include "tool_util.h"
39#include "tool_version.h"
40#include "dynbuf.h"
41
42#include "memdebug.h" /* keep this as LAST include */
43
44struct getout *new_getout(struct OperationConfig *config)
45{
46  struct getout *node = calloc(1, sizeof(struct getout));
47  struct getout *last = config->url_last;
48  if(node) {
49    static int outnum = 0;
50
51    /* append this new node last in the list */
52    if(last)
53      last->next = node;
54    else
55      config->url_list = node; /* first node */
56
57    /* move the last pointer */
58    config->url_last = node;
59
60    node->flags = config->default_node_flags;
61    node->num = outnum++;
62  }
63  return node;
64}
65
66#define MAX_FILE2STRING (256*1024*1024) /* big enough ? */
67
68ParameterError file2string(char **bufp, FILE *file)
69{
70  struct curlx_dynbuf dyn;
71  DEBUGASSERT(MAX_FILE2STRING < INT_MAX); /* needs to fit in an int later */
72  curlx_dyn_init(&dyn, MAX_FILE2STRING);
73  if(file) {
74    char buffer[256];
75
76    while(fgets(buffer, sizeof(buffer), file)) {
77      char *ptr = strchr(buffer, '\r');
78      if(ptr)
79        *ptr = '\0';
80      ptr = strchr(buffer, '\n');
81      if(ptr)
82        *ptr = '\0';
83      if(curlx_dyn_add(&dyn, buffer))
84        return PARAM_NO_MEM;
85    }
86  }
87  *bufp = curlx_dyn_ptr(&dyn);
88  return PARAM_OK;
89}
90
91ParameterError file2memory(char **bufp, size_t *size, FILE *file)
92{
93  if(file) {
94    size_t nread;
95    struct curlx_dynbuf dyn;
96    /* The size needs to fit in an int later */
97    DEBUGASSERT(MAX_FILE2MEMORY < INT_MAX);
98    curlx_dyn_init(&dyn, MAX_FILE2MEMORY);
99    do {
100      char buffer[4096];
101      nread = fread(buffer, 1, sizeof(buffer), file);
102      if(ferror(file)) {
103        curlx_dyn_free(&dyn);
104        *size = 0;
105        *bufp = NULL;
106        return PARAM_READ_ERROR;
107      }
108      if(nread)
109        if(curlx_dyn_addn(&dyn, buffer, nread))
110          return PARAM_NO_MEM;
111    } while(!feof(file));
112    *size = curlx_dyn_len(&dyn);
113    *bufp = curlx_dyn_ptr(&dyn);
114  }
115  else {
116    *size = 0;
117    *bufp = NULL;
118  }
119  return PARAM_OK;
120}
121
122/*
123 * Parse the string and write the long in the given address. Return PARAM_OK
124 * on success, otherwise a parameter specific error enum.
125 *
126 * Since this function gets called with the 'nextarg' pointer from within the
127 * getparameter a lot, we must check it for NULL before accessing the str
128 * data.
129 */
130static ParameterError getnum(long *val, const char *str, int base)
131{
132  if(str) {
133    char *endptr = NULL;
134    long num;
135    if(!str[0])
136      return PARAM_BLANK_STRING;
137    errno = 0;
138    num = strtol(str, &endptr, base);
139    if(errno == ERANGE)
140      return PARAM_NUMBER_TOO_LARGE;
141    if((endptr != str) && (*endptr == '\0')) {
142      *val = num;
143      return PARAM_OK;  /* Ok */
144    }
145  }
146  return PARAM_BAD_NUMERIC; /* badness */
147}
148
149ParameterError str2num(long *val, const char *str)
150{
151  return getnum(val, str, 10);
152}
153
154ParameterError oct2nummax(long *val, const char *str, long max)
155{
156  ParameterError result = getnum(val, str, 8);
157  if(result != PARAM_OK)
158    return result;
159  else if(*val > max)
160    return PARAM_NUMBER_TOO_LARGE;
161  else if(*val < 0)
162    return PARAM_NEGATIVE_NUMERIC;
163
164  return PARAM_OK;
165}
166
167/*
168 * Parse the string and write the long in the given address. Return PARAM_OK
169 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
170 *
171 * Since this function gets called with the 'nextarg' pointer from within the
172 * getparameter a lot, we must check it for NULL before accessing the str
173 * data.
174 */
175
176ParameterError str2unum(long *val, const char *str)
177{
178  ParameterError result = getnum(val, str, 10);
179  if(result != PARAM_OK)
180    return result;
181  if(*val < 0)
182    return PARAM_NEGATIVE_NUMERIC;
183
184  return PARAM_OK;
185}
186
187/*
188 * Parse the string and write the long in the given address if it is below the
189 * maximum allowed value. Return PARAM_OK on success, otherwise a parameter
190 * error enum. ONLY ACCEPTS POSITIVE NUMBERS!
191 *
192 * Since this function gets called with the 'nextarg' pointer from within the
193 * getparameter a lot, we must check it for NULL before accessing the str
194 * data.
195 */
196
197ParameterError str2unummax(long *val, const char *str, long max)
198{
199  ParameterError result = str2unum(val, str);
200  if(result != PARAM_OK)
201    return result;
202  if(*val > max)
203    return PARAM_NUMBER_TOO_LARGE;
204
205  return PARAM_OK;
206}
207
208
209/*
210 * Parse the string and write the double in the given address. Return PARAM_OK
211 * on success, otherwise a parameter specific error enum.
212 *
213 * The 'max' argument is the maximum value allowed, as the numbers are often
214 * multiplied when later used.
215 *
216 * Since this function gets called with the 'nextarg' pointer from within the
217 * getparameter a lot, we must check it for NULL before accessing the str
218 * data.
219 */
220
221static ParameterError str2double(double *val, const char *str, double max)
222{
223  if(str) {
224    char *endptr;
225    double num;
226    errno = 0;
227    num = strtod(str, &endptr);
228    if(errno == ERANGE)
229      return PARAM_NUMBER_TOO_LARGE;
230    if(num > max) {
231      /* too large */
232      return PARAM_NUMBER_TOO_LARGE;
233    }
234    if((endptr != str) && (endptr == str + strlen(str))) {
235      *val = num;
236      return PARAM_OK;  /* Ok */
237    }
238  }
239  return PARAM_BAD_NUMERIC; /* badness */
240}
241
242/*
243 * Parse the string as seconds with decimals, and write the number of
244 * milliseconds that corresponds in the given address. Return PARAM_OK on
245 * success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
246 *
247 * The 'max' argument is the maximum value allowed, as the numbers are often
248 * multiplied when later used.
249 *
250 * Since this function gets called with the 'nextarg' pointer from within the
251 * getparameter a lot, we must check it for NULL before accessing the str
252 * data.
253 */
254
255ParameterError secs2ms(long *valp, const char *str)
256{
257  double value;
258  ParameterError result = str2double(&value, str, (double)LONG_MAX/1000);
259  if(result != PARAM_OK)
260    return result;
261  if(value < 0)
262    return PARAM_NEGATIVE_NUMERIC;
263
264  *valp = (long)(value*1000);
265  return PARAM_OK;
266}
267
268/*
269 * Implement protocol sets in null-terminated array of protocol name pointers.
270 */
271
272/* Return index of prototype token in set, card(set) if not found.
273   Can be called with proto == NULL to get card(set). */
274static size_t protoset_index(const char * const *protoset, const char *proto)
275{
276  const char * const *p = protoset;
277
278  DEBUGASSERT(proto == proto_token(proto));     /* Ensure it is tokenized. */
279
280  for(; *p; p++)
281    if(proto == *p)
282      break;
283  return p - protoset;
284}
285
286/* Include protocol token in set. */
287static void protoset_set(const char **protoset, const char *proto)
288{
289  if(proto) {
290    size_t n = protoset_index(protoset, proto);
291
292    if(!protoset[n]) {
293      DEBUGASSERT(n < proto_count);
294      protoset[n] = proto;
295      protoset[n + 1] = NULL;
296    }
297  }
298}
299
300/* Exclude protocol token from set. */
301static void protoset_clear(const char **protoset, const char *proto)
302{
303  if(proto) {
304    size_t n = protoset_index(protoset, proto);
305
306    if(protoset[n]) {
307      size_t m = protoset_index(protoset, NULL) - 1;
308
309      protoset[n] = protoset[m];
310      protoset[m] = NULL;
311    }
312  }
313}
314
315/*
316 * Parse the string and provide an allocated libcurl compatible protocol
317 * string output. Return non-zero on failure, zero on success.
318 *
319 * The string is a list of protocols
320 *
321 * Since this function gets called with the 'nextarg' pointer from within the
322 * getparameter a lot, we must check it for NULL before accessing the str
323 * data.
324 */
325
326#define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */
327
328ParameterError proto2num(struct OperationConfig *config,
329                         const char * const *val, char **ostr, const char *str)
330{
331  char *buffer;
332  const char *sep = ",";
333  char *token;
334  const char **protoset;
335  struct curlx_dynbuf obuf;
336  size_t proto;
337  CURLcode result;
338
339  curlx_dyn_init(&obuf, MAX_PROTOSTRING);
340
341  if(!str)
342    return PARAM_OPTION_AMBIGUOUS;
343
344  buffer = strdup(str); /* because strtok corrupts it */
345  if(!buffer)
346    return PARAM_NO_MEM;
347
348  protoset = malloc((proto_count + 1) * sizeof(*protoset));
349  if(!protoset) {
350    free(buffer);
351    return PARAM_NO_MEM;
352  }
353
354  /* Preset protocol set with default values. */
355  protoset[0] = NULL;
356  for(; *val; val++) {
357    const char *p = proto_token(*val);
358
359    if(p)
360      protoset_set(protoset, p);
361  }
362
363  /* Allow strtok() here since this isn't used threaded */
364  /* !checksrc! disable BANNEDFUNC 2 */
365  for(token = strtok(buffer, sep);
366      token;
367      token = strtok(NULL, sep)) {
368    enum e_action { allow, deny, set } action = allow;
369
370    /* Process token modifiers */
371    while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */
372      switch(*token++) {
373      case '=':
374        action = set;
375        break;
376      case '-':
377        action = deny;
378        break;
379      case '+':
380        action = allow;
381        break;
382      default: /* Includes case of terminating NULL */
383        free(buffer);
384        free((char *) protoset);
385        return PARAM_BAD_USE;
386      }
387    }
388
389    if(curl_strequal(token, "all")) {
390      switch(action) {
391      case deny:
392        protoset[0] = NULL;
393        break;
394      case allow:
395      case set:
396        memcpy((char *) protoset,
397               built_in_protos, (proto_count + 1) * sizeof(*protoset));
398        break;
399      }
400    }
401    else {
402      const char *p = proto_token(token);
403
404      if(p)
405        switch(action) {
406        case deny:
407          protoset_clear(protoset, p);
408          break;
409        case set:
410          protoset[0] = NULL;
411          FALLTHROUGH();
412        case allow:
413          protoset_set(protoset, p);
414          break;
415        }
416      else { /* unknown protocol */
417        /* If they have specified only this protocol, we say treat it as
418           if no protocols are allowed */
419        if(action == set)
420          protoset[0] = NULL;
421        warnf(config->global, "unrecognized protocol '%s'", token);
422      }
423    }
424  }
425  free(buffer);
426
427  /* We need the protocols in alphabetic order for CI tests requirements. */
428  qsort((char *) protoset, protoset_index(protoset, NULL), sizeof(*protoset),
429        struplocompare4sort);
430
431  result = curlx_dyn_addn(&obuf, "", 0);
432  for(proto = 0; protoset[proto] && !result; proto++)
433    result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]);
434  free((char *) protoset);
435  curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1);
436  free(*ostr);
437  *ostr = curlx_dyn_ptr(&obuf);
438
439  return *ostr ? PARAM_OK : PARAM_NO_MEM;
440}
441
442/**
443 * Check if the given string is a protocol supported by libcurl
444 *
445 * @param str  the protocol name
446 * @return PARAM_OK  protocol supported
447 * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL  protocol not supported
448 * @return PARAM_REQUIRES_PARAMETER   missing parameter
449 */
450ParameterError check_protocol(const char *str)
451{
452  if(!str)
453    return PARAM_REQUIRES_PARAMETER;
454
455  if(proto_token(str))
456    return PARAM_OK;
457  return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
458}
459
460/**
461 * Parses the given string looking for an offset (which may be a
462 * larger-than-integer value). The offset CANNOT be negative!
463 *
464 * @param val  the offset to populate
465 * @param str  the buffer containing the offset
466 * @return PARAM_OK if successful, a parameter specific error enum if failure.
467 */
468ParameterError str2offset(curl_off_t *val, const char *str)
469{
470  char *endptr;
471  if(str[0] == '-')
472    /* offsets aren't negative, this indicates weird input */
473    return PARAM_NEGATIVE_NUMERIC;
474
475#if(SIZEOF_CURL_OFF_T > SIZEOF_LONG)
476  {
477    CURLofft offt = curlx_strtoofft(str, &endptr, 10, val);
478    if(CURL_OFFT_FLOW == offt)
479      return PARAM_NUMBER_TOO_LARGE;
480    else if(CURL_OFFT_INVAL == offt)
481      return PARAM_BAD_NUMERIC;
482  }
483#else
484  errno = 0;
485  *val = strtol(str, &endptr, 0);
486  if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
487    return PARAM_NUMBER_TOO_LARGE;
488#endif
489  if((endptr != str) && (endptr == str + strlen(str)))
490    return PARAM_OK;
491
492  return PARAM_BAD_NUMERIC;
493}
494
495#define MAX_USERPWDLENGTH (100*1024)
496static CURLcode checkpasswd(const char *kind, /* for what purpose */
497                            const size_t i,   /* operation index */
498                            const bool last,  /* TRUE if last operation */
499                            char **userpwd)   /* pointer to allocated string */
500{
501  char *psep;
502  char *osep;
503
504  if(!*userpwd)
505    return CURLE_OK;
506
507  /* Attempt to find the password separator */
508  psep = strchr(*userpwd, ':');
509
510  /* Attempt to find the options separator */
511  osep = strchr(*userpwd, ';');
512
513  if(!psep && **userpwd != ';') {
514    /* no password present, prompt for one */
515    char passwd[2048] = "";
516    char prompt[256];
517    struct curlx_dynbuf dyn;
518
519    curlx_dyn_init(&dyn, MAX_USERPWDLENGTH);
520    if(osep)
521      *osep = '\0';
522
523    /* build a nice-looking prompt */
524    if(!i && last)
525      curlx_msnprintf(prompt, sizeof(prompt),
526                      "Enter %s password for user '%s':",
527                      kind, *userpwd);
528    else
529      curlx_msnprintf(prompt, sizeof(prompt),
530                      "Enter %s password for user '%s' on URL #%zu:",
531                      kind, *userpwd, i + 1);
532
533    /* get password */
534    getpass_r(prompt, passwd, sizeof(passwd));
535    if(osep)
536      *osep = ';';
537
538    if(curlx_dyn_addf(&dyn, "%s:%s", *userpwd, passwd))
539      return CURLE_OUT_OF_MEMORY;
540
541    /* return the new string */
542    free(*userpwd);
543    *userpwd = curlx_dyn_ptr(&dyn);
544  }
545
546  return CURLE_OK;
547}
548
549ParameterError add2list(struct curl_slist **list, const char *ptr)
550{
551  struct curl_slist *newlist = curl_slist_append(*list, ptr);
552  if(newlist)
553    *list = newlist;
554  else
555    return PARAM_NO_MEM;
556
557  return PARAM_OK;
558}
559
560int ftpfilemethod(struct OperationConfig *config, const char *str)
561{
562  if(curl_strequal("singlecwd", str))
563    return CURLFTPMETHOD_SINGLECWD;
564  if(curl_strequal("nocwd", str))
565    return CURLFTPMETHOD_NOCWD;
566  if(curl_strequal("multicwd", str))
567    return CURLFTPMETHOD_MULTICWD;
568
569  warnf(config->global, "unrecognized ftp file method '%s', using default",
570        str);
571
572  return CURLFTPMETHOD_MULTICWD;
573}
574
575int ftpcccmethod(struct OperationConfig *config, const char *str)
576{
577  if(curl_strequal("passive", str))
578    return CURLFTPSSL_CCC_PASSIVE;
579  if(curl_strequal("active", str))
580    return CURLFTPSSL_CCC_ACTIVE;
581
582  warnf(config->global, "unrecognized ftp CCC method '%s', using default",
583        str);
584
585  return CURLFTPSSL_CCC_PASSIVE;
586}
587
588long delegation(struct OperationConfig *config, const char *str)
589{
590  if(curl_strequal("none", str))
591    return CURLGSSAPI_DELEGATION_NONE;
592  if(curl_strequal("policy", str))
593    return CURLGSSAPI_DELEGATION_POLICY_FLAG;
594  if(curl_strequal("always", str))
595    return CURLGSSAPI_DELEGATION_FLAG;
596
597  warnf(config->global, "unrecognized delegation method '%s', using none",
598        str);
599
600  return CURLGSSAPI_DELEGATION_NONE;
601}
602
603/*
604 * my_useragent: returns allocated string with default user agent
605 */
606static char *my_useragent(void)
607{
608  return strdup(CURL_NAME "/" CURL_VERSION);
609}
610
611#define isheadersep(x) ((((x)==':') || ((x)==';')))
612
613/*
614 * inlist() returns true if the given 'checkfor' header is present in the
615 * header list.
616 */
617static bool inlist(const struct curl_slist *head,
618                   const char *checkfor)
619{
620  size_t thislen = strlen(checkfor);
621  DEBUGASSERT(thislen);
622  DEBUGASSERT(checkfor[thislen-1] != ':');
623
624  for(; head; head = head->next) {
625    if(curl_strnequal(head->data, checkfor, thislen) &&
626       isheadersep(head->data[thislen]) )
627      return TRUE;
628  }
629
630  return FALSE;
631}
632
633CURLcode get_args(struct OperationConfig *config, const size_t i)
634{
635  CURLcode result = CURLE_OK;
636  bool last = (config->next ? FALSE : TRUE);
637
638  if(config->jsoned) {
639    ParameterError err = PARAM_OK;
640    /* --json also implies json Content-Type: and Accept: headers - if
641       they are not set with -H */
642    if(!inlist(config->headers, "Content-Type"))
643      err = add2list(&config->headers, "Content-Type: application/json");
644    if(!err && !inlist(config->headers, "Accept"))
645      err = add2list(&config->headers, "Accept: application/json");
646    if(err)
647      return CURLE_OUT_OF_MEMORY;
648  }
649
650  /* Check we have a password for the given host user */
651  if(config->userpwd && !config->oauth_bearer) {
652    result = checkpasswd("host", i, last, &config->userpwd);
653    if(result)
654      return result;
655  }
656
657  /* Check we have a password for the given proxy user */
658  if(config->proxyuserpwd) {
659    result = checkpasswd("proxy", i, last, &config->proxyuserpwd);
660    if(result)
661      return result;
662  }
663
664  /* Check we have a user agent */
665  if(!config->useragent) {
666    config->useragent = my_useragent();
667    if(!config->useragent) {
668      errorf(config->global, "out of memory");
669      result = CURLE_OUT_OF_MEMORY;
670    }
671  }
672
673  return result;
674}
675
676/*
677 * Parse the string and modify ssl_version in the val argument. Return PARAM_OK
678 * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
679 *
680 * Since this function gets called with the 'nextarg' pointer from within the
681 * getparameter a lot, we must check it for NULL before accessing the str
682 * data.
683 */
684
685ParameterError str2tls_max(long *val, const char *str)
686{
687   static struct s_tls_max {
688    const char *tls_max_str;
689    long tls_max;
690  } const tls_max_array[] = {
691    { "default", CURL_SSLVERSION_MAX_DEFAULT },
692    { "1.0",     CURL_SSLVERSION_MAX_TLSv1_0 },
693    { "1.1",     CURL_SSLVERSION_MAX_TLSv1_1 },
694    { "1.2",     CURL_SSLVERSION_MAX_TLSv1_2 },
695    { "1.3",     CURL_SSLVERSION_MAX_TLSv1_3 }
696  };
697  size_t i = 0;
698  if(!str)
699    return PARAM_REQUIRES_PARAMETER;
700  for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) {
701    if(!strcmp(str, tls_max_array[i].tls_max_str)) {
702      *val = tls_max_array[i].tls_max;
703      return PARAM_OK;
704    }
705  }
706  return PARAM_BAD_USE;
707}
708