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