1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 22 23 /*** 24 25 26 RECEIVING COOKIE INFORMATION 27 ============================ 28 29 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 30 const char *file, struct CookieInfo *inc, bool newsession); 31 32 Inits a cookie struct to store data in a local file. This is always 33 called before any cookies are set. 34 35 struct Cookie *Curl_cookie_add(struct Curl_easy *data, 36 struct CookieInfo *c, bool httpheader, char *lineptr, 37 const char *domain, const char *path); 38 39 The 'lineptr' parameter is a full "Set-cookie:" line as 40 received from a server. 41 42 The function need to replace previously stored lines that this new 43 line supersedes. 44 45 It may remove lines that are expired. 46 47 It should return an indication of success/error. 48 49 50 SENDING COOKIE INFORMATION 51 ========================== 52 53 struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, 54 char *host, char *path, bool secure); 55 56 For a given host and path, return a linked list of cookies that 57 the client should send to the server if used now. The secure 58 boolean informs the cookie if a secure connection is achieved or 59 not. 60 61 It shall only return cookies that haven't expired. 62 63 64 Example set of cookies: 65 66 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure 67 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 68 domain=.fidelity.com; path=/ftgw; secure 69 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 70 domain=.fidelity.com; path=/; secure 71 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 72 domain=.fidelity.com; path=/; secure 73 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 74 domain=.fidelity.com; path=/; secure 75 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 76 domain=.fidelity.com; path=/; secure 77 Set-cookie: 78 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, 79 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure 80 ****/ 81 82 83 #include "curl_setup.h" 84 85 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) 86 87 #include "urldata.h" 88 #include "cookie.h" 89 #include "psl.h" 90 #include "strtok.h" 91 #include "sendf.h" 92 #include "slist.h" 93 #include "share.h" 94 #include "strtoofft.h" 95 #include "strcase.h" 96 #include "curl_memrchr.h" 97 #include "inet_pton.h" 98 99 /* The last 3 #include files should be in this order */ 100 #include "curl_printf.h" 101 #include "curl_memory.h" 102 #include "memdebug.h" 103 104 static void freecookie(struct Cookie *co) 105 { 106 free(co->expirestr); 107 free(co->domain); 108 free(co->path); 109 free(co->spath); 110 free(co->name); 111 free(co->value); 112 free(co->maxage); 113 free(co->version); 114 free(co); 115 } 116 117 static bool tailmatch(const char *cooke_domain, const char *hostname) 118 { 119 size_t cookie_domain_len = strlen(cooke_domain); 120 size_t hostname_len = strlen(hostname); 121 122 if(hostname_len < cookie_domain_len) 123 return FALSE; 124 125 if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len)) 126 return FALSE; 127 128 /* A lead char of cookie_domain is not '.'. 129 RFC6265 4.1.2.3. The Domain Attribute says: 130 For example, if the value of the Domain attribute is 131 "example.com", the user agent will include the cookie in the Cookie 132 header when making HTTP requests to example.com, www.example.com, and 133 www.corp.example.com. 134 */ 135 if(hostname_len == cookie_domain_len) 136 return TRUE; 137 if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) 138 return TRUE; 139 return FALSE; 140 } 141 142 /* 143 * Return true if the given string is an IP(v4|v6) address. 144 */ 145 static bool isip(const char *domain) 146 { 147 struct in_addr addr; 148 #ifdef ENABLE_IPV6 149 struct in6_addr addr6; 150 #endif 151 152 if(Curl_inet_pton(AF_INET, domain, &addr) 153 #ifdef ENABLE_IPV6 154 || Curl_inet_pton(AF_INET6, domain, &addr6) 155 #endif 156 ) { 157 /* domain name given as IP address */ 158 return TRUE; 159 } 160 161 return FALSE; 162 } 163 164 /* 165 * matching cookie path and url path 166 * RFC6265 5.1.4 Paths and Path-Match 167 */ 168 static bool pathmatch(const char *cookie_path, const char *request_uri) 169 { 170 size_t cookie_path_len; 171 size_t uri_path_len; 172 char *uri_path = NULL; 173 char *pos; 174 bool ret = FALSE; 175 176 /* cookie_path must not have last '/' separator. ex: /sample */ 177 cookie_path_len = strlen(cookie_path); 178 if(1 == cookie_path_len) { 179 /* cookie_path must be '/' */ 180 return TRUE; 181 } 182 183 uri_path = strdup(request_uri); 184 if(!uri_path) 185 return FALSE; 186 pos = strchr(uri_path, '?'); 187 if(pos) 188 *pos = 0x0; 189 190 /* #-fragments are already cut off! */ 191 if(0 == strlen(uri_path) || uri_path[0] != '/') { 192 free(uri_path); 193 uri_path = strdup("/"); 194 if(!uri_path) 195 return FALSE; 196 } 197 198 /* here, RFC6265 5.1.4 says 199 4. Output the characters of the uri-path from the first character up 200 to, but not including, the right-most %x2F ("/"). 201 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site 202 without redirect. 203 Ignore this algorithm because /hoge is uri path for this case 204 (uri path is not /). 205 */ 206 207 uri_path_len = strlen(uri_path); 208 209 if(uri_path_len < cookie_path_len) { 210 ret = FALSE; 211 goto pathmatched; 212 } 213 214 /* not using checkprefix() because matching should be case-sensitive */ 215 if(strncmp(cookie_path, uri_path, cookie_path_len)) { 216 ret = FALSE; 217 goto pathmatched; 218 } 219 220 /* The cookie-path and the uri-path are identical. */ 221 if(cookie_path_len == uri_path_len) { 222 ret = TRUE; 223 goto pathmatched; 224 } 225 226 /* here, cookie_path_len < uri_path_len */ 227 if(uri_path[cookie_path_len] == '/') { 228 ret = TRUE; 229 goto pathmatched; 230 } 231 232 ret = FALSE; 233 234 pathmatched: 235 free(uri_path); 236 return ret; 237 } 238 239 /* 240 * Return the top-level domain, for optimal hashing. 241 */ 242 static const char *get_top_domain(const char * const domain, size_t *outlen) 243 { 244 size_t len; 245 const char *first = NULL, *last; 246 247 if(!domain) 248 return NULL; 249 250 len = strlen(domain); 251 last = memrchr(domain, '.', len); 252 if(last) { 253 first = memrchr(domain, '.', (last - domain)); 254 if(first) 255 len -= (++first - domain); 256 } 257 258 if(outlen) 259 *outlen = len; 260 261 return first? first: domain; 262 } 263 264 /* 265 * A case-insensitive hash for the cookie domains. 266 */ 267 static size_t cookie_hash_domain(const char *domain, const size_t len) 268 { 269 const char *end = domain + len; 270 size_t h = 5381; 271 272 while(domain < end) { 273 h += h << 5; 274 h ^= Curl_raw_toupper(*domain++); 275 } 276 277 return (h % COOKIE_HASH_SIZE); 278 } 279 280 /* 281 * Hash this domain. 282 */ 283 static size_t cookiehash(const char * const domain) 284 { 285 const char *top; 286 size_t len; 287 288 if(!domain || isip(domain)) 289 return 0; 290 291 top = get_top_domain(domain, &len); 292 return cookie_hash_domain(top, len); 293 } 294 295 /* 296 * cookie path sanitize 297 */ 298 static char *sanitize_cookie_path(const char *cookie_path) 299 { 300 size_t len; 301 char *new_path = strdup(cookie_path); 302 if(!new_path) 303 return NULL; 304 305 /* some stupid site sends path attribute with '"'. */ 306 len = strlen(new_path); 307 if(new_path[0] == '\"') { 308 memmove((void *)new_path, (const void *)(new_path + 1), len); 309 len--; 310 } 311 if(len && (new_path[len - 1] == '\"')) { 312 new_path[len - 1] = 0x0; 313 len--; 314 } 315 316 /* RFC6265 5.2.4 The Path Attribute */ 317 if(new_path[0] != '/') { 318 /* Let cookie-path be the default-path. */ 319 free(new_path); 320 new_path = strdup("/"); 321 return new_path; 322 } 323 324 /* convert /hoge/ to /hoge */ 325 if(len && new_path[len - 1] == '/') { 326 new_path[len - 1] = 0x0; 327 } 328 329 return new_path; 330 } 331 332 /* 333 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). 334 * 335 * NOTE: OOM or cookie parsing failures are ignored. 336 */ 337 void Curl_cookie_loadfiles(struct Curl_easy *data) 338 { 339 struct curl_slist *list = data->change.cookielist; 340 if(list) { 341 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 342 while(list) { 343 struct CookieInfo *newcookies = Curl_cookie_init(data, 344 list->data, 345 data->cookies, 346 data->set.cookiesession); 347 if(!newcookies) 348 /* Failure may be due to OOM or a bad cookie; both are ignored 349 * but only the first should be 350 */ 351 infof(data, "ignoring failed cookie_init for %s\n", list->data); 352 else 353 data->cookies = newcookies; 354 list = list->next; 355 } 356 curl_slist_free_all(data->change.cookielist); /* clean up list */ 357 data->change.cookielist = NULL; /* don't do this again! */ 358 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 359 } 360 } 361 362 /* 363 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL 364 * that will be freed before the allocated string is stored there. 365 * 366 * It is meant to easily replace strdup() 367 */ 368 static void strstore(char **str, const char *newstr) 369 { 370 free(*str); 371 *str = strdup(newstr); 372 } 373 374 /* 375 * remove_expired() removes expired cookies. 376 */ 377 static void remove_expired(struct CookieInfo *cookies) 378 { 379 struct Cookie *co, *nx; 380 curl_off_t now = (curl_off_t)time(NULL); 381 unsigned int i; 382 383 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 384 struct Cookie *pv = NULL; 385 co = cookies->cookies[i]; 386 while(co) { 387 nx = co->next; 388 if(co->expires && co->expires < now) { 389 if(!pv) { 390 cookies->cookies[i] = co->next; 391 } 392 else { 393 pv->next = co->next; 394 } 395 cookies->numcookies--; 396 freecookie(co); 397 } 398 else { 399 pv = co; 400 } 401 co = nx; 402 } 403 } 404 } 405 406 /* Make sure domain contains a dot or is localhost. */ 407 static bool bad_domain(const char *domain) 408 { 409 return !strchr(domain, '.') && !strcasecompare(domain, "localhost"); 410 } 411 412 /**************************************************************************** 413 * 414 * Curl_cookie_add() 415 * 416 * Add a single cookie line to the cookie keeping object. 417 * 418 * Be aware that sometimes we get an IP-only host name, and that might also be 419 * a numerical IPv6 address. 420 * 421 * Returns NULL on out of memory or invalid cookie. This is suboptimal, 422 * as they should be treated separately. 423 ***************************************************************************/ 424 425 struct Cookie * 426 Curl_cookie_add(struct Curl_easy *data, 427 /* The 'data' pointer here may be NULL at times, and thus 428 must only be used very carefully for things that can deal 429 with data being NULL. Such as infof() and similar */ 430 431 struct CookieInfo *c, 432 bool httpheader, /* TRUE if HTTP header-style line */ 433 bool noexpire, /* if TRUE, skip remove_expired() */ 434 char *lineptr, /* first character of the line */ 435 const char *domain, /* default domain */ 436 const char *path, /* full path used when this cookie is set, 437 used to get default path for the cookie 438 unless set */ 439 bool secure) /* TRUE if connection is over secure origin */ 440 { 441 struct Cookie *clist; 442 struct Cookie *co; 443 struct Cookie *lastc = NULL; 444 time_t now = time(NULL); 445 bool replace_old = FALSE; 446 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ 447 size_t myhash; 448 449 #ifdef CURL_DISABLE_VERBOSE_STRINGS 450 (void)data; 451 #endif 452 453 /* First, alloc and init a new struct for it */ 454 co = calloc(1, sizeof(struct Cookie)); 455 if(!co) 456 return NULL; /* bail out if we're this low on memory */ 457 458 if(httpheader) { 459 /* This line was read off a HTTP-header */ 460 char name[MAX_NAME]; 461 char what[MAX_NAME]; 462 const char *ptr; 463 const char *semiptr; 464 465 size_t linelength = strlen(lineptr); 466 if(linelength > MAX_COOKIE_LINE) { 467 /* discard overly long lines at once */ 468 free(co); 469 return NULL; 470 } 471 472 semiptr = strchr(lineptr, ';'); /* first, find a semicolon */ 473 474 while(*lineptr && ISBLANK(*lineptr)) 475 lineptr++; 476 477 ptr = lineptr; 478 do { 479 /* we have a <what>=<this> pair or a stand-alone word here */ 480 name[0] = what[0] = 0; /* init the buffers */ 481 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%" 482 MAX_NAME_TXT "[^;\r\n]", 483 name, what)) { 484 /* Use strstore() below to properly deal with received cookie 485 headers that have the same string property set more than once, 486 and then we use the last one. */ 487 const char *whatptr; 488 bool done = FALSE; 489 bool sep; 490 size_t len = strlen(what); 491 size_t nlen = strlen(name); 492 const char *endofn = &ptr[ nlen ]; 493 494 if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) || 495 ((nlen + len) > MAX_NAME)) { 496 /* too long individual name or contents, or too long combination of 497 name + contents. Chrome and Firefox support 4095 or 4096 bytes 498 combo. */ 499 freecookie(co); 500 infof(data, "oversized cookie dropped, name/val %zu + %zu bytes\n", 501 nlen, len); 502 return NULL; 503 } 504 505 /* name ends with a '=' ? */ 506 sep = (*endofn == '=')?TRUE:FALSE; 507 508 if(nlen) { 509 endofn--; /* move to the last character */ 510 if(ISBLANK(*endofn)) { 511 /* skip trailing spaces in name */ 512 while(*endofn && ISBLANK(*endofn) && nlen) { 513 endofn--; 514 nlen--; 515 } 516 name[nlen] = 0; /* new end of name */ 517 } 518 } 519 520 /* Strip off trailing whitespace from the 'what' */ 521 while(len && ISBLANK(what[len-1])) { 522 what[len-1] = 0; 523 len--; 524 } 525 526 /* Skip leading whitespace from the 'what' */ 527 whatptr = what; 528 while(*whatptr && ISBLANK(*whatptr)) 529 whatptr++; 530 531 /* 532 * Check if we have a reserved prefix set before anything else, as we 533 * otherwise have to test for the prefix in both the cookie name and 534 * "the rest". Prefixes must start with '__' and end with a '-', so 535 * only test for names where that can possibly be true. 536 */ 537 if(nlen > 3 && name[0] == '_' && name[1] == '_') { 538 if(strncasecompare("__Secure-", name, 9)) 539 co->prefix |= COOKIE_PREFIX__SECURE; 540 else if(strncasecompare("__Host-", name, 7)) 541 co->prefix |= COOKIE_PREFIX__HOST; 542 } 543 544 if(!co->name) { 545 /* The very first name/value pair is the actual cookie name */ 546 if(!sep) { 547 /* Bad name/value pair. */ 548 badcookie = TRUE; 549 break; 550 } 551 co->name = strdup(name); 552 co->value = strdup(whatptr); 553 done = TRUE; 554 if(!co->name || !co->value) { 555 badcookie = TRUE; 556 break; 557 } 558 } 559 else if(!len) { 560 /* this was a "<name>=" with no content, and we must allow 561 'secure' and 'httponly' specified this weirdly */ 562 done = TRUE; 563 /* 564 * secure cookies are only allowed to be set when the connection is 565 * using a secure protocol, or when the cookie is being set by 566 * reading from file 567 */ 568 if(strcasecompare("secure", name)) { 569 if(secure || !c->running) { 570 co->secure = TRUE; 571 } 572 else { 573 badcookie = TRUE; 574 break; 575 } 576 } 577 else if(strcasecompare("httponly", name)) 578 co->httponly = TRUE; 579 else if(sep) 580 /* there was a '=' so we're not done parsing this field */ 581 done = FALSE; 582 } 583 if(done) 584 ; 585 else if(strcasecompare("path", name)) { 586 strstore(&co->path, whatptr); 587 if(!co->path) { 588 badcookie = TRUE; /* out of memory bad */ 589 break; 590 } 591 free(co->spath); /* if this is set again */ 592 co->spath = sanitize_cookie_path(co->path); 593 if(!co->spath) { 594 badcookie = TRUE; /* out of memory bad */ 595 break; 596 } 597 } 598 else if(strcasecompare("domain", name)) { 599 bool is_ip; 600 601 /* Now, we make sure that our host is within the given domain, 602 or the given domain is not valid and thus cannot be set. */ 603 604 if('.' == whatptr[0]) 605 whatptr++; /* ignore preceding dot */ 606 607 #ifndef USE_LIBPSL 608 /* 609 * Without PSL we don't know when the incoming cookie is set on a 610 * TLD or otherwise "protected" suffix. To reduce risk, we require a 611 * dot OR the exact host name being "localhost". 612 */ 613 if(bad_domain(whatptr)) 614 domain = ":"; 615 #endif 616 617 is_ip = isip(domain ? domain : whatptr); 618 619 if(!domain 620 || (is_ip && !strcmp(whatptr, domain)) 621 || (!is_ip && tailmatch(whatptr, domain))) { 622 strstore(&co->domain, whatptr); 623 if(!co->domain) { 624 badcookie = TRUE; 625 break; 626 } 627 if(!is_ip) 628 co->tailmatch = TRUE; /* we always do that if the domain name was 629 given */ 630 } 631 else { 632 /* we did not get a tailmatch and then the attempted set domain 633 is not a domain to which the current host belongs. Mark as 634 bad. */ 635 badcookie = TRUE; 636 infof(data, "skipped cookie with bad tailmatch domain: %s\n", 637 whatptr); 638 } 639 } 640 else if(strcasecompare("version", name)) { 641 strstore(&co->version, whatptr); 642 if(!co->version) { 643 badcookie = TRUE; 644 break; 645 } 646 } 647 else if(strcasecompare("max-age", name)) { 648 /* Defined in RFC2109: 649 650 Optional. The Max-Age attribute defines the lifetime of the 651 cookie, in seconds. The delta-seconds value is a decimal non- 652 negative integer. After delta-seconds seconds elapse, the 653 client should discard the cookie. A value of zero means the 654 cookie should be discarded immediately. 655 656 */ 657 strstore(&co->maxage, whatptr); 658 if(!co->maxage) { 659 badcookie = TRUE; 660 break; 661 } 662 } 663 else if(strcasecompare("expires", name)) { 664 strstore(&co->expirestr, whatptr); 665 if(!co->expirestr) { 666 badcookie = TRUE; 667 break; 668 } 669 } 670 /* 671 else this is the second (or more) name we don't know 672 about! */ 673 } 674 else { 675 /* this is an "illegal" <what>=<this> pair */ 676 } 677 678 if(!semiptr || !*semiptr) { 679 /* we already know there are no more cookies */ 680 semiptr = NULL; 681 continue; 682 } 683 684 ptr = semiptr + 1; 685 while(*ptr && ISBLANK(*ptr)) 686 ptr++; 687 semiptr = strchr(ptr, ';'); /* now, find the next semicolon */ 688 689 if(!semiptr && *ptr) 690 /* There are no more semicolons, but there's a final name=value pair 691 coming up */ 692 semiptr = strchr(ptr, '\0'); 693 } while(semiptr); 694 695 if(co->maxage) { 696 CURLofft offt; 697 offt = curlx_strtoofft((*co->maxage == '\"')? 698 &co->maxage[1]:&co->maxage[0], NULL, 10, 699 &co->expires); 700 if(offt == CURL_OFFT_FLOW) 701 /* overflow, used max value */ 702 co->expires = CURL_OFF_T_MAX; 703 else if(!offt) { 704 if(!co->expires) 705 /* already expired */ 706 co->expires = 1; 707 else if(CURL_OFF_T_MAX - now < co->expires) 708 /* would overflow */ 709 co->expires = CURL_OFF_T_MAX; 710 else 711 co->expires += now; 712 } 713 } 714 else if(co->expirestr) { 715 /* Note that if the date couldn't get parsed for whatever reason, 716 the cookie will be treated as a session cookie */ 717 co->expires = curl_getdate(co->expirestr, NULL); 718 719 /* Session cookies have expires set to 0 so if we get that back 720 from the date parser let's add a second to make it a 721 non-session cookie */ 722 if(co->expires == 0) 723 co->expires = 1; 724 else if(co->expires < 0) 725 co->expires = 0; 726 } 727 728 if(!badcookie && !co->domain) { 729 if(domain) { 730 /* no domain was given in the header line, set the default */ 731 co->domain = strdup(domain); 732 if(!co->domain) 733 badcookie = TRUE; 734 } 735 } 736 737 if(!badcookie && !co->path && path) { 738 /* No path was given in the header line, set the default. 739 Note that the passed-in path to this function MAY have a '?' and 740 following part that MUST not be stored as part of the path. */ 741 char *queryp = strchr(path, '?'); 742 743 /* queryp is where the interesting part of the path ends, so now we 744 want to the find the last */ 745 char *endslash; 746 if(!queryp) 747 endslash = strrchr(path, '/'); 748 else 749 endslash = memrchr(path, '/', (queryp - path)); 750 if(endslash) { 751 size_t pathlen = (endslash-path + 1); /* include end slash */ 752 co->path = malloc(pathlen + 1); /* one extra for the zero byte */ 753 if(co->path) { 754 memcpy(co->path, path, pathlen); 755 co->path[pathlen] = 0; /* zero terminate */ 756 co->spath = sanitize_cookie_path(co->path); 757 if(!co->spath) 758 badcookie = TRUE; /* out of memory bad */ 759 } 760 else 761 badcookie = TRUE; 762 } 763 } 764 765 if(badcookie || !co->name) { 766 /* we didn't get a cookie name or a bad one, 767 this is an illegal line, bail out */ 768 freecookie(co); 769 return NULL; 770 } 771 772 } 773 else { 774 /* This line is NOT a HTTP header style line, we do offer support for 775 reading the odd netscape cookies-file format here */ 776 char *ptr; 777 char *firstptr; 778 char *tok_buf = NULL; 779 int fields; 780 781 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies 782 marked with httpOnly after the domain name are not accessible 783 from javascripts, but since curl does not operate at javascript 784 level, we include them anyway. In Firefox's cookie files, these 785 lines are preceded with #HttpOnly_ and then everything is 786 as usual, so we skip 10 characters of the line.. 787 */ 788 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { 789 lineptr += 10; 790 co->httponly = TRUE; 791 } 792 793 if(lineptr[0]=='#') { 794 /* don't even try the comments */ 795 free(co); 796 return NULL; 797 } 798 /* strip off the possible end-of-line characters */ 799 ptr = strchr(lineptr, '\r'); 800 if(ptr) 801 *ptr = 0; /* clear it */ 802 ptr = strchr(lineptr, '\n'); 803 if(ptr) 804 *ptr = 0; /* clear it */ 805 806 firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */ 807 808 /* Now loop through the fields and init the struct we already have 809 allocated */ 810 for(ptr = firstptr, fields = 0; ptr && !badcookie; 811 ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { 812 switch(fields) { 813 case 0: 814 if(ptr[0]=='.') /* skip preceding dots */ 815 ptr++; 816 co->domain = strdup(ptr); 817 if(!co->domain) 818 badcookie = TRUE; 819 break; 820 case 1: 821 /* This field got its explanation on the 23rd of May 2001 by 822 Andrs Garca: 823 824 flag: A TRUE/FALSE value indicating if all machines within a given 825 domain can access the variable. This value is set automatically by 826 the browser, depending on the value you set for the domain. 827 828 As far as I can see, it is set to true when the cookie says 829 .domain.com and to false when the domain is complete www.domain.com 830 */ 831 co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; 832 break; 833 case 2: 834 /* It turns out, that sometimes the file format allows the path 835 field to remain not filled in, we try to detect this and work 836 around it! Andrs Garca made us aware of this... */ 837 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { 838 /* only if the path doesn't look like a boolean option! */ 839 co->path = strdup(ptr); 840 if(!co->path) 841 badcookie = TRUE; 842 else { 843 co->spath = sanitize_cookie_path(co->path); 844 if(!co->spath) { 845 badcookie = TRUE; /* out of memory bad */ 846 } 847 } 848 break; 849 } 850 /* this doesn't look like a path, make one up! */ 851 co->path = strdup("/"); 852 if(!co->path) 853 badcookie = TRUE; 854 co->spath = strdup("/"); 855 if(!co->spath) 856 badcookie = TRUE; 857 fields++; /* add a field and fall down to secure */ 858 /* FALLTHROUGH */ 859 case 3: 860 co->secure = FALSE; 861 if(strcasecompare(ptr, "TRUE")) { 862 if(secure || c->running) 863 co->secure = TRUE; 864 else 865 badcookie = TRUE; 866 } 867 break; 868 case 4: 869 if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) 870 badcookie = TRUE; 871 break; 872 case 5: 873 co->name = strdup(ptr); 874 if(!co->name) 875 badcookie = TRUE; 876 /* For Netscape file format cookies we check prefix on the name */ 877 if(strncasecompare("__Secure-", co->name, 9)) 878 co->prefix |= COOKIE_PREFIX__SECURE; 879 else if(strncasecompare("__Host-", co->name, 7)) 880 co->prefix |= COOKIE_PREFIX__HOST; 881 break; 882 case 6: 883 co->value = strdup(ptr); 884 if(!co->value) 885 badcookie = TRUE; 886 break; 887 } 888 } 889 if(6 == fields) { 890 /* we got a cookie with blank contents, fix it */ 891 co->value = strdup(""); 892 if(!co->value) 893 badcookie = TRUE; 894 else 895 fields++; 896 } 897 898 if(!badcookie && (7 != fields)) 899 /* we did not find the sufficient number of fields */ 900 badcookie = TRUE; 901 902 if(badcookie) { 903 freecookie(co); 904 return NULL; 905 } 906 907 } 908 909 if(co->prefix & COOKIE_PREFIX__SECURE) { 910 /* The __Secure- prefix only requires that the cookie be set secure */ 911 if(!co->secure) { 912 freecookie(co); 913 return NULL; 914 } 915 } 916 if(co->prefix & COOKIE_PREFIX__HOST) { 917 /* 918 * The __Host- prefix requires the cookie to be secure, have a "/" path 919 * and not have a domain set. 920 */ 921 if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) 922 ; 923 else { 924 freecookie(co); 925 return NULL; 926 } 927 } 928 929 if(!c->running && /* read from a file */ 930 c->newsession && /* clean session cookies */ 931 !co->expires) { /* this is a session cookie since it doesn't expire! */ 932 freecookie(co); 933 return NULL; 934 } 935 936 co->livecookie = c->running; 937 co->creationtime = ++c->lastct; 938 939 /* now, we have parsed the incoming line, we must now check if this 940 supersedes an already existing cookie, which it may if the previous have 941 the same domain and path as this */ 942 943 /* at first, remove expired cookies */ 944 if(!noexpire) 945 remove_expired(c); 946 947 #ifdef USE_LIBPSL 948 /* Check if the domain is a Public Suffix and if yes, ignore the cookie. */ 949 if(domain && co->domain && !isip(co->domain)) { 950 const psl_ctx_t *psl = Curl_psl_use(data); 951 int acceptable; 952 953 if(psl) { 954 acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); 955 Curl_psl_release(data); 956 } 957 else 958 acceptable = !bad_domain(domain); 959 960 if(!acceptable) { 961 infof(data, "cookie '%s' dropped, domain '%s' must not " 962 "set cookies for '%s'\n", co->name, domain, co->domain); 963 freecookie(co); 964 return NULL; 965 } 966 } 967 #endif 968 969 myhash = cookiehash(co->domain); 970 clist = c->cookies[myhash]; 971 replace_old = FALSE; 972 while(clist) { 973 if(strcasecompare(clist->name, co->name)) { 974 /* the names are identical */ 975 976 if(clist->domain && co->domain) { 977 if(strcasecompare(clist->domain, co->domain) && 978 (clist->tailmatch == co->tailmatch)) 979 /* The domains are identical */ 980 replace_old = TRUE; 981 } 982 else if(!clist->domain && !co->domain) 983 replace_old = TRUE; 984 985 if(replace_old) { 986 /* the domains were identical */ 987 988 if(clist->spath && co->spath) { 989 if(clist->secure && !co->secure && !secure) { 990 size_t cllen; 991 const char *sep; 992 993 /* 994 * A non-secure cookie may not overlay an existing secure cookie. 995 * For an existing cookie "a" with path "/login", refuse a new 996 * cookie "a" with for example path "/login/en", while the path 997 * "/loginhelper" is ok. 998 */ 999 1000 sep = strchr(clist->spath + 1, '/'); 1001 1002 if(sep) 1003 cllen = sep - clist->spath; 1004 else 1005 cllen = strlen(clist->spath); 1006 1007 if(strncasecompare(clist->spath, co->spath, cllen)) { 1008 freecookie(co); 1009 return NULL; 1010 } 1011 } 1012 else if(strcasecompare(clist->spath, co->spath)) 1013 replace_old = TRUE; 1014 else 1015 replace_old = FALSE; 1016 } 1017 else if(!clist->spath && !co->spath) 1018 replace_old = TRUE; 1019 else 1020 replace_old = FALSE; 1021 1022 } 1023 1024 if(replace_old && !co->livecookie && clist->livecookie) { 1025 /* Both cookies matched fine, except that the already present 1026 cookie is "live", which means it was set from a header, while 1027 the new one isn't "live" and thus only read from a file. We let 1028 live cookies stay alive */ 1029 1030 /* Free the newcomer and get out of here! */ 1031 freecookie(co); 1032 return NULL; 1033 } 1034 1035 if(replace_old) { 1036 co->next = clist->next; /* get the next-pointer first */ 1037 1038 /* when replacing, creationtime is kept from old */ 1039 co->creationtime = clist->creationtime; 1040 1041 /* then free all the old pointers */ 1042 free(clist->name); 1043 free(clist->value); 1044 free(clist->domain); 1045 free(clist->path); 1046 free(clist->spath); 1047 free(clist->expirestr); 1048 free(clist->version); 1049 free(clist->maxage); 1050 1051 *clist = *co; /* then store all the new data */ 1052 1053 free(co); /* free the newly alloced memory */ 1054 co = clist; /* point to the previous struct instead */ 1055 1056 /* We have replaced a cookie, now skip the rest of the list but 1057 make sure the 'lastc' pointer is properly set */ 1058 do { 1059 lastc = clist; 1060 clist = clist->next; 1061 } while(clist); 1062 break; 1063 } 1064 } 1065 lastc = clist; 1066 clist = clist->next; 1067 } 1068 1069 if(c->running) 1070 /* Only show this when NOT reading the cookies from a file */ 1071 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " 1072 "expire %" CURL_FORMAT_CURL_OFF_T "\n", 1073 replace_old?"Replaced":"Added", co->name, co->value, 1074 co->domain, co->path, co->expires); 1075 1076 if(!replace_old) { 1077 /* then make the last item point on this new one */ 1078 if(lastc) 1079 lastc->next = co; 1080 else 1081 c->cookies[myhash] = co; 1082 c->numcookies++; /* one more cookie in the jar */ 1083 } 1084 1085 return co; 1086 } 1087 1088 /* 1089 * get_line() makes sure to only return complete whole lines that fit in 'len' 1090 * bytes and end with a newline. 1091 */ 1092 char *Curl_get_line(char *buf, int len, FILE *input) 1093 { 1094 bool partial = FALSE; 1095 while(1) { 1096 char *b = fgets(buf, len, input); 1097 if(b) { 1098 size_t rlen = strlen(b); 1099 if(rlen && (b[rlen-1] == '\n')) { 1100 if(partial) { 1101 partial = FALSE; 1102 continue; 1103 } 1104 return b; 1105 } 1106 /* read a partial, discard the next piece that ends with newline */ 1107 partial = TRUE; 1108 } 1109 else 1110 break; 1111 } 1112 return NULL; 1113 } 1114 1115 1116 /***************************************************************************** 1117 * 1118 * Curl_cookie_init() 1119 * 1120 * Inits a cookie struct to read data from a local file. This is always 1121 * called before any cookies are set. File may be NULL. 1122 * 1123 * If 'newsession' is TRUE, discard all "session cookies" on read from file. 1124 * 1125 * Returns NULL on out of memory. Invalid cookies are ignored. 1126 ****************************************************************************/ 1127 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 1128 const char *file, 1129 struct CookieInfo *inc, 1130 bool newsession) 1131 { 1132 struct CookieInfo *c; 1133 FILE *fp = NULL; 1134 bool fromfile = TRUE; 1135 char *line = NULL; 1136 1137 if(NULL == inc) { 1138 /* we didn't get a struct, create one */ 1139 c = calloc(1, sizeof(struct CookieInfo)); 1140 if(!c) 1141 return NULL; /* failed to get memory */ 1142 c->filename = strdup(file?file:"none"); /* copy the name just in case */ 1143 if(!c->filename) 1144 goto fail; /* failed to get memory */ 1145 } 1146 else { 1147 /* we got an already existing one, use that */ 1148 c = inc; 1149 } 1150 c->running = FALSE; /* this is not running, this is init */ 1151 1152 if(file && !strcmp(file, "-")) { 1153 fp = stdin; 1154 fromfile = FALSE; 1155 } 1156 else if(file && !*file) { 1157 /* points to a "" string */ 1158 fp = NULL; 1159 } 1160 else 1161 fp = file?fopen(file, FOPEN_READTEXT):NULL; 1162 1163 c->newsession = newsession; /* new session? */ 1164 1165 if(fp) { 1166 char *lineptr; 1167 bool headerline; 1168 1169 line = malloc(MAX_COOKIE_LINE); 1170 if(!line) 1171 goto fail; 1172 while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { 1173 if(checkprefix("Set-Cookie:", line)) { 1174 /* This is a cookie line, get it! */ 1175 lineptr = &line[11]; 1176 headerline = TRUE; 1177 } 1178 else { 1179 lineptr = line; 1180 headerline = FALSE; 1181 } 1182 while(*lineptr && ISBLANK(*lineptr)) 1183 lineptr++; 1184 1185 Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); 1186 } 1187 free(line); /* free the line buffer */ 1188 remove_expired(c); /* run this once, not on every cookie */ 1189 1190 if(fromfile) 1191 fclose(fp); 1192 } 1193 1194 c->running = TRUE; /* now, we're running */ 1195 1196 return c; 1197 1198 fail: 1199 free(line); 1200 if(!inc) 1201 /* Only clean up if we allocated it here, as the original could still be in 1202 * use by a share handle */ 1203 Curl_cookie_cleanup(c); 1204 if(fromfile && fp) 1205 fclose(fp); 1206 return NULL; /* out of memory */ 1207 } 1208 1209 /* sort this so that the longest path gets before the shorter path */ 1210 static int cookie_sort(const void *p1, const void *p2) 1211 { 1212 struct Cookie *c1 = *(struct Cookie **)p1; 1213 struct Cookie *c2 = *(struct Cookie **)p2; 1214 size_t l1, l2; 1215 1216 /* 1 - compare cookie path lengths */ 1217 l1 = c1->path ? strlen(c1->path) : 0; 1218 l2 = c2->path ? strlen(c2->path) : 0; 1219 1220 if(l1 != l2) 1221 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 1222 1223 /* 2 - compare cookie domain lengths */ 1224 l1 = c1->domain ? strlen(c1->domain) : 0; 1225 l2 = c2->domain ? strlen(c2->domain) : 0; 1226 1227 if(l1 != l2) 1228 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 1229 1230 /* 3 - compare cookie name lengths */ 1231 l1 = c1->name ? strlen(c1->name) : 0; 1232 l2 = c2->name ? strlen(c2->name) : 0; 1233 1234 if(l1 != l2) 1235 return (l2 > l1) ? 1 : -1; 1236 1237 /* 4 - compare cookie creation time */ 1238 return (c2->creationtime > c1->creationtime) ? 1 : -1; 1239 } 1240 1241 /* sort cookies only according to creation time */ 1242 static int cookie_sort_ct(const void *p1, const void *p2) 1243 { 1244 struct Cookie *c1 = *(struct Cookie **)p1; 1245 struct Cookie *c2 = *(struct Cookie **)p2; 1246 1247 return (c2->creationtime > c1->creationtime) ? 1 : -1; 1248 } 1249 1250 #define CLONE(field) \ 1251 do { \ 1252 if(src->field) { \ 1253 d->field = strdup(src->field); \ 1254 if(!d->field) \ 1255 goto fail; \ 1256 } \ 1257 } while(0) 1258 1259 static struct Cookie *dup_cookie(struct Cookie *src) 1260 { 1261 struct Cookie *d = calloc(sizeof(struct Cookie), 1); 1262 if(d) { 1263 CLONE(expirestr); 1264 CLONE(domain); 1265 CLONE(path); 1266 CLONE(spath); 1267 CLONE(name); 1268 CLONE(value); 1269 CLONE(maxage); 1270 CLONE(version); 1271 d->expires = src->expires; 1272 d->tailmatch = src->tailmatch; 1273 d->secure = src->secure; 1274 d->livecookie = src->livecookie; 1275 d->httponly = src->httponly; 1276 d->creationtime = src->creationtime; 1277 } 1278 return d; 1279 1280 fail: 1281 freecookie(d); 1282 return NULL; 1283 } 1284 1285 /***************************************************************************** 1286 * 1287 * Curl_cookie_getlist() 1288 * 1289 * For a given host and path, return a linked list of cookies that the 1290 * client should send to the server if used now. The secure boolean informs 1291 * the cookie if a secure connection is achieved or not. 1292 * 1293 * It shall only return cookies that haven't expired. 1294 * 1295 ****************************************************************************/ 1296 1297 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, 1298 const char *host, const char *path, 1299 bool secure) 1300 { 1301 struct Cookie *newco; 1302 struct Cookie *co; 1303 struct Cookie *mainco = NULL; 1304 size_t matches = 0; 1305 bool is_ip; 1306 const size_t myhash = cookiehash(host); 1307 1308 if(!c || !c->cookies[myhash]) 1309 return NULL; /* no cookie struct or no cookies in the struct */ 1310 1311 /* at first, remove expired cookies */ 1312 remove_expired(c); 1313 1314 /* check if host is an IP(v4|v6) address */ 1315 is_ip = isip(host); 1316 1317 co = c->cookies[myhash]; 1318 1319 while(co) { 1320 /* if the cookie requires we're secure we must only continue if we are! */ 1321 if(co->secure?secure:TRUE) { 1322 1323 /* now check if the domain is correct */ 1324 if(!co->domain || 1325 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || 1326 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { 1327 /* the right part of the host matches the domain stuff in the 1328 cookie data */ 1329 1330 /* now check the left part of the path with the cookies path 1331 requirement */ 1332 if(!co->spath || pathmatch(co->spath, path) ) { 1333 1334 /* and now, we know this is a match and we should create an 1335 entry for the return-linked-list */ 1336 1337 newco = dup_cookie(co); 1338 if(newco) { 1339 /* then modify our next */ 1340 newco->next = mainco; 1341 1342 /* point the main to us */ 1343 mainco = newco; 1344 1345 matches++; 1346 } 1347 else 1348 goto fail; 1349 } 1350 } 1351 } 1352 co = co->next; 1353 } 1354 1355 if(matches) { 1356 /* Now we need to make sure that if there is a name appearing more than 1357 once, the longest specified path version comes first. To make this 1358 the swiftest way, we just sort them all based on path length. */ 1359 struct Cookie **array; 1360 size_t i; 1361 1362 /* alloc an array and store all cookie pointers */ 1363 array = malloc(sizeof(struct Cookie *) * matches); 1364 if(!array) 1365 goto fail; 1366 1367 co = mainco; 1368 1369 for(i = 0; co; co = co->next) 1370 array[i++] = co; 1371 1372 /* now sort the cookie pointers in path length order */ 1373 qsort(array, matches, sizeof(struct Cookie *), cookie_sort); 1374 1375 /* remake the linked list order according to the new order */ 1376 1377 mainco = array[0]; /* start here */ 1378 for(i = 0; i<matches-1; i++) 1379 array[i]->next = array[i + 1]; 1380 array[matches-1]->next = NULL; /* terminate the list */ 1381 1382 free(array); /* remove the temporary data again */ 1383 } 1384 1385 return mainco; /* return the new list */ 1386 1387 fail: 1388 /* failure, clear up the allocated chain and return NULL */ 1389 Curl_cookie_freelist(mainco); 1390 return NULL; 1391 } 1392 1393 /***************************************************************************** 1394 * 1395 * Curl_cookie_clearall() 1396 * 1397 * Clear all existing cookies and reset the counter. 1398 * 1399 ****************************************************************************/ 1400 void Curl_cookie_clearall(struct CookieInfo *cookies) 1401 { 1402 if(cookies) { 1403 unsigned int i; 1404 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1405 Curl_cookie_freelist(cookies->cookies[i]); 1406 cookies->cookies[i] = NULL; 1407 } 1408 cookies->numcookies = 0; 1409 } 1410 } 1411 1412 /***************************************************************************** 1413 * 1414 * Curl_cookie_freelist() 1415 * 1416 * Free a list of cookies previously returned by Curl_cookie_getlist(); 1417 * 1418 ****************************************************************************/ 1419 1420 void Curl_cookie_freelist(struct Cookie *co) 1421 { 1422 struct Cookie *next; 1423 while(co) { 1424 next = co->next; 1425 freecookie(co); 1426 co = next; 1427 } 1428 } 1429 1430 1431 /***************************************************************************** 1432 * 1433 * Curl_cookie_clearsess() 1434 * 1435 * Free all session cookies in the cookies list. 1436 * 1437 ****************************************************************************/ 1438 void Curl_cookie_clearsess(struct CookieInfo *cookies) 1439 { 1440 struct Cookie *first, *curr, *next, *prev = NULL; 1441 unsigned int i; 1442 1443 if(!cookies) 1444 return; 1445 1446 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1447 if(!cookies->cookies[i]) 1448 continue; 1449 1450 first = curr = prev = cookies->cookies[i]; 1451 1452 for(; curr; curr = next) { 1453 next = curr->next; 1454 if(!curr->expires) { 1455 if(first == curr) 1456 first = next; 1457 1458 if(prev == curr) 1459 prev = next; 1460 else 1461 prev->next = next; 1462 1463 freecookie(curr); 1464 cookies->numcookies--; 1465 } 1466 else 1467 prev = curr; 1468 } 1469 1470 cookies->cookies[i] = first; 1471 } 1472 } 1473 1474 1475 /***************************************************************************** 1476 * 1477 * Curl_cookie_cleanup() 1478 * 1479 * Free a "cookie object" previous created with Curl_cookie_init(). 1480 * 1481 ****************************************************************************/ 1482 void Curl_cookie_cleanup(struct CookieInfo *c) 1483 { 1484 if(c) { 1485 unsigned int i; 1486 free(c->filename); 1487 for(i = 0; i < COOKIE_HASH_SIZE; i++) 1488 Curl_cookie_freelist(c->cookies[i]); 1489 free(c); /* free the base struct as well */ 1490 } 1491 } 1492 1493 /* get_netscape_format() 1494 * 1495 * Formats a string for Netscape output file, w/o a newline at the end. 1496 * 1497 * Function returns a char * to a formatted line. Has to be free()d 1498 */ 1499 static char *get_netscape_format(const struct Cookie *co) 1500 { 1501 return aprintf( 1502 "%s" /* httponly preamble */ 1503 "%s%s\t" /* domain */ 1504 "%s\t" /* tailmatch */ 1505 "%s\t" /* path */ 1506 "%s\t" /* secure */ 1507 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ 1508 "%s\t" /* name */ 1509 "%s", /* value */ 1510 co->httponly?"#HttpOnly_":"", 1511 /* Make sure all domains are prefixed with a dot if they allow 1512 tailmatching. This is Mozilla-style. */ 1513 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", 1514 co->domain?co->domain:"unknown", 1515 co->tailmatch?"TRUE":"FALSE", 1516 co->path?co->path:"/", 1517 co->secure?"TRUE":"FALSE", 1518 co->expires, 1519 co->name, 1520 co->value?co->value:""); 1521 } 1522 1523 /* 1524 * cookie_output() 1525 * 1526 * Writes all internally known cookies to the specified file. Specify 1527 * "-" as file name to write to stdout. 1528 * 1529 * The function returns non-zero on write failure. 1530 */ 1531 static int cookie_output(struct CookieInfo *c, const char *dumphere) 1532 { 1533 struct Cookie *co; 1534 FILE *out; 1535 bool use_stdout = FALSE; 1536 char *format_ptr; 1537 unsigned int i; 1538 unsigned int j; 1539 struct Cookie **array; 1540 1541 if(!c) 1542 /* no cookie engine alive */ 1543 return 0; 1544 1545 /* at first, remove expired cookies */ 1546 remove_expired(c); 1547 1548 if(!strcmp("-", dumphere)) { 1549 /* use stdout */ 1550 out = stdout; 1551 use_stdout = TRUE; 1552 } 1553 else { 1554 out = fopen(dumphere, FOPEN_WRITETEXT); 1555 if(!out) { 1556 return 1; /* failure */ 1557 } 1558 } 1559 1560 fputs("# Netscape HTTP Cookie File\n" 1561 "# https://curl.haxx.se/docs/http-cookies.html\n" 1562 "# This file was generated by libcurl! Edit at your own risk.\n\n", 1563 out); 1564 1565 if(c->numcookies) { 1566 array = malloc(sizeof(struct Cookie *) * c->numcookies); 1567 if(!array) { 1568 if(!use_stdout) 1569 fclose(out); 1570 return 1; 1571 } 1572 1573 j = 0; 1574 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1575 for(co = c->cookies[i]; co; co = co->next) { 1576 if(!co->domain) 1577 continue; 1578 array[j++] = co; 1579 } 1580 } 1581 1582 qsort(array, c->numcookies, sizeof(struct Cookie *), cookie_sort_ct); 1583 1584 for(i = 0; i < j; i++) { 1585 format_ptr = get_netscape_format(array[i]); 1586 if(format_ptr == NULL) { 1587 fprintf(out, "#\n# Fatal libcurl error\n"); 1588 free(array); 1589 if(!use_stdout) 1590 fclose(out); 1591 return 1; 1592 } 1593 fprintf(out, "%s\n", format_ptr); 1594 free(format_ptr); 1595 } 1596 1597 free(array); 1598 } 1599 if(!use_stdout) 1600 fclose(out); 1601 1602 return 0; 1603 } 1604 1605 static struct curl_slist *cookie_list(struct Curl_easy *data) 1606 { 1607 struct curl_slist *list = NULL; 1608 struct curl_slist *beg; 1609 struct Cookie *c; 1610 char *line; 1611 unsigned int i; 1612 1613 if((data->cookies == NULL) || 1614 (data->cookies->numcookies == 0)) 1615 return NULL; 1616 1617 for(i = 0; i < COOKIE_HASH_SIZE; i++) { 1618 for(c = data->cookies->cookies[i]; c; c = c->next) { 1619 if(!c->domain) 1620 continue; 1621 line = get_netscape_format(c); 1622 if(!line) { 1623 curl_slist_free_all(list); 1624 return NULL; 1625 } 1626 beg = Curl_slist_append_nodup(list, line); 1627 if(!beg) { 1628 free(line); 1629 curl_slist_free_all(list); 1630 return NULL; 1631 } 1632 list = beg; 1633 } 1634 } 1635 1636 return list; 1637 } 1638 1639 struct curl_slist *Curl_cookie_list(struct Curl_easy *data) 1640 { 1641 struct curl_slist *list; 1642 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1643 list = cookie_list(data); 1644 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 1645 return list; 1646 } 1647 1648 void Curl_flush_cookies(struct Curl_easy *data, int cleanup) 1649 { 1650 if(data->set.str[STRING_COOKIEJAR]) { 1651 if(data->change.cookielist) { 1652 /* If there is a list of cookie files to read, do it first so that 1653 we have all the told files read before we write the new jar. 1654 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ 1655 Curl_cookie_loadfiles(data); 1656 } 1657 1658 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1659 1660 /* if we have a destination file for all the cookies to get dumped to */ 1661 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR])) 1662 infof(data, "WARNING: failed to save cookies in %s\n", 1663 data->set.str[STRING_COOKIEJAR]); 1664 } 1665 else { 1666 if(cleanup && data->change.cookielist) { 1667 /* since nothing is written, we can just free the list of cookie file 1668 names */ 1669 curl_slist_free_all(data->change.cookielist); /* clean up list */ 1670 data->change.cookielist = NULL; 1671 } 1672 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1673 } 1674 1675 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { 1676 Curl_cookie_cleanup(data->cookies); 1677 } 1678 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 1679 } 1680 1681 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ 1682