1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2014, 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 http://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 26RECEIVING COOKIE INFORMATION 27============================ 28 29struct CookieInfo *cookie_init(char *file); 30 31 Inits a cookie struct to store data in a local file. This is always 32 called before any cookies are set. 33 34int cookies_set(struct CookieInfo *cookie, char *cookie_line); 35 36 The 'cookie_line' parameter is a full "Set-cookie:" line as 37 received from a server. 38 39 The function need to replace previously stored lines that this new 40 line superceeds. 41 42 It may remove lines that are expired. 43 44 It should return an indication of success/error. 45 46 47SENDING COOKIE INFORMATION 48========================== 49 50struct Cookies *cookie_getlist(struct CookieInfo *cookie, 51 char *host, char *path, bool secure); 52 53 For a given host and path, return a linked list of cookies that 54 the client should send to the server if used now. The secure 55 boolean informs the cookie if a secure connection is achieved or 56 not. 57 58 It shall only return cookies that haven't expired. 59 60 61Example set of cookies: 62 63 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure 64 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 65 domain=.fidelity.com; path=/ftgw; secure 66 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 67 domain=.fidelity.com; path=/; secure 68 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 69 domain=.fidelity.com; path=/; secure 70 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 71 domain=.fidelity.com; path=/; secure 72 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 73 domain=.fidelity.com; path=/; secure 74 Set-cookie: 75 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, 76 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure 77****/ 78 79 80#include "curl_setup.h" 81 82#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) 83 84#define _MPRINTF_REPLACE 85#include <curl/mprintf.h> 86 87#include "urldata.h" 88#include "cookie.h" 89#include "strequal.h" 90#include "strtok.h" 91#include "sendf.h" 92#include "slist.h" 93#include "curl_memory.h" 94#include "share.h" 95#include "strtoofft.h" 96#include "rawstr.h" 97#include "curl_memrchr.h" 98 99/* The last #include file should be: */ 100#include "memdebug.h" 101 102static void freecookie(struct Cookie *co) 103{ 104 if(co->expirestr) 105 free(co->expirestr); 106 if(co->domain) 107 free(co->domain); 108 if(co->path) 109 free(co->path); 110 if(co->spath) 111 free(co->spath); 112 if(co->name) 113 free(co->name); 114 if(co->value) 115 free(co->value); 116 if(co->maxage) 117 free(co->maxage); 118 if(co->version) 119 free(co->version); 120 121 free(co); 122} 123 124static bool tailmatch(const char *cooke_domain, const char *hostname) 125{ 126 size_t cookie_domain_len = strlen(cooke_domain); 127 size_t hostname_len = strlen(hostname); 128 129 if(hostname_len < cookie_domain_len) 130 return FALSE; 131 132 if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len)) 133 return FALSE; 134 135 /* A lead char of cookie_domain is not '.'. 136 RFC6265 4.1.2.3. The Domain Attribute says: 137 For example, if the value of the Domain attribute is 138 "example.com", the user agent will include the cookie in the Cookie 139 header when making HTTP requests to example.com, www.example.com, and 140 www.corp.example.com. 141 */ 142 if(hostname_len == cookie_domain_len) 143 return TRUE; 144 if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) 145 return TRUE; 146 return FALSE; 147} 148 149/* 150 * matching cookie path and url path 151 * RFC6265 5.1.4 Paths and Path-Match 152 */ 153static bool pathmatch(const char* cookie_path, const char* request_uri) 154{ 155 size_t cookie_path_len; 156 size_t uri_path_len; 157 char* uri_path = NULL; 158 char* pos; 159 bool ret = FALSE; 160 161 /* cookie_path must not have last '/' separator. ex: /sample */ 162 cookie_path_len = strlen(cookie_path); 163 if(1 == cookie_path_len) { 164 /* cookie_path must be '/' */ 165 return TRUE; 166 } 167 168 uri_path = strdup(request_uri); 169 if(!uri_path) 170 return FALSE; 171 pos = strchr(uri_path, '?'); 172 if(pos) 173 *pos = 0x0; 174 175 /* #-fragments are already cut off! */ 176 if(0 == strlen(uri_path) || uri_path[0] != '/') { 177 free(uri_path); 178 uri_path = strdup("/"); 179 if(!uri_path) 180 return FALSE; 181 } 182 183 /* here, RFC6265 5.1.4 says 184 4. Output the characters of the uri-path from the first character up 185 to, but not including, the right-most %x2F ("/"). 186 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site 187 without redirect. 188 Ignore this algorithm because /hoge is uri path for this case 189 (uri path is not /). 190 */ 191 192 uri_path_len = strlen(uri_path); 193 194 if(uri_path_len < cookie_path_len) { 195 ret = FALSE; 196 goto pathmatched; 197 } 198 199 /* not using checkprefix() because matching should be case-sensitive */ 200 if(strncmp(cookie_path, uri_path, cookie_path_len)) { 201 ret = FALSE; 202 goto pathmatched; 203 } 204 205 /* The cookie-path and the uri-path are identical. */ 206 if(cookie_path_len == uri_path_len) { 207 ret = TRUE; 208 goto pathmatched; 209 } 210 211 /* here, cookie_path_len < url_path_len */ 212 if(uri_path[cookie_path_len] == '/') { 213 ret = TRUE; 214 goto pathmatched; 215 } 216 217 ret = FALSE; 218 219pathmatched: 220 free(uri_path); 221 return ret; 222} 223 224/* 225 * cookie path sanitize 226 */ 227static char *sanitize_cookie_path(const char *cookie_path) 228{ 229 size_t len; 230 char *new_path = strdup(cookie_path); 231 if(!new_path) 232 return NULL; 233 234 /* some stupid site sends path attribute with '"'. */ 235 if(new_path[0] == '\"') { 236 memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path)); 237 } 238 if(new_path[strlen(new_path) - 1] == '\"') { 239 new_path[strlen(new_path) - 1] = 0x0; 240 } 241 242 /* RFC6265 5.2.4 The Path Attribute */ 243 if(new_path[0] != '/') { 244 /* Let cookie-path be the default-path. */ 245 free(new_path); 246 new_path = strdup("/"); 247 return new_path; 248 } 249 250 /* convert /hoge/ to /hoge */ 251 len = strlen(new_path); 252 if(1 < len && new_path[len - 1] == '/') { 253 new_path[len - 1] = 0x0; 254 } 255 256 return new_path; 257} 258 259/* 260 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). 261 */ 262void Curl_cookie_loadfiles(struct SessionHandle *data) 263{ 264 struct curl_slist *list = data->change.cookielist; 265 if(list) { 266 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 267 while(list) { 268 data->cookies = Curl_cookie_init(data, 269 list->data, 270 data->cookies, 271 data->set.cookiesession); 272 list = list->next; 273 } 274 curl_slist_free_all(data->change.cookielist); /* clean up list */ 275 data->change.cookielist = NULL; /* don't do this again! */ 276 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 277 } 278} 279 280/* 281 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL 282 * that will be freed before the allocated string is stored there. 283 * 284 * It is meant to easily replace strdup() 285 */ 286static void strstore(char **str, const char *newstr) 287{ 288 if(*str) 289 free(*str); 290 *str = strdup(newstr); 291} 292 293/* 294 * remove_expired() removes expired cookies. 295 */ 296static void remove_expired(struct CookieInfo *cookies) 297{ 298 struct Cookie *co, *nx, *pv; 299 curl_off_t now = (curl_off_t)time(NULL); 300 301 co = cookies->cookies; 302 pv = NULL; 303 while(co) { 304 nx = co->next; 305 if((co->expirestr || co->maxage) && co->expires < now) { 306 if(co == cookies->cookies) { 307 cookies->cookies = co->next; 308 } 309 else { 310 pv->next = co->next; 311 } 312 cookies->numcookies--; 313 freecookie(co); 314 } 315 else { 316 pv = co; 317 } 318 co = nx; 319 } 320} 321 322/**************************************************************************** 323 * 324 * Curl_cookie_add() 325 * 326 * Add a single cookie line to the cookie keeping object. 327 * 328 * Be aware that sometimes we get an IP-only host name, and that might also be 329 * a numerical IPv6 address. 330 * 331 ***************************************************************************/ 332 333struct Cookie * 334Curl_cookie_add(struct SessionHandle *data, 335 /* The 'data' pointer here may be NULL at times, and thus 336 must only be used very carefully for things that can deal 337 with data being NULL. Such as infof() and similar */ 338 339 struct CookieInfo *c, 340 bool httpheader, /* TRUE if HTTP header-style line */ 341 char *lineptr, /* first character of the line */ 342 const char *domain, /* default domain */ 343 const char *path) /* full path used when this cookie is set, 344 used to get default path for the cookie 345 unless set */ 346{ 347 struct Cookie *clist; 348 char name[MAX_NAME]; 349 struct Cookie *co; 350 struct Cookie *lastc=NULL; 351 time_t now = time(NULL); 352 bool replace_old = FALSE; 353 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ 354 355#ifdef CURL_DISABLE_VERBOSE_STRINGS 356 (void)data; 357#endif 358 359 /* First, alloc and init a new struct for it */ 360 co = calloc(1, sizeof(struct Cookie)); 361 if(!co) 362 return NULL; /* bail out if we're this low on memory */ 363 364 if(httpheader) { 365 /* This line was read off a HTTP-header */ 366 const char *ptr; 367 const char *semiptr; 368 char *what; 369 370 what = malloc(MAX_COOKIE_LINE); 371 if(!what) { 372 free(co); 373 return NULL; 374 } 375 376 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */ 377 378 while(*lineptr && ISBLANK(*lineptr)) 379 lineptr++; 380 381 ptr = lineptr; 382 do { 383 /* we have a <what>=<this> pair or a stand-alone word here */ 384 name[0]=what[0]=0; /* init the buffers */ 385 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%" 386 MAX_COOKIE_LINE_TXT "[^;\r\n]", 387 name, what)) { 388 /* Use strstore() below to properly deal with received cookie 389 headers that have the same string property set more than once, 390 and then we use the last one. */ 391 const char *whatptr; 392 bool done = FALSE; 393 bool sep; 394 size_t len=strlen(what); 395 const char *endofn = &ptr[ strlen(name) ]; 396 397 /* skip trailing spaces in name */ 398 while(*endofn && ISBLANK(*endofn)) 399 endofn++; 400 401 /* name ends with a '=' ? */ 402 sep = (*endofn == '=')?TRUE:FALSE; 403 404 /* Strip off trailing whitespace from the 'what' */ 405 while(len && ISBLANK(what[len-1])) { 406 what[len-1]=0; 407 len--; 408 } 409 410 /* Skip leading whitespace from the 'what' */ 411 whatptr=what; 412 while(*whatptr && ISBLANK(*whatptr)) 413 whatptr++; 414 415 if(!len) { 416 /* this was a "<name>=" with no content, and we must allow 417 'secure' and 'httponly' specified this weirdly */ 418 done = TRUE; 419 if(Curl_raw_equal("secure", name)) 420 co->secure = TRUE; 421 else if(Curl_raw_equal("httponly", name)) 422 co->httponly = TRUE; 423 else if(sep) 424 /* there was a '=' so we're not done parsing this field */ 425 done = FALSE; 426 } 427 if(done) 428 ; 429 else if(Curl_raw_equal("path", name)) { 430 strstore(&co->path, whatptr); 431 if(!co->path) { 432 badcookie = TRUE; /* out of memory bad */ 433 break; 434 } 435 co->spath = sanitize_cookie_path(co->path); 436 if(!co->spath) { 437 badcookie = TRUE; /* out of memory bad */ 438 break; 439 } 440 } 441 else if(Curl_raw_equal("domain", name)) { 442 /* Now, we make sure that our host is within the given domain, 443 or the given domain is not valid and thus cannot be set. */ 444 445 if('.' == whatptr[0]) 446 whatptr++; /* ignore preceding dot */ 447 448 if(!domain || tailmatch(whatptr, domain)) { 449 const char *tailptr=whatptr; 450 if(tailptr[0] == '.') 451 tailptr++; 452 strstore(&co->domain, tailptr); /* don't prefix w/dots 453 internally */ 454 if(!co->domain) { 455 badcookie = TRUE; 456 break; 457 } 458 co->tailmatch=TRUE; /* we always do that if the domain name was 459 given */ 460 } 461 else { 462 /* we did not get a tailmatch and then the attempted set domain 463 is not a domain to which the current host belongs. Mark as 464 bad. */ 465 badcookie=TRUE; 466 infof(data, "skipped cookie with bad tailmatch domain: %s\n", 467 whatptr); 468 } 469 } 470 else if(Curl_raw_equal("version", name)) { 471 strstore(&co->version, whatptr); 472 if(!co->version) { 473 badcookie = TRUE; 474 break; 475 } 476 } 477 else if(Curl_raw_equal("max-age", name)) { 478 /* Defined in RFC2109: 479 480 Optional. The Max-Age attribute defines the lifetime of the 481 cookie, in seconds. The delta-seconds value is a decimal non- 482 negative integer. After delta-seconds seconds elapse, the 483 client should discard the cookie. A value of zero means the 484 cookie should be discarded immediately. 485 486 */ 487 strstore(&co->maxage, whatptr); 488 if(!co->maxage) { 489 badcookie = TRUE; 490 break; 491 } 492 } 493 else if(Curl_raw_equal("expires", name)) { 494 strstore(&co->expirestr, whatptr); 495 if(!co->expirestr) { 496 badcookie = TRUE; 497 break; 498 } 499 } 500 else if(!co->name) { 501 co->name = strdup(name); 502 co->value = strdup(whatptr); 503 if(!co->name || !co->value) { 504 badcookie = TRUE; 505 break; 506 } 507 } 508 /* 509 else this is the second (or more) name we don't know 510 about! */ 511 } 512 else { 513 /* this is an "illegal" <what>=<this> pair */ 514 } 515 516 if(!semiptr || !*semiptr) { 517 /* we already know there are no more cookies */ 518 semiptr = NULL; 519 continue; 520 } 521 522 ptr=semiptr+1; 523 while(*ptr && ISBLANK(*ptr)) 524 ptr++; 525 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */ 526 527 if(!semiptr && *ptr) 528 /* There are no more semicolons, but there's a final name=value pair 529 coming up */ 530 semiptr=strchr(ptr, '\0'); 531 } while(semiptr); 532 533 if(co->maxage) { 534 co->expires = 535 curlx_strtoofft((*co->maxage=='\"')? 536 &co->maxage[1]:&co->maxage[0], NULL, 10); 537 if(CURL_OFF_T_MAX - now < co->expires) 538 /* avoid overflow */ 539 co->expires = CURL_OFF_T_MAX; 540 else 541 co->expires += now; 542 } 543 else if(co->expirestr) { 544 /* Note that if the date couldn't get parsed for whatever reason, 545 the cookie will be treated as a session cookie */ 546 co->expires = curl_getdate(co->expirestr, NULL); 547 548 /* Session cookies have expires set to 0 so if we get that back 549 from the date parser let's add a second to make it a 550 non-session cookie */ 551 if(co->expires == 0) 552 co->expires = 1; 553 else if(co->expires < 0) 554 co->expires = 0; 555 } 556 557 if(!badcookie && !co->domain) { 558 if(domain) { 559 /* no domain was given in the header line, set the default */ 560 co->domain=strdup(domain); 561 if(!co->domain) 562 badcookie = TRUE; 563 } 564 } 565 566 if(!badcookie && !co->path && path) { 567 /* No path was given in the header line, set the default. 568 Note that the passed-in path to this function MAY have a '?' and 569 following part that MUST not be stored as part of the path. */ 570 char *queryp = strchr(path, '?'); 571 572 /* queryp is where the interesting part of the path ends, so now we 573 want to the find the last */ 574 char *endslash; 575 if(!queryp) 576 endslash = strrchr(path, '/'); 577 else 578 endslash = memrchr(path, '/', (size_t)(queryp - path)); 579 if(endslash) { 580 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */ 581 co->path=malloc(pathlen+1); /* one extra for the zero byte */ 582 if(co->path) { 583 memcpy(co->path, path, pathlen); 584 co->path[pathlen]=0; /* zero terminate */ 585 co->spath = sanitize_cookie_path(co->path); 586 if(!co->spath) 587 badcookie = TRUE; /* out of memory bad */ 588 } 589 else 590 badcookie = TRUE; 591 } 592 } 593 594 free(what); 595 596 if(badcookie || !co->name) { 597 /* we didn't get a cookie name or a bad one, 598 this is an illegal line, bail out */ 599 freecookie(co); 600 return NULL; 601 } 602 603 } 604 else { 605 /* This line is NOT a HTTP header style line, we do offer support for 606 reading the odd netscape cookies-file format here */ 607 char *ptr; 608 char *firstptr; 609 char *tok_buf=NULL; 610 int fields; 611 612 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies 613 marked with httpOnly after the domain name are not accessible 614 from javascripts, but since curl does not operate at javascript 615 level, we include them anyway. In Firefox's cookie files, these 616 lines are preceded with #HttpOnly_ and then everything is 617 as usual, so we skip 10 characters of the line.. 618 */ 619 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { 620 lineptr += 10; 621 co->httponly = TRUE; 622 } 623 624 if(lineptr[0]=='#') { 625 /* don't even try the comments */ 626 free(co); 627 return NULL; 628 } 629 /* strip off the possible end-of-line characters */ 630 ptr=strchr(lineptr, '\r'); 631 if(ptr) 632 *ptr=0; /* clear it */ 633 ptr=strchr(lineptr, '\n'); 634 if(ptr) 635 *ptr=0; /* clear it */ 636 637 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */ 638 639 /* Now loop through the fields and init the struct we already have 640 allocated */ 641 for(ptr=firstptr, fields=0; ptr && !badcookie; 642 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) { 643 switch(fields) { 644 case 0: 645 if(ptr[0]=='.') /* skip preceding dots */ 646 ptr++; 647 co->domain = strdup(ptr); 648 if(!co->domain) 649 badcookie = TRUE; 650 break; 651 case 1: 652 /* This field got its explanation on the 23rd of May 2001 by 653 Andr�s Garc�a: 654 655 flag: A TRUE/FALSE value indicating if all machines within a given 656 domain can access the variable. This value is set automatically by 657 the browser, depending on the value you set for the domain. 658 659 As far as I can see, it is set to true when the cookie says 660 .domain.com and to false when the domain is complete www.domain.com 661 */ 662 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE; 663 break; 664 case 2: 665 /* It turns out, that sometimes the file format allows the path 666 field to remain not filled in, we try to detect this and work 667 around it! Andr�s Garc�a made us aware of this... */ 668 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { 669 /* only if the path doesn't look like a boolean option! */ 670 co->path = strdup(ptr); 671 if(!co->path) 672 badcookie = TRUE; 673 else { 674 co->spath = sanitize_cookie_path(co->path); 675 if(!co->spath) { 676 badcookie = TRUE; /* out of memory bad */ 677 } 678 } 679 break; 680 } 681 /* this doesn't look like a path, make one up! */ 682 co->path = strdup("/"); 683 if(!co->path) 684 badcookie = TRUE; 685 co->spath = strdup("/"); 686 if(!co->spath) 687 badcookie = TRUE; 688 fields++; /* add a field and fall down to secure */ 689 /* FALLTHROUGH */ 690 case 3: 691 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE; 692 break; 693 case 4: 694 co->expires = curlx_strtoofft(ptr, NULL, 10); 695 break; 696 case 5: 697 co->name = strdup(ptr); 698 if(!co->name) 699 badcookie = TRUE; 700 break; 701 case 6: 702 co->value = strdup(ptr); 703 if(!co->value) 704 badcookie = TRUE; 705 break; 706 } 707 } 708 if(6 == fields) { 709 /* we got a cookie with blank contents, fix it */ 710 co->value = strdup(""); 711 if(!co->value) 712 badcookie = TRUE; 713 else 714 fields++; 715 } 716 717 if(!badcookie && (7 != fields)) 718 /* we did not find the sufficient number of fields */ 719 badcookie = TRUE; 720 721 if(badcookie) { 722 freecookie(co); 723 return NULL; 724 } 725 726 } 727 728 if(!c->running && /* read from a file */ 729 c->newsession && /* clean session cookies */ 730 !co->expires) { /* this is a session cookie since it doesn't expire! */ 731 freecookie(co); 732 return NULL; 733 } 734 735 co->livecookie = c->running; 736 737 /* now, we have parsed the incoming line, we must now check if this 738 superceeds an already existing cookie, which it may if the previous have 739 the same domain and path as this */ 740 741 /* at first, remove expired cookies */ 742 remove_expired(c); 743 744 clist = c->cookies; 745 replace_old = FALSE; 746 while(clist) { 747 if(Curl_raw_equal(clist->name, co->name)) { 748 /* the names are identical */ 749 750 if(clist->domain && co->domain) { 751 if(Curl_raw_equal(clist->domain, co->domain)) 752 /* The domains are identical */ 753 replace_old=TRUE; 754 } 755 else if(!clist->domain && !co->domain) 756 replace_old = TRUE; 757 758 if(replace_old) { 759 /* the domains were identical */ 760 761 if(clist->spath && co->spath) { 762 if(Curl_raw_equal(clist->spath, co->spath)) { 763 replace_old = TRUE; 764 } 765 else 766 replace_old = FALSE; 767 } 768 else if(!clist->spath && !co->spath) 769 replace_old = TRUE; 770 else 771 replace_old = FALSE; 772 773 } 774 775 if(replace_old && !co->livecookie && clist->livecookie) { 776 /* Both cookies matched fine, except that the already present 777 cookie is "live", which means it was set from a header, while 778 the new one isn't "live" and thus only read from a file. We let 779 live cookies stay alive */ 780 781 /* Free the newcomer and get out of here! */ 782 freecookie(co); 783 return NULL; 784 } 785 786 if(replace_old) { 787 co->next = clist->next; /* get the next-pointer first */ 788 789 /* then free all the old pointers */ 790 free(clist->name); 791 if(clist->value) 792 free(clist->value); 793 if(clist->domain) 794 free(clist->domain); 795 if(clist->path) 796 free(clist->path); 797 if(clist->spath) 798 free(clist->spath); 799 if(clist->expirestr) 800 free(clist->expirestr); 801 802 if(clist->version) 803 free(clist->version); 804 if(clist->maxage) 805 free(clist->maxage); 806 807 *clist = *co; /* then store all the new data */ 808 809 free(co); /* free the newly alloced memory */ 810 co = clist; /* point to the previous struct instead */ 811 812 /* We have replaced a cookie, now skip the rest of the list but 813 make sure the 'lastc' pointer is properly set */ 814 do { 815 lastc = clist; 816 clist = clist->next; 817 } while(clist); 818 break; 819 } 820 } 821 lastc = clist; 822 clist = clist->next; 823 } 824 825 if(c->running) 826 /* Only show this when NOT reading the cookies from a file */ 827 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " 828 "expire %" CURL_FORMAT_CURL_OFF_T "\n", 829 replace_old?"Replaced":"Added", co->name, co->value, 830 co->domain, co->path, co->expires); 831 832 if(!replace_old) { 833 /* then make the last item point on this new one */ 834 if(lastc) 835 lastc->next = co; 836 else 837 c->cookies = co; 838 c->numcookies++; /* one more cookie in the jar */ 839 } 840 841 return co; 842} 843 844/***************************************************************************** 845 * 846 * Curl_cookie_init() 847 * 848 * Inits a cookie struct to read data from a local file. This is always 849 * called before any cookies are set. File may be NULL. 850 * 851 * If 'newsession' is TRUE, discard all "session cookies" on read from file. 852 * 853 ****************************************************************************/ 854struct CookieInfo *Curl_cookie_init(struct SessionHandle *data, 855 const char *file, 856 struct CookieInfo *inc, 857 bool newsession) 858{ 859 struct CookieInfo *c; 860 FILE *fp; 861 bool fromfile=TRUE; 862 863 if(NULL == inc) { 864 /* we didn't get a struct, create one */ 865 c = calloc(1, sizeof(struct CookieInfo)); 866 if(!c) 867 return NULL; /* failed to get memory */ 868 c->filename = strdup(file?file:"none"); /* copy the name just in case */ 869 } 870 else { 871 /* we got an already existing one, use that */ 872 c = inc; 873 } 874 c->running = FALSE; /* this is not running, this is init */ 875 876 if(file && strequal(file, "-")) { 877 fp = stdin; 878 fromfile=FALSE; 879 } 880 else if(file && !*file) { 881 /* points to a "" string */ 882 fp = NULL; 883 } 884 else 885 fp = file?fopen(file, "r"):NULL; 886 887 c->newsession = newsession; /* new session? */ 888 889 if(fp) { 890 char *lineptr; 891 bool headerline; 892 893 char *line = malloc(MAX_COOKIE_LINE); 894 if(line) { 895 while(fgets(line, MAX_COOKIE_LINE, fp)) { 896 if(checkprefix("Set-Cookie:", line)) { 897 /* This is a cookie line, get it! */ 898 lineptr=&line[11]; 899 headerline=TRUE; 900 } 901 else { 902 lineptr=line; 903 headerline=FALSE; 904 } 905 while(*lineptr && ISBLANK(*lineptr)) 906 lineptr++; 907 908 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL); 909 } 910 free(line); /* free the line buffer */ 911 } 912 if(fromfile) 913 fclose(fp); 914 } 915 916 c->running = TRUE; /* now, we're running */ 917 918 return c; 919} 920 921/* sort this so that the longest path gets before the shorter path */ 922static int cookie_sort(const void *p1, const void *p2) 923{ 924 struct Cookie *c1 = *(struct Cookie **)p1; 925 struct Cookie *c2 = *(struct Cookie **)p2; 926 size_t l1, l2; 927 928 /* 1 - compare cookie path lengths */ 929 l1 = c1->path ? strlen(c1->path) : 0; 930 l2 = c2->path ? strlen(c2->path) : 0; 931 932 if(l1 != l2) 933 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 934 935 /* 2 - compare cookie domain lengths */ 936 l1 = c1->domain ? strlen(c1->domain) : 0; 937 l2 = c2->domain ? strlen(c2->domain) : 0; 938 939 if(l1 != l2) 940 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 941 942 /* 3 - compare cookie names */ 943 if(c1->name && c2->name) 944 return strcmp(c1->name, c2->name); 945 946 /* sorry, can't be more deterministic */ 947 return 0; 948} 949 950/***************************************************************************** 951 * 952 * Curl_cookie_getlist() 953 * 954 * For a given host and path, return a linked list of cookies that the 955 * client should send to the server if used now. The secure boolean informs 956 * the cookie if a secure connection is achieved or not. 957 * 958 * It shall only return cookies that haven't expired. 959 * 960 ****************************************************************************/ 961 962struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, 963 const char *host, const char *path, 964 bool secure) 965{ 966 struct Cookie *newco; 967 struct Cookie *co; 968 time_t now = time(NULL); 969 struct Cookie *mainco=NULL; 970 size_t matches = 0; 971 972 if(!c || !c->cookies) 973 return NULL; /* no cookie struct or no cookies in the struct */ 974 975 /* at first, remove expired cookies */ 976 remove_expired(c); 977 978 co = c->cookies; 979 980 while(co) { 981 /* only process this cookie if it is not expired or had no expire 982 date AND that if the cookie requires we're secure we must only 983 continue if we are! */ 984 if((!co->expires || (co->expires > now)) && 985 (co->secure?secure:TRUE)) { 986 987 /* now check if the domain is correct */ 988 if(!co->domain || 989 (co->tailmatch && tailmatch(co->domain, host)) || 990 (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) { 991 /* the right part of the host matches the domain stuff in the 992 cookie data */ 993 994 /* now check the left part of the path with the cookies path 995 requirement */ 996 if(!co->spath || pathmatch(co->spath, path) ) { 997 998 /* and now, we know this is a match and we should create an 999 entry for the return-linked-list */ 1000 1001 newco = malloc(sizeof(struct Cookie)); 1002 if(newco) { 1003 /* first, copy the whole source cookie: */ 1004 memcpy(newco, co, sizeof(struct Cookie)); 1005 1006 /* then modify our next */ 1007 newco->next = mainco; 1008 1009 /* point the main to us */ 1010 mainco = newco; 1011 1012 matches++; 1013 } 1014 else { 1015 fail: 1016 /* failure, clear up the allocated chain and return NULL */ 1017 while(mainco) { 1018 co = mainco->next; 1019 free(mainco); 1020 mainco = co; 1021 } 1022 1023 return NULL; 1024 } 1025 } 1026 } 1027 } 1028 co = co->next; 1029 } 1030 1031 if(matches) { 1032 /* Now we need to make sure that if there is a name appearing more than 1033 once, the longest specified path version comes first. To make this 1034 the swiftest way, we just sort them all based on path length. */ 1035 struct Cookie **array; 1036 size_t i; 1037 1038 /* alloc an array and store all cookie pointers */ 1039 array = malloc(sizeof(struct Cookie *) * matches); 1040 if(!array) 1041 goto fail; 1042 1043 co = mainco; 1044 1045 for(i=0; co; co = co->next) 1046 array[i++] = co; 1047 1048 /* now sort the cookie pointers in path length order */ 1049 qsort(array, matches, sizeof(struct Cookie *), cookie_sort); 1050 1051 /* remake the linked list order according to the new order */ 1052 1053 mainco = array[0]; /* start here */ 1054 for(i=0; i<matches-1; i++) 1055 array[i]->next = array[i+1]; 1056 array[matches-1]->next = NULL; /* terminate the list */ 1057 1058 free(array); /* remove the temporary data again */ 1059 } 1060 1061 return mainco; /* return the new list */ 1062} 1063 1064/***************************************************************************** 1065 * 1066 * Curl_cookie_clearall() 1067 * 1068 * Clear all existing cookies and reset the counter. 1069 * 1070 ****************************************************************************/ 1071void Curl_cookie_clearall(struct CookieInfo *cookies) 1072{ 1073 if(cookies) { 1074 Curl_cookie_freelist(cookies->cookies, TRUE); 1075 cookies->cookies = NULL; 1076 cookies->numcookies = 0; 1077 } 1078} 1079 1080/***************************************************************************** 1081 * 1082 * Curl_cookie_freelist() 1083 * 1084 * Free a list of cookies previously returned by Curl_cookie_getlist(); 1085 * 1086 * The 'cookiestoo' argument tells this function whether to just free the 1087 * list or actually also free all cookies within the list as well. 1088 * 1089 ****************************************************************************/ 1090 1091void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo) 1092{ 1093 struct Cookie *next; 1094 if(co) { 1095 while(co) { 1096 next = co->next; 1097 if(cookiestoo) 1098 freecookie(co); 1099 else 1100 free(co); /* we only free the struct since the "members" are all just 1101 pointed out in the main cookie list! */ 1102 co = next; 1103 } 1104 } 1105} 1106 1107 1108/***************************************************************************** 1109 * 1110 * Curl_cookie_clearsess() 1111 * 1112 * Free all session cookies in the cookies list. 1113 * 1114 ****************************************************************************/ 1115void Curl_cookie_clearsess(struct CookieInfo *cookies) 1116{ 1117 struct Cookie *first, *curr, *next, *prev = NULL; 1118 1119 if(!cookies || !cookies->cookies) 1120 return; 1121 1122 first = curr = prev = cookies->cookies; 1123 1124 for(; curr; curr = next) { 1125 next = curr->next; 1126 if(!curr->expires) { 1127 if(first == curr) 1128 first = next; 1129 1130 if(prev == curr) 1131 prev = next; 1132 else 1133 prev->next = next; 1134 1135 freecookie(curr); 1136 cookies->numcookies--; 1137 } 1138 else 1139 prev = curr; 1140 } 1141 1142 cookies->cookies = first; 1143} 1144 1145 1146/***************************************************************************** 1147 * 1148 * Curl_cookie_cleanup() 1149 * 1150 * Free a "cookie object" previous created with cookie_init(). 1151 * 1152 ****************************************************************************/ 1153void Curl_cookie_cleanup(struct CookieInfo *c) 1154{ 1155 struct Cookie *co; 1156 struct Cookie *next; 1157 if(c) { 1158 if(c->filename) 1159 free(c->filename); 1160 co = c->cookies; 1161 1162 while(co) { 1163 next = co->next; 1164 freecookie(co); 1165 co = next; 1166 } 1167 free(c); /* free the base struct as well */ 1168 } 1169} 1170 1171/* get_netscape_format() 1172 * 1173 * Formats a string for Netscape output file, w/o a newline at the end. 1174 * 1175 * Function returns a char * to a formatted line. Has to be free()d 1176*/ 1177static char *get_netscape_format(const struct Cookie *co) 1178{ 1179 return aprintf( 1180 "%s" /* httponly preamble */ 1181 "%s%s\t" /* domain */ 1182 "%s\t" /* tailmatch */ 1183 "%s\t" /* path */ 1184 "%s\t" /* secure */ 1185 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ 1186 "%s\t" /* name */ 1187 "%s", /* value */ 1188 co->httponly?"#HttpOnly_":"", 1189 /* Make sure all domains are prefixed with a dot if they allow 1190 tailmatching. This is Mozilla-style. */ 1191 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", 1192 co->domain?co->domain:"unknown", 1193 co->tailmatch?"TRUE":"FALSE", 1194 co->path?co->path:"/", 1195 co->secure?"TRUE":"FALSE", 1196 co->expires, 1197 co->name, 1198 co->value?co->value:""); 1199} 1200 1201/* 1202 * cookie_output() 1203 * 1204 * Writes all internally known cookies to the specified file. Specify 1205 * "-" as file name to write to stdout. 1206 * 1207 * The function returns non-zero on write failure. 1208 */ 1209static int cookie_output(struct CookieInfo *c, const char *dumphere) 1210{ 1211 struct Cookie *co; 1212 FILE *out; 1213 bool use_stdout=FALSE; 1214 1215 if((NULL == c) || (0 == c->numcookies)) 1216 /* If there are no known cookies, we don't write or even create any 1217 destination file */ 1218 return 0; 1219 1220 /* at first, remove expired cookies */ 1221 remove_expired(c); 1222 1223 if(strequal("-", dumphere)) { 1224 /* use stdout */ 1225 out = stdout; 1226 use_stdout=TRUE; 1227 } 1228 else { 1229 out = fopen(dumphere, "w"); 1230 if(!out) 1231 return 1; /* failure */ 1232 } 1233 1234 if(c) { 1235 char *format_ptr; 1236 1237 fputs("# Netscape HTTP Cookie File\n" 1238 "# http://curl.haxx.se/docs/http-cookies.html\n" 1239 "# This file was generated by libcurl! Edit at your own risk.\n\n", 1240 out); 1241 co = c->cookies; 1242 1243 while(co) { 1244 format_ptr = get_netscape_format(co); 1245 if(format_ptr == NULL) { 1246 fprintf(out, "#\n# Fatal libcurl error\n"); 1247 if(!use_stdout) 1248 fclose(out); 1249 return 1; 1250 } 1251 fprintf(out, "%s\n", format_ptr); 1252 free(format_ptr); 1253 co=co->next; 1254 } 1255 } 1256 1257 if(!use_stdout) 1258 fclose(out); 1259 1260 return 0; 1261} 1262 1263struct curl_slist *Curl_cookie_list(struct SessionHandle *data) 1264{ 1265 struct curl_slist *list = NULL; 1266 struct curl_slist *beg; 1267 struct Cookie *c; 1268 char *line; 1269 1270 if((data->cookies == NULL) || 1271 (data->cookies->numcookies == 0)) 1272 return NULL; 1273 1274 c = data->cookies->cookies; 1275 1276 while(c) { 1277 /* fill the list with _all_ the cookies we know */ 1278 line = get_netscape_format(c); 1279 if(!line) { 1280 curl_slist_free_all(list); 1281 return NULL; 1282 } 1283 beg = Curl_slist_append_nodup(list, line); 1284 if(!beg) { 1285 free(line); 1286 curl_slist_free_all(list); 1287 return NULL; 1288 } 1289 list = beg; 1290 c = c->next; 1291 } 1292 1293 return list; 1294} 1295 1296void Curl_flush_cookies(struct SessionHandle *data, int cleanup) 1297{ 1298 if(data->set.str[STRING_COOKIEJAR]) { 1299 if(data->change.cookielist) { 1300 /* If there is a list of cookie files to read, do it first so that 1301 we have all the told files read before we write the new jar. 1302 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ 1303 Curl_cookie_loadfiles(data); 1304 } 1305 1306 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1307 1308 /* if we have a destination file for all the cookies to get dumped to */ 1309 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR])) 1310 infof(data, "WARNING: failed to save cookies in %s\n", 1311 data->set.str[STRING_COOKIEJAR]); 1312 } 1313 else { 1314 if(cleanup && data->change.cookielist) { 1315 /* since nothing is written, we can just free the list of cookie file 1316 names */ 1317 curl_slist_free_all(data->change.cookielist); /* clean up list */ 1318 data->change.cookielist = NULL; 1319 } 1320 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 1321 } 1322 1323 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { 1324 Curl_cookie_cleanup(data->cookies); 1325 } 1326 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 1327} 1328 1329#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ 1330