VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/cookie.c@ 98326

Last change on this file since 98326 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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