VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/cookie.c@ 95312

Last change on this file since 95312 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

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