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