1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, 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)
97 #include "strtoofft.h"
99 #include "curl_memrchr.h"
100 #include "inet_pton.h"
102 /* The last 3 #include files should be in this order */
103 #include "curl_printf.h"
104 #include "curl_memory.h"
105 #include "memdebug.h"
107 static void freecookie(struct Cookie *co)
120 static bool tailmatch(const char *cooke_domain, const char *hostname)
122 size_t cookie_domain_len = strlen(cooke_domain);
123 size_t hostname_len = strlen(hostname);
125 if(hostname_len < cookie_domain_len)
128 if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len))
131 /* A lead char of cookie_domain is not '.'.
132 RFC6265 4.1.2.3. The Domain Attribute says:
133 For example, if the value of the Domain attribute is
134 "example.com", the user agent will include the cookie in the Cookie
135 header when making HTTP requests to example.com, www.example.com, and
136 www.corp.example.com.
138 if(hostname_len == cookie_domain_len)
140 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
146 * matching cookie path and url path
147 * RFC6265 5.1.4 Paths and Path-Match
149 static bool pathmatch(const char *cookie_path, const char *request_uri)
151 size_t cookie_path_len;
153 char *uri_path = NULL;
157 /* cookie_path must not have last '/' separator. ex: /sample */
158 cookie_path_len = strlen(cookie_path);
159 if(1 == cookie_path_len) {
160 /* cookie_path must be '/' */
164 uri_path = strdup(request_uri);
167 pos = strchr(uri_path, '?');
171 /* #-fragments are already cut off! */
172 if(0 == strlen(uri_path) || uri_path[0] != '/') {
174 uri_path = strdup("/");
179 /* here, RFC6265 5.1.4 says
180 4. Output the characters of the uri-path from the first character up
181 to, but not including, the right-most %x2F ("/").
182 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
184 Ignore this algorithm because /hoge is uri path for this case
188 uri_path_len = strlen(uri_path);
190 if(uri_path_len < cookie_path_len) {
195 /* not using checkprefix() because matching should be case-sensitive */
196 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
201 /* The cookie-path and the uri-path are identical. */
202 if(cookie_path_len == uri_path_len) {
207 /* here, cookie_path_len < url_path_len */
208 if(uri_path[cookie_path_len] == '/') {
221 * cookie path sanitize
223 static char *sanitize_cookie_path(const char *cookie_path)
226 char *new_path = strdup(cookie_path);
230 /* some stupid site sends path attribute with '"'. */
231 len = strlen(new_path);
232 if(new_path[0] == '\"') {
233 memmove((void *)new_path, (const void *)(new_path + 1), len);
236 if(len && (new_path[len - 1] == '\"')) {
237 new_path[len - 1] = 0x0;
241 /* RFC6265 5.2.4 The Path Attribute */
242 if(new_path[0] != '/') {
243 /* Let cookie-path be the default-path. */
245 new_path = strdup("/");
249 /* convert /hoge/ to /hoge */
250 if(len && new_path[len - 1] == '/') {
251 new_path[len - 1] = 0x0;
258 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
260 * NOTE: OOM or cookie parsing failures are ignored.
262 void Curl_cookie_loadfiles(struct Curl_easy *data)
264 struct curl_slist *list = data->change.cookielist;
266 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
268 struct CookieInfo *newcookies = Curl_cookie_init(data,
271 data->set.cookiesession);
273 /* Failure may be due to OOM or a bad cookie; both are ignored
274 * but only the first should be
276 infof(data, "ignoring failed cookie_init for %s\n", list->data);
278 data->cookies = newcookies;
281 curl_slist_free_all(data->change.cookielist); /* clean up list */
282 data->change.cookielist = NULL; /* don't do this again! */
283 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
288 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
289 * that will be freed before the allocated string is stored there.
291 * It is meant to easily replace strdup()
293 static void strstore(char **str, const char *newstr)
296 *str = strdup(newstr);
300 * remove_expired() removes expired cookies.
302 static void remove_expired(struct CookieInfo *cookies)
304 struct Cookie *co, *nx, *pv;
305 curl_off_t now = (curl_off_t)time(NULL);
307 co = cookies->cookies;
311 if(co->expires && co->expires < now) {
313 cookies->cookies = co->next;
318 cookies->numcookies--;
329 * Return true if the given string is an IP(v4|v6) address.
331 static bool isip(const char *domain)
335 struct in6_addr addr6;
338 if(Curl_inet_pton(AF_INET, domain, &addr)
340 || Curl_inet_pton(AF_INET6, domain, &addr6)
343 /* domain name given as IP address */
350 /****************************************************************************
354 * Add a single cookie line to the cookie keeping object.
356 * Be aware that sometimes we get an IP-only host name, and that might also be
357 * a numerical IPv6 address.
359 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
360 * as they should be treated separately.
361 ***************************************************************************/
364 Curl_cookie_add(struct Curl_easy *data,
365 /* The 'data' pointer here may be NULL at times, and thus
366 must only be used very carefully for things that can deal
367 with data being NULL. Such as infof() and similar */
369 struct CookieInfo *c,
370 bool httpheader, /* TRUE if HTTP header-style line */
371 char *lineptr, /* first character of the line */
372 const char *domain, /* default domain */
373 const char *path) /* full path used when this cookie is set,
374 used to get default path for the cookie
377 struct Cookie *clist;
379 struct Cookie *lastc = NULL;
380 time_t now = time(NULL);
381 bool replace_old = FALSE;
382 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
385 const psl_ctx_t *psl;
388 #ifdef CURL_DISABLE_VERBOSE_STRINGS
392 /* First, alloc and init a new struct for it */
393 co = calloc(1, sizeof(struct Cookie));
395 return NULL; /* bail out if we're this low on memory */
398 /* This line was read off a HTTP-header */
404 size_t linelength = strlen(lineptr);
405 if(linelength > MAX_COOKIE_LINE) {
406 /* discard overly long lines at once */
411 semiptr = strchr(lineptr, ';'); /* first, find a semicolon */
413 while(*lineptr && ISBLANK(*lineptr))
418 /* we have a <what>=<this> pair or a stand-alone word here */
419 name[0] = what[0] = 0; /* init the buffers */
420 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%"
421 MAX_NAME_TXT "[^;\r\n]",
423 /* Use strstore() below to properly deal with received cookie
424 headers that have the same string property set more than once,
425 and then we use the last one. */
429 size_t len = strlen(what);
430 size_t nlen = strlen(name);
431 const char *endofn = &ptr[ nlen ];
433 if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
434 ((nlen + len) > MAX_NAME)) {
435 /* too long individual name or contents, or too long combination of
436 name + contents. Chrome and Firefox support 4095 or 4096 bytes
439 infof(data, "oversized cookie dropped, name/val %d + %d bytes\n",
444 /* name ends with a '=' ? */
445 sep = (*endofn == '=')?TRUE:FALSE;
448 endofn--; /* move to the last character */
449 if(ISBLANK(*endofn)) {
450 /* skip trailing spaces in name */
451 while(*endofn && ISBLANK(*endofn) && nlen) {
455 name[nlen] = 0; /* new end of name */
459 /* Strip off trailing whitespace from the 'what' */
460 while(len && ISBLANK(what[len-1])) {
465 /* Skip leading whitespace from the 'what' */
467 while(*whatptr && ISBLANK(*whatptr))
470 if(!co->name && sep) {
471 /* The very first name/value pair is the actual cookie name */
472 co->name = strdup(name);
473 co->value = strdup(whatptr);
474 if(!co->name || !co->value) {
480 /* this was a "<name>=" with no content, and we must allow
481 'secure' and 'httponly' specified this weirdly */
483 if(strcasecompare("secure", name))
485 else if(strcasecompare("httponly", name))
488 /* there was a '=' so we're not done parsing this field */
493 else if(strcasecompare("path", name)) {
494 strstore(&co->path, whatptr);
496 badcookie = TRUE; /* out of memory bad */
499 free(co->spath); /* if this is set again */
500 co->spath = sanitize_cookie_path(co->path);
502 badcookie = TRUE; /* out of memory bad */
506 else if(strcasecompare("domain", name)) {
509 /* Now, we make sure that our host is within the given domain,
510 or the given domain is not valid and thus cannot be set. */
512 if('.' == whatptr[0])
513 whatptr++; /* ignore preceding dot */
517 * Without PSL we don't know when the incoming cookie is set on a
518 * TLD or otherwise "protected" suffix. To reduce risk, we require a
519 * dot OR the exact host name being "localhost".
523 /* check for more dots */
524 dotp = strchr(whatptr, '.');
525 if(!dotp && !strcasecompare("localhost", whatptr))
530 is_ip = isip(domain ? domain : whatptr);
533 || (is_ip && !strcmp(whatptr, domain))
534 || (!is_ip && tailmatch(whatptr, domain))) {
535 strstore(&co->domain, whatptr);
541 co->tailmatch = TRUE; /* we always do that if the domain name was
545 /* we did not get a tailmatch and then the attempted set domain
546 is not a domain to which the current host belongs. Mark as
549 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
553 else if(strcasecompare("version", name)) {
554 strstore(&co->version, whatptr);
560 else if(strcasecompare("max-age", name)) {
561 /* Defined in RFC2109:
563 Optional. The Max-Age attribute defines the lifetime of the
564 cookie, in seconds. The delta-seconds value is a decimal non-
565 negative integer. After delta-seconds seconds elapse, the
566 client should discard the cookie. A value of zero means the
567 cookie should be discarded immediately.
570 strstore(&co->maxage, whatptr);
576 else if(strcasecompare("expires", name)) {
577 strstore(&co->expirestr, whatptr);
584 else this is the second (or more) name we don't know
588 /* this is an "illegal" <what>=<this> pair */
591 if(!semiptr || !*semiptr) {
592 /* we already know there are no more cookies */
598 while(*ptr && ISBLANK(*ptr))
600 semiptr = strchr(ptr, ';'); /* now, find the next semicolon */
603 /* There are no more semicolons, but there's a final name=value pair
605 semiptr = strchr(ptr, '\0');
610 offt = curlx_strtoofft((*co->maxage == '\"')?
611 &co->maxage[1]:&co->maxage[0], NULL, 10,
613 if(offt == CURL_OFFT_FLOW)
614 /* overflow, used max value */
615 co->expires = CURL_OFF_T_MAX;
617 if(CURL_OFF_T_MAX - now < co->expires)
619 co->expires = CURL_OFF_T_MAX;
624 else if(co->expirestr) {
625 /* Note that if the date couldn't get parsed for whatever reason,
626 the cookie will be treated as a session cookie */
627 co->expires = curl_getdate(co->expirestr, NULL);
629 /* Session cookies have expires set to 0 so if we get that back
630 from the date parser let's add a second to make it a
631 non-session cookie */
634 else if(co->expires < 0)
638 if(!badcookie && !co->domain) {
640 /* no domain was given in the header line, set the default */
641 co->domain = strdup(domain);
647 if(!badcookie && !co->path && path) {
648 /* No path was given in the header line, set the default.
649 Note that the passed-in path to this function MAY have a '?' and
650 following part that MUST not be stored as part of the path. */
651 char *queryp = strchr(path, '?');
653 /* queryp is where the interesting part of the path ends, so now we
654 want to the find the last */
657 endslash = strrchr(path, '/');
659 endslash = memrchr(path, '/', (size_t)(queryp - path));
661 size_t pathlen = (size_t)(endslash-path + 1); /* include end slash */
662 co->path = malloc(pathlen + 1); /* one extra for the zero byte */
664 memcpy(co->path, path, pathlen);
665 co->path[pathlen] = 0; /* zero terminate */
666 co->spath = sanitize_cookie_path(co->path);
668 badcookie = TRUE; /* out of memory bad */
675 if(badcookie || !co->name) {
676 /* we didn't get a cookie name or a bad one,
677 this is an illegal line, bail out */
684 /* This line is NOT a HTTP header style line, we do offer support for
685 reading the odd netscape cookies-file format here */
688 char *tok_buf = NULL;
691 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
692 marked with httpOnly after the domain name are not accessible
693 from javascripts, but since curl does not operate at javascript
694 level, we include them anyway. In Firefox's cookie files, these
695 lines are preceded with #HttpOnly_ and then everything is
696 as usual, so we skip 10 characters of the line..
698 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
703 if(lineptr[0]=='#') {
704 /* don't even try the comments */
708 /* strip off the possible end-of-line characters */
709 ptr = strchr(lineptr, '\r');
711 *ptr = 0; /* clear it */
712 ptr = strchr(lineptr, '\n');
714 *ptr = 0; /* clear it */
716 firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
718 /* Now loop through the fields and init the struct we already have
720 for(ptr = firstptr, fields = 0; ptr && !badcookie;
721 ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
724 if(ptr[0]=='.') /* skip preceding dots */
726 co->domain = strdup(ptr);
731 /* This field got its explanation on the 23rd of May 2001 by
734 flag: A TRUE/FALSE value indicating if all machines within a given
735 domain can access the variable. This value is set automatically by
736 the browser, depending on the value you set for the domain.
738 As far as I can see, it is set to true when the cookie says
739 .domain.com and to false when the domain is complete www.domain.com
741 co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
744 /* It turns out, that sometimes the file format allows the path
745 field to remain not filled in, we try to detect this and work
746 around it! Andrés GarcÃa made us aware of this... */
747 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
748 /* only if the path doesn't look like a boolean option! */
749 co->path = strdup(ptr);
753 co->spath = sanitize_cookie_path(co->path);
755 badcookie = TRUE; /* out of memory bad */
760 /* this doesn't look like a path, make one up! */
761 co->path = strdup("/");
764 co->spath = strdup("/");
767 fields++; /* add a field and fall down to secure */
770 co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
773 if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
777 co->name = strdup(ptr);
782 co->value = strdup(ptr);
789 /* we got a cookie with blank contents, fix it */
790 co->value = strdup("");
797 if(!badcookie && (7 != fields))
798 /* we did not find the sufficient number of fields */
808 if(!c->running && /* read from a file */
809 c->newsession && /* clean session cookies */
810 !co->expires) { /* this is a session cookie since it doesn't expire! */
815 co->livecookie = c->running;
817 /* now, we have parsed the incoming line, we must now check if this
818 superceeds an already existing cookie, which it may if the previous have
819 the same domain and path as this */
821 /* at first, remove expired cookies */
825 /* Check if the domain is a Public Suffix and if yes, ignore the cookie.
826 This needs a libpsl compiled with builtin data. */
827 if(domain && co->domain && !isip(co->domain)) {
829 if(psl && !psl_is_cookie_domain_acceptable(psl, domain, co->domain)) {
831 "cookie '%s' dropped, domain '%s' must not set cookies for '%s'\n",
832 co->name, domain, co->domain);
842 if(strcasecompare(clist->name, co->name)) {
843 /* the names are identical */
845 if(clist->domain && co->domain) {
846 if(strcasecompare(clist->domain, co->domain) &&
847 (clist->tailmatch == co->tailmatch))
848 /* The domains are identical */
851 else if(!clist->domain && !co->domain)
855 /* the domains were identical */
857 if(clist->spath && co->spath) {
858 if(strcasecompare(clist->spath, co->spath)) {
864 else if(!clist->spath && !co->spath)
871 if(replace_old && !co->livecookie && clist->livecookie) {
872 /* Both cookies matched fine, except that the already present
873 cookie is "live", which means it was set from a header, while
874 the new one isn't "live" and thus only read from a file. We let
875 live cookies stay alive */
877 /* Free the newcomer and get out of here! */
883 co->next = clist->next; /* get the next-pointer first */
885 /* then free all the old pointers */
891 free(clist->expirestr);
892 free(clist->version);
895 *clist = *co; /* then store all the new data */
897 free(co); /* free the newly alloced memory */
898 co = clist; /* point to the previous struct instead */
900 /* We have replaced a cookie, now skip the rest of the list but
901 make sure the 'lastc' pointer is properly set */
914 /* Only show this when NOT reading the cookies from a file */
915 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
916 "expire %" CURL_FORMAT_CURL_OFF_T "\n",
917 replace_old?"Replaced":"Added", co->name, co->value,
918 co->domain, co->path, co->expires);
921 /* then make the last item point on this new one */
926 c->numcookies++; /* one more cookie in the jar */
933 * get_line() makes sure to only return complete whole lines that fit in 'len'
934 * bytes and end with a newline.
936 static char *get_line(char *buf, int len, FILE *input)
938 bool partial = FALSE;
940 char *b = fgets(buf, len, input);
942 size_t rlen = strlen(b);
943 if(rlen && (b[rlen-1] == '\n')) {
950 /* read a partial, discard the next piece that ends with newline */
960 /*****************************************************************************
964 * Inits a cookie struct to read data from a local file. This is always
965 * called before any cookies are set. File may be NULL.
967 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
969 * Returns NULL on out of memory. Invalid cookies are ignored.
970 ****************************************************************************/
971 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
973 struct CookieInfo *inc,
976 struct CookieInfo *c;
978 bool fromfile = TRUE;
982 /* we didn't get a struct, create one */
983 c = calloc(1, sizeof(struct CookieInfo));
985 return NULL; /* failed to get memory */
986 c->filename = strdup(file?file:"none"); /* copy the name just in case */
988 goto fail; /* failed to get memory */
991 /* we got an already existing one, use that */
994 c->running = FALSE; /* this is not running, this is init */
996 if(file && !strcmp(file, "-")) {
1000 else if(file && !*file) {
1001 /* points to a "" string */
1005 fp = file?fopen(file, FOPEN_READTEXT):NULL;
1007 c->newsession = newsession; /* new session? */
1013 line = malloc(MAX_COOKIE_LINE);
1016 while(get_line(line, MAX_COOKIE_LINE, fp)) {
1017 if(checkprefix("Set-Cookie:", line)) {
1018 /* This is a cookie line, get it! */
1019 lineptr = &line[11];
1026 while(*lineptr && ISBLANK(*lineptr))
1029 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
1031 free(line); /* free the line buffer */
1037 c->running = TRUE; /* now, we're running */
1044 /* Only clean up if we allocated it here, as the original could still be in
1045 * use by a share handle */
1046 Curl_cookie_cleanup(c);
1049 return NULL; /* out of memory */
1052 /* sort this so that the longest path gets before the shorter path */
1053 static int cookie_sort(const void *p1, const void *p2)
1055 struct Cookie *c1 = *(struct Cookie **)p1;
1056 struct Cookie *c2 = *(struct Cookie **)p2;
1059 /* 1 - compare cookie path lengths */
1060 l1 = c1->path ? strlen(c1->path) : 0;
1061 l2 = c2->path ? strlen(c2->path) : 0;
1064 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1066 /* 2 - compare cookie domain lengths */
1067 l1 = c1->domain ? strlen(c1->domain) : 0;
1068 l2 = c2->domain ? strlen(c2->domain) : 0;
1071 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1073 /* 3 - compare cookie names */
1074 if(c1->name && c2->name)
1075 return strcmp(c1->name, c2->name);
1077 /* sorry, can't be more deterministic */
1081 #define CLONE(field) \
1084 d->field = strdup(src->field); \
1090 static struct Cookie *dup_cookie(struct Cookie *src)
1092 struct Cookie *d = calloc(sizeof(struct Cookie), 1);
1102 d->expires = src->expires;
1103 d->tailmatch = src->tailmatch;
1104 d->secure = src->secure;
1105 d->livecookie = src->livecookie;
1106 d->httponly = src->httponly;
1115 /*****************************************************************************
1117 * Curl_cookie_getlist()
1119 * For a given host and path, return a linked list of cookies that the
1120 * client should send to the server if used now. The secure boolean informs
1121 * the cookie if a secure connection is achieved or not.
1123 * It shall only return cookies that haven't expired.
1125 ****************************************************************************/
1127 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1128 const char *host, const char *path,
1131 struct Cookie *newco;
1133 time_t now = time(NULL);
1134 struct Cookie *mainco = NULL;
1138 if(!c || !c->cookies)
1139 return NULL; /* no cookie struct or no cookies in the struct */
1141 /* at first, remove expired cookies */
1144 /* check if host is an IP(v4|v6) address */
1150 /* only process this cookie if it is not expired or had no expire
1151 date AND that if the cookie requires we're secure we must only
1152 continue if we are! */
1153 if((!co->expires || (co->expires > now)) &&
1154 (co->secure?secure:TRUE)) {
1156 /* now check if the domain is correct */
1158 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
1159 ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
1160 /* the right part of the host matches the domain stuff in the
1163 /* now check the left part of the path with the cookies path
1165 if(!co->spath || pathmatch(co->spath, path) ) {
1167 /* and now, we know this is a match and we should create an
1168 entry for the return-linked-list */
1170 newco = dup_cookie(co);
1172 /* then modify our next */
1173 newco->next = mainco;
1175 /* point the main to us */
1182 /* failure, clear up the allocated chain and return NULL */
1183 Curl_cookie_freelist(mainco);
1193 /* Now we need to make sure that if there is a name appearing more than
1194 once, the longest specified path version comes first. To make this
1195 the swiftest way, we just sort them all based on path length. */
1196 struct Cookie **array;
1199 /* alloc an array and store all cookie pointers */
1200 array = malloc(sizeof(struct Cookie *) * matches);
1206 for(i = 0; co; co = co->next)
1209 /* now sort the cookie pointers in path length order */
1210 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1212 /* remake the linked list order according to the new order */
1214 mainco = array[0]; /* start here */
1215 for(i = 0; i<matches-1; i++)
1216 array[i]->next = array[i + 1];
1217 array[matches-1]->next = NULL; /* terminate the list */
1219 free(array); /* remove the temporary data again */
1222 return mainco; /* return the new list */
1225 /*****************************************************************************
1227 * Curl_cookie_clearall()
1229 * Clear all existing cookies and reset the counter.
1231 ****************************************************************************/
1232 void Curl_cookie_clearall(struct CookieInfo *cookies)
1235 Curl_cookie_freelist(cookies->cookies);
1236 cookies->cookies = NULL;
1237 cookies->numcookies = 0;
1241 /*****************************************************************************
1243 * Curl_cookie_freelist()
1245 * Free a list of cookies previously returned by Curl_cookie_getlist();
1247 ****************************************************************************/
1249 void Curl_cookie_freelist(struct Cookie *co)
1251 struct Cookie *next;
1260 /*****************************************************************************
1262 * Curl_cookie_clearsess()
1264 * Free all session cookies in the cookies list.
1266 ****************************************************************************/
1267 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1269 struct Cookie *first, *curr, *next, *prev = NULL;
1271 if(!cookies || !cookies->cookies)
1274 first = curr = prev = cookies->cookies;
1276 for(; curr; curr = next) {
1278 if(!curr->expires) {
1288 cookies->numcookies--;
1294 cookies->cookies = first;
1298 /*****************************************************************************
1300 * Curl_cookie_cleanup()
1302 * Free a "cookie object" previous created with Curl_cookie_init().
1304 ****************************************************************************/
1305 void Curl_cookie_cleanup(struct CookieInfo *c)
1309 Curl_cookie_freelist(c->cookies);
1310 free(c); /* free the base struct as well */
1314 /* get_netscape_format()
1316 * Formats a string for Netscape output file, w/o a newline at the end.
1318 * Function returns a char * to a formatted line. Has to be free()d
1320 static char *get_netscape_format(const struct Cookie *co)
1323 "%s" /* httponly preamble */
1324 "%s%s\t" /* domain */
1325 "%s\t" /* tailmatch */
1328 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1331 co->httponly?"#HttpOnly_":"",
1332 /* Make sure all domains are prefixed with a dot if they allow
1333 tailmatching. This is Mozilla-style. */
1334 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1335 co->domain?co->domain:"unknown",
1336 co->tailmatch?"TRUE":"FALSE",
1337 co->path?co->path:"/",
1338 co->secure?"TRUE":"FALSE",
1341 co->value?co->value:"");
1347 * Writes all internally known cookies to the specified file. Specify
1348 * "-" as file name to write to stdout.
1350 * The function returns non-zero on write failure.
1352 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1356 bool use_stdout = FALSE;
1359 if((NULL == c) || (0 == c->numcookies))
1360 /* If there are no known cookies, we don't write or even create any
1364 /* at first, remove expired cookies */
1367 if(!strcmp("-", dumphere)) {
1373 out = fopen(dumphere, FOPEN_WRITETEXT);
1375 return 1; /* failure */
1378 fputs("# Netscape HTTP Cookie File\n"
1379 "# https://curl.haxx.se/docs/http-cookies.html\n"
1380 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1383 for(co = c->cookies; co; co = co->next) {
1386 format_ptr = get_netscape_format(co);
1387 if(format_ptr == NULL) {
1388 fprintf(out, "#\n# Fatal libcurl error\n");
1393 fprintf(out, "%s\n", format_ptr);
1403 static struct curl_slist *cookie_list(struct Curl_easy *data)
1405 struct curl_slist *list = NULL;
1406 struct curl_slist *beg;
1410 if((data->cookies == NULL) ||
1411 (data->cookies->numcookies == 0))
1414 for(c = data->cookies->cookies; c; c = c->next) {
1417 line = get_netscape_format(c);
1419 curl_slist_free_all(list);
1422 beg = Curl_slist_append_nodup(list, line);
1425 curl_slist_free_all(list);
1434 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
1436 struct curl_slist *list;
1437 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1438 list = cookie_list(data);
1439 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1443 void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
1445 if(data->set.str[STRING_COOKIEJAR]) {
1446 if(data->change.cookielist) {
1447 /* If there is a list of cookie files to read, do it first so that
1448 we have all the told files read before we write the new jar.
1449 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1450 Curl_cookie_loadfiles(data);
1453 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1455 /* if we have a destination file for all the cookies to get dumped to */
1456 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1457 infof(data, "WARNING: failed to save cookies in %s\n",
1458 data->set.str[STRING_COOKIEJAR]);
1461 if(cleanup && data->change.cookielist) {
1462 /* since nothing is written, we can just free the list of cookie file
1464 curl_slist_free_all(data->change.cookielist); /* clean up list */
1465 data->change.cookielist = NULL;
1467 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1470 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1471 Curl_cookie_cleanup(data->cookies);
1473 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1476 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */