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 }