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