1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
26 RECEIVING COOKIE INFORMATION
27 ============================
29 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
30 const char *file, struct CookieInfo *inc, bool newsession);
32 Inits a cookie struct to store data in a local file. This is always
33 called before any cookies are set.
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);
39 The 'lineptr' parameter is a full "Set-cookie:" line as
40 received from a server.
42 The function need to replace previously stored lines that this new
45 It may remove lines that are expired.
47 It should return an indication of success/error.
50 SENDING COOKIE INFORMATION
51 ==========================
53 struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
54 char *host, char *path, bool secure);
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
61 It shall only return cookies that haven't expired.
64 Example set of cookies:
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
78 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
79 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
83 #include "curl_setup.h"
85 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
94 #include "strtoofft.h"
96 #include "curl_get_line.h"
97 #include "curl_memrchr.h"
98 #include "inet_pton.h"
99 #include "parsedate.h"
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"
108 static void freecookie(struct Cookie *co)
121 static bool tailmatch(const char *cooke_domain, const char *hostname)
123 size_t cookie_domain_len = strlen(cooke_domain);
124 size_t hostname_len = strlen(hostname);
126 if(hostname_len < cookie_domain_len)
129 if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len))
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.
139 if(hostname_len == cookie_domain_len)
141 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
147 * Return true if the given string is an IP(v4|v6) address.
149 static bool isip(const char *domain)
153 struct in6_addr addr6;
156 if(Curl_inet_pton(AF_INET, domain, &addr)
158 || Curl_inet_pton(AF_INET6, domain, &addr6)
161 /* domain name given as IP address */
169 * matching cookie path and url path
170 * RFC6265 5.1.4 Paths and Path-Match
172 static bool pathmatch(const char *cookie_path, const char *request_uri)
174 size_t cookie_path_len;
176 char *uri_path = NULL;
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 '/' */
187 uri_path = strdup(request_uri);
190 pos = strchr(uri_path, '?');
194 /* #-fragments are already cut off! */
195 if(0 == strlen(uri_path) || uri_path[0] != '/') {
197 uri_path = strdup("/");
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
207 Ignore this algorithm because /hoge is uri path for this case
211 uri_path_len = strlen(uri_path);
213 if(uri_path_len < cookie_path_len) {
218 /* not using checkprefix() because matching should be case-sensitive */
219 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
224 /* The cookie-path and the uri-path are identical. */
225 if(cookie_path_len == uri_path_len) {
230 /* here, cookie_path_len < uri_path_len */
231 if(uri_path[cookie_path_len] == '/') {
244 * Return the top-level domain, for optimal hashing.
246 static const char *get_top_domain(const char * const domain, size_t *outlen)
249 const char *first = NULL, *last;
252 len = strlen(domain);
253 last = memrchr(domain, '.', len);
255 first = memrchr(domain, '.', (last - domain));
257 len -= (++first - domain);
264 return first? first: domain;
268 * A case-insensitive hash for the cookie domains.
270 static size_t cookie_hash_domain(const char *domain, const size_t len)
272 const char *end = domain + len;
275 while(domain < end) {
277 h ^= Curl_raw_toupper(*domain++);
280 return (h % COOKIE_HASH_SIZE);
286 static size_t cookiehash(const char * const domain)
291 if(!domain || isip(domain))
294 top = get_top_domain(domain, &len);
295 return cookie_hash_domain(top, len);
299 * cookie path sanitize
301 static char *sanitize_cookie_path(const char *cookie_path)
304 char *new_path = strdup(cookie_path);
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);
314 if(len && (new_path[len - 1] == '\"')) {
315 new_path[len - 1] = 0x0;
319 /* RFC6265 5.2.4 The Path Attribute */
320 if(new_path[0] != '/') {
321 /* Let cookie-path be the default-path. */
323 new_path = strdup("/");
327 /* convert /hoge/ to /hoge */
328 if(len && new_path[len - 1] == '/') {
329 new_path[len - 1] = 0x0;
336 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
338 * NOTE: OOM or cookie parsing failures are ignored.
340 void Curl_cookie_loadfiles(struct Curl_easy *data)
342 struct curl_slist *list = data->change.cookielist;
344 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
346 struct CookieInfo *newcookies = Curl_cookie_init(data,
349 data->set.cookiesession);
351 /* Failure may be due to OOM or a bad cookie; both are ignored
352 * but only the first should be
354 infof(data, "ignoring failed cookie_init for %s\n", list->data);
356 data->cookies = newcookies;
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);
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.
369 * It is meant to easily replace strdup()
371 static void strstore(char **str, const char *newstr)
374 *str = strdup(newstr);
378 * remove_expired() removes expired cookies.
380 static void remove_expired(struct CookieInfo *cookies)
382 struct Cookie *co, *nx;
383 curl_off_t now = (curl_off_t)time(NULL);
386 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
387 struct Cookie *pv = NULL;
388 co = cookies->cookies[i];
391 if(co->expires && co->expires < now) {
393 cookies->cookies[i] = co->next;
398 cookies->numcookies--;
409 /* Make sure domain contains a dot or is localhost. */
410 static bool bad_domain(const char *domain)
412 return !strchr(domain, '.') && !strcasecompare(domain, "localhost");
415 /****************************************************************************
419 * Add a single cookie line to the cookie keeping object.
421 * Be aware that sometimes we get an IP-only host name, and that might also be
422 * a numerical IPv6 address.
424 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
425 * as they should be treated separately.
426 ***************************************************************************/
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 */
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
442 bool secure) /* TRUE if connection is over secure origin */
444 struct Cookie *clist;
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 */
452 #ifdef CURL_DISABLE_VERBOSE_STRINGS
456 /* First, alloc and init a new struct for it */
457 co = calloc(1, sizeof(struct Cookie));
459 return NULL; /* bail out if we're this low on memory */
462 /* This line was read off a HTTP-header */
468 size_t linelength = strlen(lineptr);
469 if(linelength > MAX_COOKIE_LINE) {
470 /* discard overly long lines at once */
475 semiptr = strchr(lineptr, ';'); /* first, find a semicolon */
477 while(*lineptr && ISBLANK(*lineptr))
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]",
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. */
493 size_t len = strlen(what);
494 size_t nlen = strlen(name);
495 const char *endofn = &ptr[ nlen ];
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
503 infof(data, "oversized cookie dropped, name/val %zu + %zu bytes\n",
508 /* name ends with a '=' ? */
509 sep = (*endofn == '=')?TRUE:FALSE;
512 endofn--; /* move to the last character */
513 if(ISBLANK(*endofn)) {
514 /* skip trailing spaces in name */
515 while(*endofn && ISBLANK(*endofn) && nlen) {
519 name[nlen] = 0; /* new end of name */
523 /* Strip off trailing whitespace from the 'what' */
524 while(len && ISBLANK(what[len-1])) {
529 /* Skip leading whitespace from the 'what' */
531 while(*whatptr && ISBLANK(*whatptr))
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.
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;
548 /* The very first name/value pair is the actual cookie name */
550 /* Bad name/value pair. */
554 co->name = strdup(name);
555 co->value = strdup(whatptr);
557 if(!co->name || !co->value) {
563 /* this was a "<name>=" with no content, and we must allow
564 'secure' and 'httponly' specified this weirdly */
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
571 if(strcasecompare("secure", name)) {
572 if(secure || !c->running) {
580 else if(strcasecompare("httponly", name))
583 /* there was a '=' so we're not done parsing this field */
588 else if(strcasecompare("path", name)) {
589 strstore(&co->path, whatptr);
591 badcookie = TRUE; /* out of memory bad */
594 free(co->spath); /* if this is set again */
595 co->spath = sanitize_cookie_path(co->path);
597 badcookie = TRUE; /* out of memory bad */
601 else if(strcasecompare("domain", name)) {
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. */
607 if('.' == whatptr[0])
608 whatptr++; /* ignore preceding dot */
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".
616 if(bad_domain(whatptr))
620 is_ip = isip(domain ? domain : whatptr);
623 || (is_ip && !strcmp(whatptr, domain))
624 || (!is_ip && tailmatch(whatptr, domain))) {
625 strstore(&co->domain, whatptr);
631 co->tailmatch = TRUE; /* we always do that if the domain name was
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
639 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
643 else if(strcasecompare("version", name)) {
644 strstore(&co->version, whatptr);
650 else if(strcasecompare("max-age", name)) {
651 /* Defined in RFC2109:
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.
660 strstore(&co->maxage, whatptr);
666 else if(strcasecompare("expires", name)) {
667 strstore(&co->expirestr, whatptr);
674 else this is the second (or more) name we don't know
678 /* this is an "illegal" <what>=<this> pair */
681 if(!semiptr || !*semiptr) {
682 /* we already know there are no more cookies */
688 while(*ptr && ISBLANK(*ptr))
690 semiptr = strchr(ptr, ';'); /* now, find the next semicolon */
693 /* There are no more semicolons, but there's a final name=value pair
695 semiptr = strchr(ptr, '\0');
700 offt = curlx_strtoofft((*co->maxage == '\"')?
701 &co->maxage[1]:&co->maxage[0], NULL, 10,
703 if(offt == CURL_OFFT_FLOW)
704 /* overflow, used max value */
705 co->expires = CURL_OFF_T_MAX;
708 /* already expired */
710 else if(CURL_OFF_T_MAX - now < co->expires)
712 co->expires = CURL_OFF_T_MAX;
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);
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 */
727 else if(co->expires < 0)
731 if(!badcookie && !co->domain) {
733 /* no domain was given in the header line, set the default */
734 co->domain = strdup(domain);
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, '?');
746 /* queryp is where the interesting part of the path ends, so now we
747 want to the find the last */
750 endslash = strrchr(path, '/');
752 endslash = memrchr(path, '/', (queryp - path));
754 size_t pathlen = (endslash-path + 1); /* include end slash */
755 co->path = malloc(pathlen + 1); /* one extra for the zero byte */
757 memcpy(co->path, path, pathlen);
758 co->path[pathlen] = 0; /* null-terminate */
759 co->spath = sanitize_cookie_path(co->path);
761 badcookie = TRUE; /* out of memory bad */
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 */
777 /* This line is NOT a HTTP header style line, we do offer support for
778 reading the odd netscape cookies-file format here */
781 char *tok_buf = NULL;
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..
791 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
796 if(lineptr[0]=='#') {
797 /* don't even try the comments */
801 /* strip off the possible end-of-line characters */
802 ptr = strchr(lineptr, '\r');
804 *ptr = 0; /* clear it */
805 ptr = strchr(lineptr, '\n');
807 *ptr = 0; /* clear it */
809 firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
811 /* Now loop through the fields and init the struct we already have
813 for(ptr = firstptr, fields = 0; ptr && !badcookie;
814 ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
817 if(ptr[0]=='.') /* skip preceding dots */
819 co->domain = strdup(ptr);
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
828 co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
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);
838 co->spath = sanitize_cookie_path(co->path);
840 badcookie = TRUE; /* out of memory bad */
845 /* this doesn't look like a path, make one up! */
846 co->path = strdup("/");
849 co->spath = strdup("/");
852 fields++; /* add a field and fall down to secure */
856 if(strcasecompare(ptr, "TRUE")) {
857 if(secure || c->running)
864 if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
868 co->name = strdup(ptr);
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;
880 co->value = strdup(ptr);
887 /* we got a cookie with blank contents, fix it */
888 co->value = strdup("");
895 if(!badcookie && (7 != fields))
896 /* we did not find the sufficient number of fields */
906 if(co->prefix & COOKIE_PREFIX__SECURE) {
907 /* The __Secure- prefix only requires that the cookie be set secure */
913 if(co->prefix & COOKIE_PREFIX__HOST) {
915 * The __Host- prefix requires the cookie to be secure, have a "/" path
916 * and not have a domain set.
918 if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
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! */
933 co->livecookie = c->running;
934 co->creationtime = ++c->lastct;
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 */
940 /* at first, remove expired cookies */
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);
951 acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
952 Curl_psl_release(data);
955 acceptable = !bad_domain(domain);
958 infof(data, "cookie '%s' dropped, domain '%s' must not "
959 "set cookies for '%s'\n", co->name, domain, co->domain);
966 myhash = cookiehash(co->domain);
967 clist = c->cookies[myhash];
970 if(strcasecompare(clist->name, co->name)) {
971 /* the names are identical */
973 if(clist->domain && co->domain) {
974 if(strcasecompare(clist->domain, co->domain) &&
975 (clist->tailmatch == co->tailmatch))
976 /* The domains are identical */
979 else if(!clist->domain && !co->domain)
983 /* the domains were identical */
985 if(clist->spath && co->spath) {
986 if(clist->secure && !co->secure && !secure) {
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.
997 sep = strchr(clist->spath + 1, '/');
1000 cllen = sep - clist->spath;
1002 cllen = strlen(clist->spath);
1004 if(strncasecompare(clist->spath, co->spath, cllen)) {
1009 else if(strcasecompare(clist->spath, co->spath))
1012 replace_old = FALSE;
1014 else if(!clist->spath && !co->spath)
1017 replace_old = FALSE;
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 */
1027 /* Free the newcomer and get out of here! */
1033 co->next = clist->next; /* get the next-pointer first */
1035 /* when replacing, creationtime is kept from old */
1036 co->creationtime = clist->creationtime;
1038 /* then free all the old pointers */
1041 free(clist->domain);
1044 free(clist->expirestr);
1045 free(clist->version);
1046 free(clist->maxage);
1048 *clist = *co; /* then store all the new data */
1050 free(co); /* free the newly allocated memory */
1051 co = clist; /* point to the previous struct instead */
1053 /* We have replaced a cookie, now skip the rest of the list but
1054 make sure the 'lastc' pointer is properly set */
1057 clist = clist->next;
1063 clist = clist->next;
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);
1074 /* then make the last item point on this new one */
1078 c->cookies[myhash] = co;
1079 c->numcookies++; /* one more cookie in the jar */
1086 /*****************************************************************************
1088 * Curl_cookie_init()
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.
1093 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
1095 * Note that 'data' might be called as NULL pointer.
1097 * Returns NULL on out of memory. Invalid cookies are ignored.
1098 ****************************************************************************/
1099 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
1101 struct CookieInfo *inc,
1104 struct CookieInfo *c;
1106 bool fromfile = TRUE;
1110 /* we didn't get a struct, create one */
1111 c = calloc(1, sizeof(struct CookieInfo));
1113 return NULL; /* failed to get memory */
1114 c->filename = strdup(file?file:"none"); /* copy the name just in case */
1116 goto fail; /* failed to get memory */
1119 /* we got an already existing one, use that */
1122 c->running = FALSE; /* this is not running, this is init */
1124 if(file && !strcmp(file, "-")) {
1128 else if(file && !*file) {
1129 /* points to a "" string */
1133 fp = file?fopen(file, FOPEN_READTEXT):NULL;
1135 c->newsession = newsession; /* new session? */
1141 line = malloc(MAX_COOKIE_LINE);
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];
1154 while(*lineptr && ISBLANK(*lineptr))
1157 Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
1159 free(line); /* free the line buffer */
1160 remove_expired(c); /* run this once, not on every cookie */
1166 c->running = TRUE; /* now, we're running */
1168 data->state.cookie_engine = TRUE;
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);
1180 return NULL; /* out of memory */
1183 /* sort this so that the longest path gets before the shorter path */
1184 static int cookie_sort(const void *p1, const void *p2)
1186 struct Cookie *c1 = *(struct Cookie **)p1;
1187 struct Cookie *c2 = *(struct Cookie **)p2;
1190 /* 1 - compare cookie path lengths */
1191 l1 = c1->path ? strlen(c1->path) : 0;
1192 l2 = c2->path ? strlen(c2->path) : 0;
1195 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1197 /* 2 - compare cookie domain lengths */
1198 l1 = c1->domain ? strlen(c1->domain) : 0;
1199 l2 = c2->domain ? strlen(c2->domain) : 0;
1202 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1204 /* 3 - compare cookie name lengths */
1205 l1 = c1->name ? strlen(c1->name) : 0;
1206 l2 = c2->name ? strlen(c2->name) : 0;
1209 return (l2 > l1) ? 1 : -1;
1211 /* 4 - compare cookie creation time */
1212 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1215 /* sort cookies only according to creation time */
1216 static int cookie_sort_ct(const void *p1, const void *p2)
1218 struct Cookie *c1 = *(struct Cookie **)p1;
1219 struct Cookie *c2 = *(struct Cookie **)p2;
1221 return (c2->creationtime > c1->creationtime) ? 1 : -1;
1224 #define CLONE(field) \
1227 d->field = strdup(src->field); \
1233 static struct Cookie *dup_cookie(struct Cookie *src)
1235 struct Cookie *d = calloc(sizeof(struct Cookie), 1);
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;
1259 /*****************************************************************************
1261 * Curl_cookie_getlist()
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.
1267 * It shall only return cookies that haven't expired.
1269 ****************************************************************************/
1271 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1272 const char *host, const char *path,
1275 struct Cookie *newco;
1277 struct Cookie *mainco = NULL;
1280 const size_t myhash = cookiehash(host);
1282 if(!c || !c->cookies[myhash])
1283 return NULL; /* no cookie struct or no cookies in the struct */
1285 /* at first, remove expired cookies */
1288 /* check if host is an IP(v4|v6) address */
1291 co = c->cookies[myhash];
1294 /* if the cookie requires we're secure we must only continue if we are! */
1295 if(co->secure?secure:TRUE) {
1297 /* now check if the domain is correct */
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
1304 /* now check the left part of the path with the cookies path
1306 if(!co->spath || pathmatch(co->spath, path) ) {
1308 /* and now, we know this is a match and we should create an
1309 entry for the return-linked-list */
1311 newco = dup_cookie(co);
1313 /* then modify our next */
1314 newco->next = mainco;
1316 /* point the main to us */
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;
1336 /* alloc an array and store all cookie pointers */
1337 array = malloc(sizeof(struct Cookie *) * matches);
1343 for(i = 0; co; co = co->next)
1346 /* now sort the cookie pointers in path length order */
1347 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1349 /* remake the linked list order according to the new order */
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 */
1356 free(array); /* remove the temporary data again */
1359 return mainco; /* return the new list */
1362 /* failure, clear up the allocated chain and return NULL */
1363 Curl_cookie_freelist(mainco);
1367 /*****************************************************************************
1369 * Curl_cookie_clearall()
1371 * Clear all existing cookies and reset the counter.
1373 ****************************************************************************/
1374 void Curl_cookie_clearall(struct CookieInfo *cookies)
1378 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1379 Curl_cookie_freelist(cookies->cookies[i]);
1380 cookies->cookies[i] = NULL;
1382 cookies->numcookies = 0;
1386 /*****************************************************************************
1388 * Curl_cookie_freelist()
1390 * Free a list of cookies previously returned by Curl_cookie_getlist();
1392 ****************************************************************************/
1394 void Curl_cookie_freelist(struct Cookie *co)
1396 struct Cookie *next;
1405 /*****************************************************************************
1407 * Curl_cookie_clearsess()
1409 * Free all session cookies in the cookies list.
1411 ****************************************************************************/
1412 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1414 struct Cookie *first, *curr, *next, *prev = NULL;
1420 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1421 if(!cookies->cookies[i])
1424 first = curr = prev = cookies->cookies[i];
1426 for(; curr; curr = next) {
1428 if(!curr->expires) {
1438 cookies->numcookies--;
1444 cookies->cookies[i] = first;
1449 /*****************************************************************************
1451 * Curl_cookie_cleanup()
1453 * Free a "cookie object" previous created with Curl_cookie_init().
1455 ****************************************************************************/
1456 void Curl_cookie_cleanup(struct CookieInfo *c)
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 */
1467 /* get_netscape_format()
1469 * Formats a string for Netscape output file, w/o a newline at the end.
1471 * Function returns a char * to a formatted line. Has to be free()d
1473 static char *get_netscape_format(const struct Cookie *co)
1476 "%s" /* httponly preamble */
1477 "%s%s\t" /* domain */
1478 "%s\t" /* tailmatch */
1481 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
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",
1494 co->value?co->value:"");
1500 * Writes all internally known cookies to the specified file. Specify
1501 * "-" as file name to write to stdout.
1503 * The function returns non-zero on write failure.
1505 static int cookie_output(struct Curl_easy *data,
1506 struct CookieInfo *c, const char *filename)
1510 bool use_stdout = FALSE;
1511 char *tempstore = NULL;
1515 /* no cookie engine alive */
1518 /* at first, remove expired cookies */
1521 if(!strcmp("-", filename)) {
1527 unsigned char randsuffix[9];
1529 if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
1532 tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
1536 out = fopen(tempstore, FOPEN_WRITETEXT);
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",
1549 struct Cookie **array;
1551 array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
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) {
1561 array[nvalid++] = co;
1565 qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
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");
1574 fprintf(out, "%s\n", format_ptr);
1584 if(Curl_rename(tempstore, filename)) {
1594 if(out && !use_stdout)
1597 return error ? 1 : 0;
1600 static struct curl_slist *cookie_list(struct Curl_easy *data)
1602 struct curl_slist *list = NULL;
1603 struct curl_slist *beg;
1608 if((data->cookies == NULL) ||
1609 (data->cookies->numcookies == 0))
1612 for(i = 0; i < COOKIE_HASH_SIZE; i++) {
1613 for(c = data->cookies->cookies[i]; c; c = c->next) {
1616 line = get_netscape_format(c);
1618 curl_slist_free_all(list);
1621 beg = Curl_slist_append_nodup(list, line);
1624 curl_slist_free_all(list);
1634 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
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);
1643 void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
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);
1653 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
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]);
1661 if(cleanup && data->change.cookielist) {
1662 /* since nothing is written, we can just free the list of cookie file
1664 curl_slist_free_all(data->change.cookielist); /* clean up list */
1665 data->change.cookielist = NULL;
1667 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1670 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1671 Curl_cookie_cleanup(data->cookies);
1672 data->cookies = NULL;
1674 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1677 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */