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