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#ifndef CURL_DISABLE_LIBCURL_OPTION 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_easysrc.h" 34#include "tool_setopt.h" 35#include "tool_msgs.h" 36#include "dynbuf.h" 37 38#include "memdebug.h" /* keep this as LAST include */ 39 40/* Lookup tables for converting setopt values back to symbols */ 41/* For enums, values may be in any order. */ 42/* For bit masks, put combinations first, then single bits, */ 43/* and finally any "NONE" value. */ 44 45#define NV(e) {#e, e} 46#define NV1(e, v) {#e, (v)} 47#define NVEND {NULL, 0} /* sentinel to mark end of list */ 48 49const struct NameValue setopt_nv_CURLPROXY[] = { 50 NV(CURLPROXY_HTTP), 51 NV(CURLPROXY_HTTP_1_0), 52 NV(CURLPROXY_HTTPS), 53 NV(CURLPROXY_SOCKS4), 54 NV(CURLPROXY_SOCKS5), 55 NV(CURLPROXY_SOCKS4A), 56 NV(CURLPROXY_SOCKS5_HOSTNAME), 57 NVEND, 58}; 59 60const struct NameValue setopt_nv_CURL_SOCKS_PROXY[] = { 61 NV(CURLPROXY_SOCKS4), 62 NV(CURLPROXY_SOCKS5), 63 NV(CURLPROXY_SOCKS4A), 64 NV(CURLPROXY_SOCKS5_HOSTNAME), 65 NVEND, 66}; 67 68const struct NameValueUnsigned setopt_nv_CURLHSTS[] = { 69 NV(CURLHSTS_ENABLE), 70 NVEND, 71}; 72 73const struct NameValueUnsigned setopt_nv_CURLAUTH[] = { 74 NV(CURLAUTH_ANY), /* combination */ 75 NV(CURLAUTH_ANYSAFE), /* combination */ 76 NV(CURLAUTH_BASIC), 77 NV(CURLAUTH_DIGEST), 78 NV(CURLAUTH_GSSNEGOTIATE), 79 NV(CURLAUTH_NTLM), 80 NV(CURLAUTH_DIGEST_IE), 81 NV(CURLAUTH_NTLM_WB), 82 NV(CURLAUTH_ONLY), 83 NV(CURLAUTH_NONE), 84 NVEND, 85}; 86 87const struct NameValue setopt_nv_CURL_HTTP_VERSION[] = { 88 NV(CURL_HTTP_VERSION_NONE), 89 NV(CURL_HTTP_VERSION_1_0), 90 NV(CURL_HTTP_VERSION_1_1), 91 NV(CURL_HTTP_VERSION_2_0), 92 NV(CURL_HTTP_VERSION_2TLS), 93 NV(CURL_HTTP_VERSION_3), 94 NV(CURL_HTTP_VERSION_3ONLY), 95 NVEND, 96}; 97 98const struct NameValue setopt_nv_CURL_SSLVERSION[] = { 99 NV(CURL_SSLVERSION_DEFAULT), 100 NV(CURL_SSLVERSION_TLSv1), 101 NV(CURL_SSLVERSION_SSLv2), 102 NV(CURL_SSLVERSION_SSLv3), 103 NV(CURL_SSLVERSION_TLSv1_0), 104 NV(CURL_SSLVERSION_TLSv1_1), 105 NV(CURL_SSLVERSION_TLSv1_2), 106 NV(CURL_SSLVERSION_TLSv1_3), 107 NVEND, 108}; 109 110const struct NameValue setopt_nv_CURL_TIMECOND[] = { 111 NV(CURL_TIMECOND_IFMODSINCE), 112 NV(CURL_TIMECOND_IFUNMODSINCE), 113 NV(CURL_TIMECOND_LASTMOD), 114 NV(CURL_TIMECOND_NONE), 115 NVEND, 116}; 117 118const struct NameValue setopt_nv_CURLFTPSSL_CCC[] = { 119 NV(CURLFTPSSL_CCC_NONE), 120 NV(CURLFTPSSL_CCC_PASSIVE), 121 NV(CURLFTPSSL_CCC_ACTIVE), 122 NVEND, 123}; 124 125const struct NameValue setopt_nv_CURLUSESSL[] = { 126 NV(CURLUSESSL_NONE), 127 NV(CURLUSESSL_TRY), 128 NV(CURLUSESSL_CONTROL), 129 NV(CURLUSESSL_ALL), 130 NVEND, 131}; 132 133const struct NameValueUnsigned setopt_nv_CURLSSLOPT[] = { 134 NV(CURLSSLOPT_ALLOW_BEAST), 135 NV(CURLSSLOPT_NO_REVOKE), 136 NV(CURLSSLOPT_NO_PARTIALCHAIN), 137 NV(CURLSSLOPT_REVOKE_BEST_EFFORT), 138 NV(CURLSSLOPT_NATIVE_CA), 139 NV(CURLSSLOPT_AUTO_CLIENT_CERT), 140 NVEND, 141}; 142 143const struct NameValue setopt_nv_CURL_NETRC[] = { 144 NV(CURL_NETRC_IGNORED), 145 NV(CURL_NETRC_OPTIONAL), 146 NV(CURL_NETRC_REQUIRED), 147 NVEND, 148}; 149 150/* These options have non-zero default values. */ 151static const struct NameValue setopt_nv_CURLNONZERODEFAULTS[] = { 152 NV1(CURLOPT_SSL_VERIFYPEER, 1), 153 NV1(CURLOPT_SSL_VERIFYHOST, 1), 154 NV1(CURLOPT_SSL_ENABLE_NPN, 1), 155 NV1(CURLOPT_SSL_ENABLE_ALPN, 1), 156 NV1(CURLOPT_TCP_NODELAY, 1), 157 NV1(CURLOPT_PROXY_SSL_VERIFYPEER, 1), 158 NV1(CURLOPT_PROXY_SSL_VERIFYHOST, 1), 159 NV1(CURLOPT_SOCKS5_AUTH, 1), 160 NVEND 161}; 162 163/* Format and add code; jump to nomem on malloc error */ 164#define ADD(args) do { \ 165 ret = easysrc_add args; \ 166 if(ret) \ 167 goto nomem; \ 168} while(0) 169#define ADDF(args) do { \ 170 ret = easysrc_addf args; \ 171 if(ret) \ 172 goto nomem; \ 173} while(0) 174#define NULL_CHECK(p) do { \ 175 if(!p) { \ 176 ret = CURLE_OUT_OF_MEMORY; \ 177 goto nomem; \ 178 } \ 179} while(0) 180 181#define DECL0(s) ADD((&easysrc_decl, s)) 182#define DECL1(f,a) ADDF((&easysrc_decl, f,a)) 183 184#define DATA0(s) ADD((&easysrc_data, s)) 185#define DATA1(f,a) ADDF((&easysrc_data, f,a)) 186#define DATA2(f,a,b) ADDF((&easysrc_data, f,a,b)) 187#define DATA3(f,a,b,c) ADDF((&easysrc_data, f,a,b,c)) 188 189#define CODE0(s) ADD((&easysrc_code, s)) 190#define CODE1(f,a) ADDF((&easysrc_code, f,a)) 191#define CODE2(f,a,b) ADDF((&easysrc_code, f,a,b)) 192#define CODE3(f,a,b,c) ADDF((&easysrc_code, f,a,b,c)) 193 194#define CLEAN0(s) ADD((&easysrc_clean, s)) 195#define CLEAN1(f,a) ADDF((&easysrc_clean, f,a)) 196 197#define REM0(s) ADD((&easysrc_toohard, s)) 198#define REM1(f,a) ADDF((&easysrc_toohard, f,a)) 199#define REM3(f,a,b,c) ADDF((&easysrc_toohard, f,a,b,c)) 200 201/* Escape string to C string syntax. Return NULL if out of memory. 202 * Is this correct for those wacky EBCDIC guys? */ 203 204#define MAX_STRING_LENGTH_OUTPUT 2000 205#define ZERO_TERMINATED -1 206 207static char *c_escape(const char *str, curl_off_t len) 208{ 209 const char *s; 210 unsigned int cutoff = 0; 211 CURLcode result; 212 struct curlx_dynbuf escaped; 213 214 curlx_dyn_init(&escaped, 4 * MAX_STRING_LENGTH_OUTPUT + 3); 215 216 if(len == ZERO_TERMINATED) 217 len = strlen(str); 218 219 if(len > MAX_STRING_LENGTH_OUTPUT) { 220 /* cap ridiculously long strings */ 221 len = MAX_STRING_LENGTH_OUTPUT; 222 cutoff = 3; 223 } 224 225 result = curlx_dyn_addn(&escaped, STRCONST("")); 226 for(s = str; !result && len; s++, len--) { 227 /* escape question marks as well, to prevent generating accidental 228 trigraphs */ 229 static const char from[] = "\t\r\n?\"\\"; 230 static const char to[] = "\\t\\r\\n\\?\\\"\\\\"; 231 const char *p = strchr(from, *s); 232 233 if(!p && ISPRINT(*s)) 234 continue; 235 236 result = curlx_dyn_addn(&escaped, str, s - str); 237 str = s + 1; 238 239 if(!result) { 240 if(p && *p) 241 result = curlx_dyn_addn(&escaped, to + 2 * (p - from), 2); 242 else { 243 result = curlx_dyn_addf(&escaped, 244 /* Octal escape to avoid >2 digit hex. */ 245 (len > 1 && ISXDIGIT(s[1])) ? 246 "\\%03o" : "\\x%02x", 247 (unsigned int) *(unsigned char *) s); 248 } 249 } 250 } 251 252 if(!result) 253 result = curlx_dyn_addn(&escaped, str, s - str); 254 255 if(!result) 256 (void) !curlx_dyn_addn(&escaped, "...", cutoff); 257 258 return curlx_dyn_ptr(&escaped); 259} 260 261/* setopt wrapper for enum types */ 262CURLcode tool_setopt_enum(CURL *curl, struct GlobalConfig *config, 263 const char *name, CURLoption tag, 264 const struct NameValue *nvlist, long lval) 265{ 266 CURLcode ret = CURLE_OK; 267 bool skip = FALSE; 268 269 ret = curl_easy_setopt(curl, tag, lval); 270 if(!lval) 271 skip = TRUE; 272 273 if(config->libcurl && !skip && !ret) { 274 /* we only use this for real if --libcurl was used */ 275 const struct NameValue *nv = NULL; 276 for(nv = nvlist; nv->name; nv++) { 277 if(nv->value == lval) 278 break; /* found it */ 279 } 280 if(!nv->name) { 281 /* If no definition was found, output an explicit value. 282 * This could happen if new values are defined and used 283 * but the NameValue list is not updated. */ 284 CODE2("curl_easy_setopt(hnd, %s, %ldL);", name, lval); 285 } 286 else { 287 CODE2("curl_easy_setopt(hnd, %s, (long)%s);", name, nv->name); 288 } 289 } 290 291#ifdef DEBUGBUILD 292 if(ret) 293 warnf(config, "option %s returned error (%d)", name, (int)ret); 294#endif 295nomem: 296 return ret; 297} 298 299/* setopt wrapper for bitmasks */ 300CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config, 301 const char *name, CURLoption tag, 302 const struct NameValueUnsigned *nvlist, 303 long lval) 304{ 305 CURLcode ret = CURLE_OK; 306 bool skip = FALSE; 307 308 ret = curl_easy_setopt(curl, tag, lval); 309 if(!lval) 310 skip = TRUE; 311 312 if(config->libcurl && !skip && !ret) { 313 /* we only use this for real if --libcurl was used */ 314 char preamble[80]; 315 unsigned long rest = (unsigned long)lval; 316 const struct NameValueUnsigned *nv = NULL; 317 msnprintf(preamble, sizeof(preamble), 318 "curl_easy_setopt(hnd, %s, ", name); 319 for(nv = nvlist; nv->name; nv++) { 320 if((nv->value & ~ rest) == 0) { 321 /* all value flags contained in rest */ 322 rest &= ~ nv->value; /* remove bits handled here */ 323 CODE3("%s(long)%s%s", 324 preamble, nv->name, rest ? " |" : ");"); 325 if(!rest) 326 break; /* handled them all */ 327 /* replace with all spaces for continuation line */ 328 msnprintf(preamble, sizeof(preamble), "%*s", (int)strlen(preamble), 329 ""); 330 } 331 } 332 /* If any bits have no definition, output an explicit value. 333 * This could happen if new bits are defined and used 334 * but the NameValue list is not updated. */ 335 if(rest) 336 CODE2("%s%luUL);", preamble, rest); 337 } 338 339nomem: 340 return ret; 341} 342 343/* Generate code for a struct curl_slist. */ 344static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno) 345{ 346 CURLcode ret = CURLE_OK; 347 char *escaped = NULL; 348 349 /* May need several slist variables, so invent name */ 350 *slistno = ++easysrc_slist_count; 351 352 DECL1("struct curl_slist *slist%d;", *slistno); 353 DATA1("slist%d = NULL;", *slistno); 354 CLEAN1("curl_slist_free_all(slist%d);", *slistno); 355 CLEAN1("slist%d = NULL;", *slistno); 356 for(; slist; slist = slist->next) { 357 Curl_safefree(escaped); 358 escaped = c_escape(slist->data, ZERO_TERMINATED); 359 if(!escaped) 360 return CURLE_OUT_OF_MEMORY; 361 DATA3("slist%d = curl_slist_append(slist%d, \"%s\");", 362 *slistno, *slistno, escaped); 363 } 364 365nomem: 366 Curl_safefree(escaped); 367 return ret; 368} 369 370static CURLcode libcurl_generate_mime(CURL *curl, 371 struct GlobalConfig *config, 372 struct tool_mime *toolmime, 373 int *mimeno); /* Forward. */ 374 375/* Wrapper to generate source code for a mime part. */ 376static CURLcode libcurl_generate_mime_part(CURL *curl, 377 struct GlobalConfig *config, 378 struct tool_mime *part, 379 int mimeno) 380{ 381 CURLcode ret = CURLE_OK; 382 int submimeno = 0; 383 char *escaped = NULL; 384 const char *data = NULL; 385 const char *filename = part->filename; 386 387 /* Parts are linked in reverse order. */ 388 if(part->prev) { 389 ret = libcurl_generate_mime_part(curl, config, part->prev, mimeno); 390 if(ret) 391 return ret; 392 } 393 394 /* Create the part. */ 395 CODE2("part%d = curl_mime_addpart(mime%d);", mimeno, mimeno); 396 397 switch(part->kind) { 398 case TOOLMIME_PARTS: 399 ret = libcurl_generate_mime(curl, config, part, &submimeno); 400 if(!ret) { 401 CODE2("curl_mime_subparts(part%d, mime%d);", mimeno, submimeno); 402 CODE1("mime%d = NULL;", submimeno); /* Avoid freeing in CLEAN. */ 403 } 404 break; 405 406 case TOOLMIME_DATA: 407 data = part->data; 408 if(!ret) { 409 Curl_safefree(escaped); 410 escaped = c_escape(data, ZERO_TERMINATED); 411 NULL_CHECK(escaped); 412 CODE2("curl_mime_data(part%d, \"%s\", CURL_ZERO_TERMINATED);", 413 mimeno, escaped); 414 } 415 break; 416 417 case TOOLMIME_FILE: 418 case TOOLMIME_FILEDATA: 419 escaped = c_escape(part->data, ZERO_TERMINATED); 420 NULL_CHECK(escaped); 421 CODE2("curl_mime_filedata(part%d, \"%s\");", mimeno, escaped); 422 if(part->kind == TOOLMIME_FILEDATA && !filename) { 423 CODE1("curl_mime_filename(part%d, NULL);", mimeno); 424 } 425 break; 426 427 case TOOLMIME_STDIN: 428 if(!filename) 429 filename = "-"; 430 FALLTHROUGH(); 431 case TOOLMIME_STDINDATA: 432 /* Can only be reading stdin in the current context. */ 433 CODE1("curl_mime_data_cb(part%d, -1, (curl_read_callback) fread, \\", 434 mimeno); 435 CODE0(" (curl_seek_callback) fseek, NULL, stdin);"); 436 break; 437 default: 438 /* Other cases not possible in this context. */ 439 break; 440 } 441 442 if(!ret && part->encoder) { 443 Curl_safefree(escaped); 444 escaped = c_escape(part->encoder, ZERO_TERMINATED); 445 NULL_CHECK(escaped); 446 CODE2("curl_mime_encoder(part%d, \"%s\");", mimeno, escaped); 447 } 448 449 if(!ret && filename) { 450 Curl_safefree(escaped); 451 escaped = c_escape(filename, ZERO_TERMINATED); 452 NULL_CHECK(escaped); 453 CODE2("curl_mime_filename(part%d, \"%s\");", mimeno, escaped); 454 } 455 456 if(!ret && part->name) { 457 Curl_safefree(escaped); 458 escaped = c_escape(part->name, ZERO_TERMINATED); 459 NULL_CHECK(escaped); 460 CODE2("curl_mime_name(part%d, \"%s\");", mimeno, escaped); 461 } 462 463 if(!ret && part->type) { 464 Curl_safefree(escaped); 465 escaped = c_escape(part->type, ZERO_TERMINATED); 466 NULL_CHECK(escaped); 467 CODE2("curl_mime_type(part%d, \"%s\");", mimeno, escaped); 468 } 469 470 if(!ret && part->headers) { 471 int slistno; 472 473 ret = libcurl_generate_slist(part->headers, &slistno); 474 if(!ret) { 475 CODE2("curl_mime_headers(part%d, slist%d, 1);", mimeno, slistno); 476 CODE1("slist%d = NULL;", slistno); /* Prevent CLEANing. */ 477 } 478 } 479 480nomem: 481 Curl_safefree(escaped); 482 return ret; 483} 484 485/* Wrapper to generate source code for a mime structure. */ 486static CURLcode libcurl_generate_mime(CURL *curl, 487 struct GlobalConfig *config, 488 struct tool_mime *toolmime, 489 int *mimeno) 490{ 491 CURLcode ret = CURLE_OK; 492 493 /* May need several mime variables, so invent name. */ 494 *mimeno = ++easysrc_mime_count; 495 DECL1("curl_mime *mime%d;", *mimeno); 496 DATA1("mime%d = NULL;", *mimeno); 497 CODE1("mime%d = curl_mime_init(hnd);", *mimeno); 498 CLEAN1("curl_mime_free(mime%d);", *mimeno); 499 CLEAN1("mime%d = NULL;", *mimeno); 500 501 if(toolmime->subparts) { 502 DECL1("curl_mimepart *part%d;", *mimeno); 503 ret = libcurl_generate_mime_part(curl, config, 504 toolmime->subparts, *mimeno); 505 } 506 507nomem: 508 return ret; 509} 510 511/* setopt wrapper for CURLOPT_MIMEPOST */ 512CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config, 513 const char *name, CURLoption tag, 514 curl_mime *mimepost) 515{ 516 CURLcode ret = curl_easy_setopt(curl, tag, mimepost); 517 int mimeno = 0; 518 519 if(!ret && config->libcurl) { 520 ret = libcurl_generate_mime(curl, config, 521 config->current->mimeroot, &mimeno); 522 523 if(!ret) 524 CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, mimeno); 525 } 526 527nomem: 528 return ret; 529} 530 531/* setopt wrapper for curl_slist options */ 532CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config, 533 const char *name, CURLoption tag, 534 struct curl_slist *list) 535{ 536 CURLcode ret = CURLE_OK; 537 538 ret = curl_easy_setopt(curl, tag, list); 539 540 if(config->libcurl && list && !ret) { 541 int i; 542 543 ret = libcurl_generate_slist(list, &i); 544 if(!ret) 545 CODE2("curl_easy_setopt(hnd, %s, slist%d);", name, i); 546 } 547 548nomem: 549 return ret; 550} 551 552/* generic setopt wrapper for all other options. 553 * Some type information is encoded in the tag value. */ 554CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *global, 555 struct OperationConfig *config, 556 const char *name, CURLoption tag, ...) 557{ 558 va_list arg; 559 char buf[256]; 560 const char *value = NULL; 561 bool remark = FALSE; 562 bool skip = FALSE; 563 bool escape = FALSE; 564 char *escaped = NULL; 565 CURLcode ret = CURLE_OK; 566 567 va_start(arg, tag); 568 569 if(tag < CURLOPTTYPE_OBJECTPOINT) { 570 /* Value is expected to be a long */ 571 long lval = va_arg(arg, long); 572 long defval = 0L; 573 const struct NameValue *nv = NULL; 574 for(nv = setopt_nv_CURLNONZERODEFAULTS; nv->name; nv++) { 575 if(!strcmp(name, nv->name)) { 576 defval = nv->value; 577 break; /* found it */ 578 } 579 } 580 581 msnprintf(buf, sizeof(buf), "%ldL", lval); 582 value = buf; 583 ret = curl_easy_setopt(curl, tag, lval); 584 if(lval == defval) 585 skip = TRUE; 586 } 587 else if(tag < CURLOPTTYPE_OFF_T) { 588 /* Value is some sort of object pointer */ 589 void *pval = va_arg(arg, void *); 590 591 /* function pointers are never printable */ 592 if(tag >= CURLOPTTYPE_FUNCTIONPOINT) { 593 if(pval) { 594 value = "function pointer"; 595 remark = TRUE; 596 } 597 else 598 skip = TRUE; 599 } 600 601 else if(pval && str) { 602 value = (char *)pval; 603 escape = TRUE; 604 } 605 else if(pval) { 606 value = "object pointer"; 607 remark = TRUE; 608 } 609 else 610 skip = TRUE; 611 612 ret = curl_easy_setopt(curl, tag, pval); 613 614 } 615 else if(tag < CURLOPTTYPE_BLOB) { 616 /* Value is expected to be curl_off_t */ 617 curl_off_t oval = va_arg(arg, curl_off_t); 618 msnprintf(buf, sizeof(buf), 619 "(curl_off_t)%" CURL_FORMAT_CURL_OFF_T, oval); 620 value = buf; 621 ret = curl_easy_setopt(curl, tag, oval); 622 623 if(!oval) 624 skip = TRUE; 625 } 626 else { 627 /* Value is a blob */ 628 void *pblob = va_arg(arg, void *); 629 630 /* blobs are never printable */ 631 if(pblob) { 632 value = "blob pointer"; 633 remark = TRUE; 634 } 635 else 636 skip = TRUE; 637 638 ret = curl_easy_setopt(curl, tag, pblob); 639 } 640 641 va_end(arg); 642 643 if(global->libcurl && !skip && !ret) { 644 /* we only use this for real if --libcurl was used */ 645 646 if(remark) 647 REM3("%s was set to a%s %s", name, (*value == 'o' ? "n" : ""), value); 648 else { 649 if(escape) { 650 curl_off_t len = ZERO_TERMINATED; 651 if(tag == CURLOPT_POSTFIELDS) 652 len = curlx_dyn_len(&config->postdata); 653 escaped = c_escape(value, len); 654 NULL_CHECK(escaped); 655 CODE2("curl_easy_setopt(hnd, %s, \"%s\");", name, escaped); 656 } 657 else 658 CODE2("curl_easy_setopt(hnd, %s, %s);", name, value); 659 } 660 } 661 662nomem: 663 Curl_safefree(escaped); 664 return ret; 665} 666 667#else /* CURL_DISABLE_LIBCURL_OPTION */ 668 669#endif /* CURL_DISABLE_LIBCURL_OPTION */ 670