quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

tool_paramhlp.c (20451B)


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