VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/cookie.c@ 99371

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

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

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