| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 1998 - 2021, Daniel Stenberg, <[email protected]>, 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. |
| * |
| ***************************************************************************/ |
| #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_homedir.h" |
| #include "tool_msgs.h" |
| #include "tool_paramhlp.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 MAX_FILE2STRING (256*1024*1024) /* big enough ? */ |
| |
| ParameterError file2string(char **bufp, FILE *file) |
| { |
| struct curlx_dynbuf dyn; |
| curlx_dyn_init(&dyn, MAX_FILE2STRING); |
| if(file) { |
| char buffer[256]; |
| |
| while(fgets(buffer, sizeof(buffer), file)) { |
| char *ptr = strchr(buffer, '\r'); |
| if(ptr) |
| *ptr = '\0'; |
| ptr = strchr(buffer, '\n'); |
| if(ptr) |
| *ptr = '\0'; |
| if(curlx_dyn_add(&dyn, buffer)) |
| return PARAM_NO_MEM; |
| } |
| } |
| *bufp = curlx_dyn_ptr(&dyn); |
| return PARAM_OK; |
| } |
| |
| #define MAX_FILE2MEMORY (1024*1024*1024) /* big enough ? */ |
| |
| ParameterError file2memory(char **bufp, size_t *size, FILE *file) |
| { |
| if(file) { |
| size_t nread; |
| struct curlx_dynbuf dyn; |
| curlx_dyn_init(&dyn, MAX_FILE2MEMORY); |
| do { |
| char buffer[4096]; |
| nread = fread(buffer, 1, sizeof(buffer), file); |
| if(nread) |
| if(curlx_dyn_addn(&dyn, buffer, nread)) |
| return PARAM_NO_MEM; |
| } while(nread); |
| *size = curlx_dyn_len(&dyn); |
| *bufp = curlx_dyn_ptr(&dyn); |
| } |
| else { |
| *size = 0; |
| *bufp = NULL; |
| } |
| return PARAM_OK; |
| } |
| |
| void cleanarg(char *str) |
| { |
| #ifdef HAVE_WRITABLE_ARGV |
| /* now that GetStr has copied the contents of nextarg, wipe the next |
| * argument out so that the username:password isn't displayed in the |
| * system process list */ |
| if(str) { |
| size_t len = strlen(str); |
| memset(str, ' ', len); |
| } |
| #else |
| (void)str; |
| #endif |
| } |
| |
| /* |
| * 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; |
| errno = 0; |
| num = strtol(str, &endptr, base); |
| if(errno == ERANGE) |
| return PARAM_NUMBER_TOO_LARGE; |
| if((endptr != str) && (endptr == str + strlen(str))) { |
| *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, long 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 and write the double 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 str2udouble(double *valp, const char *str, long max) |
| { |
| double value; |
| ParameterError result = str2double(&value, str, max); |
| if(result != PARAM_OK) |
| return result; |
| if(value < 0) |
| return PARAM_NEGATIVE_NUMERIC; |
| |
| *valp = value; |
| return PARAM_OK; |
| } |
| |
| /* |
| * Parse the string and modify the long in the given address. 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. |
| */ |
| |
| long proto2num(struct OperationConfig *config, long *val, const char *str) |
| { |
| char *buffer; |
| const char *sep = ","; |
| char *token; |
| |
| static struct sprotos { |
| const char *name; |
| long bit; |
| } const protos[] = { |
| { "all", CURLPROTO_ALL }, |
| { "http", CURLPROTO_HTTP }, |
| { "https", CURLPROTO_HTTPS }, |
| { "ftp", CURLPROTO_FTP }, |
| { "ftps", CURLPROTO_FTPS }, |
| { "scp", CURLPROTO_SCP }, |
| { "sftp", CURLPROTO_SFTP }, |
| { "telnet", CURLPROTO_TELNET }, |
| { "ldap", CURLPROTO_LDAP }, |
| { "ldaps", CURLPROTO_LDAPS }, |
| { "dict", CURLPROTO_DICT }, |
| { "file", CURLPROTO_FILE }, |
| { "tftp", CURLPROTO_TFTP }, |
| { "imap", CURLPROTO_IMAP }, |
| { "imaps", CURLPROTO_IMAPS }, |
| { "pop3", CURLPROTO_POP3 }, |
| { "pop3s", CURLPROTO_POP3S }, |
| { "smtp", CURLPROTO_SMTP }, |
| { "smtps", CURLPROTO_SMTPS }, |
| { "rtsp", CURLPROTO_RTSP }, |
| { "gopher", CURLPROTO_GOPHER }, |
| { "smb", CURLPROTO_SMB }, |
| { "smbs", CURLPROTO_SMBS }, |
| { NULL, 0 } |
| }; |
| |
| if(!str) |
| return 1; |
| |
| buffer = strdup(str); /* because strtok corrupts it */ |
| if(!buffer) |
| return 1; |
| |
| /* Allow strtok() here since this isn't used threaded */ |
| /* !checksrc! disable BANNEDFUNC 2 */ |
| for(token = strtok(buffer, sep); |
| token; |
| token = strtok(NULL, sep)) { |
| enum e_action { allow, deny, set } action = allow; |
| |
| struct sprotos const *pp; |
| |
| /* 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 */ |
| Curl_safefree(buffer); |
| return 1; |
| } |
| } |
| |
| for(pp = protos; pp->name; pp++) { |
| if(curl_strequal(token, pp->name)) { |
| switch(action) { |
| case deny: |
| *val &= ~(pp->bit); |
| break; |
| case allow: |
| *val |= pp->bit; |
| break; |
| case set: |
| *val = pp->bit; |
| break; |
| } |
| break; |
| } |
| } |
| |
| if(!(pp->name)) { /* unknown protocol */ |
| /* If they have specified only this protocol, we say treat it as |
| if no protocols are allowed */ |
| if(action == set) |
| *val = 0; |
| warnf(config->global, "unrecognized protocol '%s'\n", token); |
| } |
| } |
| Curl_safefree(buffer); |
| return 0; |
| } |
| |
| /** |
| * 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 |
| */ |
| int check_protocol(const char *str) |
| { |
| const char * const *pp; |
| const curl_version_info_data *curlinfo = curl_version_info(CURLVERSION_NOW); |
| if(!str) |
| return PARAM_REQUIRES_PARAMETER; |
| for(pp = curlinfo->protocols; *pp; pp++) { |
| if(curl_strequal(*pp, 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 aren't negative, this indicates weird input */ |
| return PARAM_NEGATIVE_NUMERIC; |
| |
| #if(SIZEOF_CURL_OFF_T > SIZEOF_LONG) |
| { |
| CURLofft offt = curlx_strtoofft(str, &endptr, 0, 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\n", |
| 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\n", |
| 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\n", |
| 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); |
| } |
| |
| CURLcode get_args(struct OperationConfig *config, const size_t i) |
| { |
| CURLcode result = CURLE_OK; |
| bool last = (config->next ? FALSE : TRUE); |
| |
| /* 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\n"); |
| 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; |
| } |