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