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