VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/cookie.c@ 85902

Last change on this file since 85902 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette