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