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