Annotation of embedaddon/curl/lib/cookie.c, revision 1.1.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>