comparison mupdf-source/thirdparty/curl/lib/cookie.c @ 2:b50eed0cc0ef upstream

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