123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
- #include "tool_setup.h"
- #include "strcase.h"
- #define ENABLE_CURLX_PRINTF
- /* use our own printf() functions */
- #include "curlx.h"
- #include "tool_cfgable.h"
- #include "tool_getparam.h"
- #include "tool_getpass.h"
- #include "tool_msgs.h"
- #include "tool_paramhlp.h"
- #include "tool_libinfo.h"
- #include "tool_util.h"
- #include "tool_version.h"
- #include "dynbuf.h"
- #include "memdebug.h" /* keep this as LAST include */
- struct getout *new_getout(struct OperationConfig *config)
- {
- struct getout *node = calloc(1, sizeof(struct getout));
- struct getout *last = config->url_last;
- if(node) {
- static int outnum = 0;
- /* append this new node last in the list */
- if(last)
- last->next = node;
- else
- config->url_list = node; /* first node */
- /* move the last pointer */
- config->url_last = node;
- node->flags = config->default_node_flags;
- node->num = outnum++;
- }
- return node;
- }
- #define ISCRLF(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
- /* memcrlf() has two modes. Both operate on a given memory area with
- a specified size.
- countcrlf FALSE - return number of bytes from the start that DO NOT include
- any CR or LF or NULL
- countcrlf TRUE - return number of bytes from the start that are ONLY CR or
- LF or NULL.
- */
- static size_t memcrlf(char *orig,
- bool countcrlf, /* TRUE if we count CRLF, FALSE
- if we count non-CRLF */
- size_t max)
- {
- char *ptr;
- size_t total = max;
- for(ptr = orig; max; max--, ptr++) {
- bool crlf = ISCRLF(*ptr);
- if(countcrlf ^ crlf)
- return ptr - orig;
- }
- return total; /* no delimiter found */
- }
- #define MAX_FILE2STRING (256*1024*1024) /* big enough ? */
- ParameterError file2string(char **bufp, FILE *file)
- {
- struct curlx_dynbuf dyn;
- DEBUGASSERT(MAX_FILE2STRING < INT_MAX); /* needs to fit in an int later */
- curlx_dyn_init(&dyn, MAX_FILE2STRING);
- if(file) {
- do {
- char buffer[4096];
- char *ptr;
- size_t nread = fread(buffer, 1, sizeof(buffer), file);
- if(ferror(file)) {
- curlx_dyn_free(&dyn);
- *bufp = NULL;
- return PARAM_READ_ERROR;
- }
- ptr = buffer;
- while(nread) {
- size_t nlen = memcrlf(ptr, FALSE, nread);
- if(curlx_dyn_addn(&dyn, ptr, nlen))
- return PARAM_NO_MEM;
- nread -= nlen;
- if(nread) {
- ptr += nlen;
- nlen = memcrlf(ptr, TRUE, nread);
- ptr += nlen;
- nread -= nlen;
- }
- }
- } while(!feof(file));
- }
- *bufp = curlx_dyn_ptr(&dyn);
- return PARAM_OK;
- }
- ParameterError file2memory(char **bufp, size_t *size, FILE *file)
- {
- if(file) {
- size_t nread;
- struct curlx_dynbuf dyn;
- /* The size needs to fit in an int later */
- DEBUGASSERT(MAX_FILE2MEMORY < INT_MAX);
- curlx_dyn_init(&dyn, MAX_FILE2MEMORY);
- do {
- char buffer[4096];
- nread = fread(buffer, 1, sizeof(buffer), file);
- if(ferror(file)) {
- curlx_dyn_free(&dyn);
- *size = 0;
- *bufp = NULL;
- return PARAM_READ_ERROR;
- }
- if(nread)
- if(curlx_dyn_addn(&dyn, buffer, nread))
- return PARAM_NO_MEM;
- } while(!feof(file));
- *size = curlx_dyn_len(&dyn);
- *bufp = curlx_dyn_ptr(&dyn);
- }
- else {
- *size = 0;
- *bufp = NULL;
- }
- return PARAM_OK;
- }
- /*
- * Parse the string and write the long in the given address. Return PARAM_OK
- * on success, otherwise a parameter specific error enum.
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- static ParameterError getnum(long *val, const char *str, int base)
- {
- if(str) {
- char *endptr = NULL;
- long num;
- if(!str[0])
- return PARAM_BLANK_STRING;
- errno = 0;
- num = strtol(str, &endptr, base);
- if(errno == ERANGE)
- return PARAM_NUMBER_TOO_LARGE;
- if((endptr != str) && (*endptr == '\0')) {
- *val = num;
- return PARAM_OK; /* Ok */
- }
- }
- return PARAM_BAD_NUMERIC; /* badness */
- }
- ParameterError str2num(long *val, const char *str)
- {
- return getnum(val, str, 10);
- }
- ParameterError oct2nummax(long *val, const char *str, long max)
- {
- ParameterError result = getnum(val, str, 8);
- if(result != PARAM_OK)
- return result;
- else if(*val > max)
- return PARAM_NUMBER_TOO_LARGE;
- else if(*val < 0)
- return PARAM_NEGATIVE_NUMERIC;
- return PARAM_OK;
- }
- /*
- * Parse the string and write the long in the given address. Return PARAM_OK
- * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- ParameterError str2unum(long *val, const char *str)
- {
- ParameterError result = getnum(val, str, 10);
- if(result != PARAM_OK)
- return result;
- if(*val < 0)
- return PARAM_NEGATIVE_NUMERIC;
- return PARAM_OK;
- }
- /*
- * Parse the string and write the long in the given address if it is below the
- * maximum allowed value. Return PARAM_OK on success, otherwise a parameter
- * error enum. ONLY ACCEPTS POSITIVE NUMBERS!
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- ParameterError str2unummax(long *val, const char *str, long max)
- {
- ParameterError result = str2unum(val, str);
- if(result != PARAM_OK)
- return result;
- if(*val > max)
- return PARAM_NUMBER_TOO_LARGE;
- return PARAM_OK;
- }
- /*
- * Parse the string and write the double in the given address. Return PARAM_OK
- * on success, otherwise a parameter specific error enum.
- *
- * The 'max' argument is the maximum value allowed, as the numbers are often
- * multiplied when later used.
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- static ParameterError str2double(double *val, const char *str, double max)
- {
- if(str) {
- char *endptr;
- double num;
- errno = 0;
- num = strtod(str, &endptr);
- if(errno == ERANGE)
- return PARAM_NUMBER_TOO_LARGE;
- if(num > max) {
- /* too large */
- return PARAM_NUMBER_TOO_LARGE;
- }
- if((endptr != str) && (endptr == str + strlen(str))) {
- *val = num;
- return PARAM_OK; /* Ok */
- }
- }
- return PARAM_BAD_NUMERIC; /* badness */
- }
- /*
- * Parse the string as seconds with decimals, and write the number of
- * milliseconds that corresponds in the given address. Return PARAM_OK on
- * success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
- *
- * The 'max' argument is the maximum value allowed, as the numbers are often
- * multiplied when later used.
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- ParameterError secs2ms(long *valp, const char *str)
- {
- double value;
- ParameterError result = str2double(&value, str, (double)LONG_MAX/1000);
- if(result != PARAM_OK)
- return result;
- if(value < 0)
- return PARAM_NEGATIVE_NUMERIC;
- *valp = (long)(value*1000);
- return PARAM_OK;
- }
- /*
- * Implement protocol sets in null-terminated array of protocol name pointers.
- */
- /* Return index of prototype token in set, card(set) if not found.
- Can be called with proto == NULL to get card(set). */
- static size_t protoset_index(const char * const *protoset, const char *proto)
- {
- const char * const *p = protoset;
- DEBUGASSERT(proto == proto_token(proto)); /* Ensure it is tokenized. */
- for(; *p; p++)
- if(proto == *p)
- break;
- return p - protoset;
- }
- /* Include protocol token in set. */
- static void protoset_set(const char **protoset, const char *proto)
- {
- if(proto) {
- size_t n = protoset_index(protoset, proto);
- if(!protoset[n]) {
- DEBUGASSERT(n < proto_count);
- protoset[n] = proto;
- protoset[n + 1] = NULL;
- }
- }
- }
- /* Exclude protocol token from set. */
- static void protoset_clear(const char **protoset, const char *proto)
- {
- if(proto) {
- size_t n = protoset_index(protoset, proto);
- if(protoset[n]) {
- size_t m = protoset_index(protoset, NULL) - 1;
- protoset[n] = protoset[m];
- protoset[m] = NULL;
- }
- }
- }
- /*
- * Parse the string and provide an allocated libcurl compatible protocol
- * string output. Return non-zero on failure, zero on success.
- *
- * The string is a list of protocols
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- #define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */
- ParameterError proto2num(struct OperationConfig *config,
- const char * const *val, char **ostr, const char *str)
- {
- char *buffer;
- const char *sep = ",";
- char *token;
- const char **protoset;
- struct curlx_dynbuf obuf;
- size_t proto;
- CURLcode result;
- curlx_dyn_init(&obuf, MAX_PROTOSTRING);
- if(!str)
- return PARAM_OPTION_AMBIGUOUS;
- buffer = strdup(str); /* because strtok corrupts it */
- if(!buffer)
- return PARAM_NO_MEM;
- protoset = malloc((proto_count + 1) * sizeof(*protoset));
- if(!protoset) {
- free(buffer);
- return PARAM_NO_MEM;
- }
- /* Preset protocol set with default values. */
- protoset[0] = NULL;
- for(; *val; val++) {
- const char *p = proto_token(*val);
- if(p)
- protoset_set(protoset, p);
- }
- /* Allow strtok() here since this is not used threaded */
- /* !checksrc! disable BANNEDFUNC 2 */
- for(token = strtok(buffer, sep);
- token;
- token = strtok(NULL, sep)) {
- enum e_action { allow, deny, set } action = allow;
- /* Process token modifiers */
- while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */
- switch(*token++) {
- case '=':
- action = set;
- break;
- case '-':
- action = deny;
- break;
- case '+':
- action = allow;
- break;
- default: /* Includes case of terminating NULL */
- free(buffer);
- free((char *) protoset);
- return PARAM_BAD_USE;
- }
- }
- if(curl_strequal(token, "all")) {
- switch(action) {
- case deny:
- protoset[0] = NULL;
- break;
- case allow:
- case set:
- memcpy((char *) protoset,
- built_in_protos, (proto_count + 1) * sizeof(*protoset));
- break;
- }
- }
- else {
- const char *p = proto_token(token);
- if(p)
- switch(action) {
- case deny:
- protoset_clear(protoset, p);
- break;
- case set:
- protoset[0] = NULL;
- FALLTHROUGH();
- case allow:
- protoset_set(protoset, p);
- break;
- }
- else { /* unknown protocol */
- /* If they have specified only this protocol, we say treat it as
- if no protocols are allowed */
- if(action == set)
- protoset[0] = NULL;
- warnf(config->global, "unrecognized protocol '%s'", token);
- }
- }
- }
- free(buffer);
- /* We need the protocols in alphabetic order for CI tests requirements. */
- qsort((char *) protoset, protoset_index(protoset, NULL), sizeof(*protoset),
- struplocompare4sort);
- result = curlx_dyn_addn(&obuf, "", 0);
- for(proto = 0; protoset[proto] && !result; proto++)
- result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]);
- free((char *) protoset);
- curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1);
- free(*ostr);
- *ostr = curlx_dyn_ptr(&obuf);
- return *ostr ? PARAM_OK : PARAM_NO_MEM;
- }
- /**
- * Check if the given string is a protocol supported by libcurl
- *
- * @param str the protocol name
- * @return PARAM_OK protocol supported
- * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL protocol not supported
- * @return PARAM_REQUIRES_PARAMETER missing parameter
- */
- ParameterError check_protocol(const char *str)
- {
- if(!str)
- return PARAM_REQUIRES_PARAMETER;
- if(proto_token(str))
- return PARAM_OK;
- return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
- }
- /**
- * Parses the given string looking for an offset (which may be a
- * larger-than-integer value). The offset CANNOT be negative!
- *
- * @param val the offset to populate
- * @param str the buffer containing the offset
- * @return PARAM_OK if successful, a parameter specific error enum if failure.
- */
- ParameterError str2offset(curl_off_t *val, const char *str)
- {
- char *endptr;
- if(str[0] == '-')
- /* offsets are not negative, this indicates weird input */
- return PARAM_NEGATIVE_NUMERIC;
- #if(SIZEOF_CURL_OFF_T > SIZEOF_LONG)
- {
- CURLofft offt = curlx_strtoofft(str, &endptr, 10, val);
- if(CURL_OFFT_FLOW == offt)
- return PARAM_NUMBER_TOO_LARGE;
- else if(CURL_OFFT_INVAL == offt)
- return PARAM_BAD_NUMERIC;
- }
- #else
- errno = 0;
- *val = strtol(str, &endptr, 0);
- if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
- return PARAM_NUMBER_TOO_LARGE;
- #endif
- if((endptr != str) && (endptr == str + strlen(str)))
- return PARAM_OK;
- return PARAM_BAD_NUMERIC;
- }
- #define MAX_USERPWDLENGTH (100*1024)
- static CURLcode checkpasswd(const char *kind, /* for what purpose */
- const size_t i, /* operation index */
- const bool last, /* TRUE if last operation */
- char **userpwd) /* pointer to allocated string */
- {
- char *psep;
- char *osep;
- if(!*userpwd)
- return CURLE_OK;
- /* Attempt to find the password separator */
- psep = strchr(*userpwd, ':');
- /* Attempt to find the options separator */
- osep = strchr(*userpwd, ';');
- if(!psep && **userpwd != ';') {
- /* no password present, prompt for one */
- char passwd[2048] = "";
- char prompt[256];
- struct curlx_dynbuf dyn;
- curlx_dyn_init(&dyn, MAX_USERPWDLENGTH);
- if(osep)
- *osep = '\0';
- /* build a nice-looking prompt */
- if(!i && last)
- curlx_msnprintf(prompt, sizeof(prompt),
- "Enter %s password for user '%s':",
- kind, *userpwd);
- else
- curlx_msnprintf(prompt, sizeof(prompt),
- "Enter %s password for user '%s' on URL #%zu:",
- kind, *userpwd, i + 1);
- /* get password */
- getpass_r(prompt, passwd, sizeof(passwd));
- if(osep)
- *osep = ';';
- if(curlx_dyn_addf(&dyn, "%s:%s", *userpwd, passwd))
- return CURLE_OUT_OF_MEMORY;
- /* return the new string */
- free(*userpwd);
- *userpwd = curlx_dyn_ptr(&dyn);
- }
- return CURLE_OK;
- }
- ParameterError add2list(struct curl_slist **list, const char *ptr)
- {
- struct curl_slist *newlist = curl_slist_append(*list, ptr);
- if(newlist)
- *list = newlist;
- else
- return PARAM_NO_MEM;
- return PARAM_OK;
- }
- int ftpfilemethod(struct OperationConfig *config, const char *str)
- {
- if(curl_strequal("singlecwd", str))
- return CURLFTPMETHOD_SINGLECWD;
- if(curl_strequal("nocwd", str))
- return CURLFTPMETHOD_NOCWD;
- if(curl_strequal("multicwd", str))
- return CURLFTPMETHOD_MULTICWD;
- warnf(config->global, "unrecognized ftp file method '%s', using default",
- str);
- return CURLFTPMETHOD_MULTICWD;
- }
- int ftpcccmethod(struct OperationConfig *config, const char *str)
- {
- if(curl_strequal("passive", str))
- return CURLFTPSSL_CCC_PASSIVE;
- if(curl_strequal("active", str))
- return CURLFTPSSL_CCC_ACTIVE;
- warnf(config->global, "unrecognized ftp CCC method '%s', using default",
- str);
- return CURLFTPSSL_CCC_PASSIVE;
- }
- long delegation(struct OperationConfig *config, const char *str)
- {
- if(curl_strequal("none", str))
- return CURLGSSAPI_DELEGATION_NONE;
- if(curl_strequal("policy", str))
- return CURLGSSAPI_DELEGATION_POLICY_FLAG;
- if(curl_strequal("always", str))
- return CURLGSSAPI_DELEGATION_FLAG;
- warnf(config->global, "unrecognized delegation method '%s', using none",
- str);
- return CURLGSSAPI_DELEGATION_NONE;
- }
- /*
- * my_useragent: returns allocated string with default user agent
- */
- static char *my_useragent(void)
- {
- return strdup(CURL_NAME "/" CURL_VERSION);
- }
- #define isheadersep(x) ((((x)==':') || ((x)==';')))
- /*
- * inlist() returns true if the given 'checkfor' header is present in the
- * header list.
- */
- static bool inlist(const struct curl_slist *head,
- const char *checkfor)
- {
- size_t thislen = strlen(checkfor);
- DEBUGASSERT(thislen);
- DEBUGASSERT(checkfor[thislen-1] != ':');
- for(; head; head = head->next) {
- if(curl_strnequal(head->data, checkfor, thislen) &&
- isheadersep(head->data[thislen]) )
- return TRUE;
- }
- return FALSE;
- }
- CURLcode get_args(struct OperationConfig *config, const size_t i)
- {
- CURLcode result = CURLE_OK;
- bool last = (config->next ? FALSE : TRUE);
- if(config->jsoned) {
- ParameterError err = PARAM_OK;
- /* --json also implies json Content-Type: and Accept: headers - if
- they are not set with -H */
- if(!inlist(config->headers, "Content-Type"))
- err = add2list(&config->headers, "Content-Type: application/json");
- if(!err && !inlist(config->headers, "Accept"))
- err = add2list(&config->headers, "Accept: application/json");
- if(err)
- return CURLE_OUT_OF_MEMORY;
- }
- /* Check we have a password for the given host user */
- if(config->userpwd && !config->oauth_bearer) {
- result = checkpasswd("host", i, last, &config->userpwd);
- if(result)
- return result;
- }
- /* Check we have a password for the given proxy user */
- if(config->proxyuserpwd) {
- result = checkpasswd("proxy", i, last, &config->proxyuserpwd);
- if(result)
- return result;
- }
- /* Check we have a user agent */
- if(!config->useragent) {
- config->useragent = my_useragent();
- if(!config->useragent) {
- errorf(config->global, "out of memory");
- result = CURLE_OUT_OF_MEMORY;
- }
- }
- return result;
- }
- /*
- * Parse the string and modify ssl_version in the val argument. Return PARAM_OK
- * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
- ParameterError str2tls_max(long *val, const char *str)
- {
- static struct s_tls_max {
- const char *tls_max_str;
- long tls_max;
- } const tls_max_array[] = {
- { "default", CURL_SSLVERSION_MAX_DEFAULT },
- { "1.0", CURL_SSLVERSION_MAX_TLSv1_0 },
- { "1.1", CURL_SSLVERSION_MAX_TLSv1_1 },
- { "1.2", CURL_SSLVERSION_MAX_TLSv1_2 },
- { "1.3", CURL_SSLVERSION_MAX_TLSv1_3 }
- };
- size_t i = 0;
- if(!str)
- return PARAM_REQUIRES_PARAMETER;
- for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) {
- if(!strcmp(str, tls_max_array[i].tls_max_str)) {
- *val = tls_max_array[i].tls_max;
- return PARAM_OK;
- }
- }
- return PARAM_BAD_USE;
- }
|