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