1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, 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)
98 #include "strtoofft.h"
100 #include "curl_memrchr.h"
101 #include "inet_pton.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(!Curl_raw_equal(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 * matching cookie path and url path
148 * RFC6265 5.1.4 Paths and Path-Match
150 static bool pathmatch(const char* cookie_path, const char* request_uri)
152 size_t cookie_path_len;
154 char* uri_path = NULL;
158 /* cookie_path must not have last '/' separator. ex: /sample */
159 cookie_path_len = strlen(cookie_path);
160 if(1 == cookie_path_len) {
161 /* cookie_path must be '/' */
165 uri_path = strdup(request_uri);
168 pos = strchr(uri_path, '?');
172 /* #-fragments are already cut off! */
173 if(0 == strlen(uri_path) || uri_path[0] != '/') {
175 uri_path = strdup("/");
180 /* here, RFC6265 5.1.4 says
181 4. Output the characters of the uri-path from the first character up
182 to, but not including, the right-most %x2F ("/").
183 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
185 Ignore this algorithm because /hoge is uri path for this case
189 uri_path_len = strlen(uri_path);
191 if(uri_path_len < cookie_path_len) {
196 /* not using checkprefix() because matching should be case-sensitive */
197 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
202 /* The cookie-path and the uri-path are identical. */
203 if(cookie_path_len == uri_path_len) {
208 /* here, cookie_path_len < url_path_len */
209 if(uri_path[cookie_path_len] == '/') {
222 * cookie path sanitize
224 static char *sanitize_cookie_path(const char *cookie_path)
227 char *new_path = strdup(cookie_path);
231 /* some stupid site sends path attribute with '"'. */
232 len = strlen(new_path);
233 if(new_path[0] == '\"') {
234 memmove((void *)new_path, (const void *)(new_path + 1), len);
237 if(len && (new_path[len - 1] == '\"')) {
238 new_path[len - 1] = 0x0;
242 /* RFC6265 5.2.4 The Path Attribute */
243 if(new_path[0] != '/') {
244 /* Let cookie-path be the default-path. */
246 new_path = strdup("/");
250 /* convert /hoge/ to /hoge */
251 if(len && new_path[len - 1] == '/') {
252 new_path[len - 1] = 0x0;
259 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
261 * NOTE: OOM or cookie parsing failures are ignored.
263 void Curl_cookie_loadfiles(struct Curl_easy *data)
265 struct curl_slist *list = data->change.cookielist;
267 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
269 struct CookieInfo *newcookies = Curl_cookie_init(data,
272 data->set.cookiesession);
274 /* Failure may be due to OOM or a bad cookie; both are ignored
275 * but only the first should be
277 infof(data, "ignoring failed cookie_init for %s\n", list->data);
279 data->cookies = newcookies;
282 curl_slist_free_all(data->change.cookielist); /* clean up list */
283 data->change.cookielist = NULL; /* don't do this again! */
284 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
289 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
290 * that will be freed before the allocated string is stored there.
292 * It is meant to easily replace strdup()
294 static void strstore(char **str, const char *newstr)
297 *str = strdup(newstr);
301 * remove_expired() removes expired cookies.
303 static void remove_expired(struct CookieInfo *cookies)
305 struct Cookie *co, *nx, *pv;
306 curl_off_t now = (curl_off_t)time(NULL);
308 co = cookies->cookies;
312 if(co->expires && co->expires < now) {
313 if(co == cookies->cookies) {
314 cookies->cookies = co->next;
319 cookies->numcookies--;
330 * Return true if the given string is an IP(v4|v6) address.
332 static bool isip(const char *domain)
336 struct in6_addr addr6;
339 if(Curl_inet_pton(AF_INET, domain, &addr)
341 || Curl_inet_pton(AF_INET6, domain, &addr6)
344 /* domain name given as IP address */
351 /****************************************************************************
355 * Add a single cookie line to the cookie keeping object.
357 * Be aware that sometimes we get an IP-only host name, and that might also be
358 * a numerical IPv6 address.
360 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
361 * as they should be treated separately.
362 ***************************************************************************/
365 Curl_cookie_add(struct Curl_easy *data,
366 /* The 'data' pointer here may be NULL at times, and thus
367 must only be used very carefully for things that can deal
368 with data being NULL. Such as infof() and similar */
370 struct CookieInfo *c,
371 bool httpheader, /* TRUE if HTTP header-style line */
372 char *lineptr, /* first character of the line */
373 const char *domain, /* default domain */
374 const char *path) /* full path used when this cookie is set,
375 used to get default path for the cookie
378 struct Cookie *clist;
381 struct Cookie *lastc=NULL;
382 time_t now = time(NULL);
383 bool replace_old = FALSE;
384 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
387 const psl_ctx_t *psl;
390 #ifdef CURL_DISABLE_VERBOSE_STRINGS
394 /* First, alloc and init a new struct for it */
395 co = calloc(1, sizeof(struct Cookie));
397 return NULL; /* bail out if we're this low on memory */
400 /* This line was read off a HTTP-header */
405 what = malloc(MAX_COOKIE_LINE);
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_COOKIE_LINE_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 /* name ends with a '=' ? */
434 sep = (*endofn == '=')?TRUE:FALSE;
437 endofn--; /* move to the last character */
438 if(ISBLANK(*endofn)) {
439 /* skip trailing spaces in name */
440 while(*endofn && ISBLANK(*endofn) && nlen) {
444 name[nlen]=0; /* new end of name */
448 /* Strip off trailing whitespace from the 'what' */
449 while(len && ISBLANK(what[len-1])) {
454 /* Skip leading whitespace from the 'what' */
456 while(*whatptr && ISBLANK(*whatptr))
459 if(!co->name && sep) {
460 /* The very first name/value pair is the actual cookie name */
461 co->name = strdup(name);
462 co->value = strdup(whatptr);
463 if(!co->name || !co->value) {
469 /* this was a "<name>=" with no content, and we must allow
470 'secure' and 'httponly' specified this weirdly */
472 if(Curl_raw_equal("secure", name))
474 else if(Curl_raw_equal("httponly", name))
477 /* there was a '=' so we're not done parsing this field */
482 else if(Curl_raw_equal("path", name)) {
483 strstore(&co->path, whatptr);
485 badcookie = TRUE; /* out of memory bad */
488 co->spath = sanitize_cookie_path(co->path);
490 badcookie = TRUE; /* out of memory bad */
494 else if(Curl_raw_equal("domain", name)) {
498 /* Now, we make sure that our host is within the given domain,
499 or the given domain is not valid and thus cannot be set. */
501 if('.' == whatptr[0])
502 whatptr++; /* ignore preceding dot */
504 is_ip = isip(domain ? domain : whatptr);
506 /* check for more dots */
507 dotp = strchr(whatptr, '.');
512 || (is_ip && !strcmp(whatptr, domain))
513 || (!is_ip && tailmatch(whatptr, domain))) {
514 strstore(&co->domain, whatptr);
520 co->tailmatch=TRUE; /* we always do that if the domain name was
524 /* we did not get a tailmatch and then the attempted set domain
525 is not a domain to which the current host belongs. Mark as
528 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
532 else if(Curl_raw_equal("version", name)) {
533 strstore(&co->version, whatptr);
539 else if(Curl_raw_equal("max-age", name)) {
540 /* Defined in RFC2109:
542 Optional. The Max-Age attribute defines the lifetime of the
543 cookie, in seconds. The delta-seconds value is a decimal non-
544 negative integer. After delta-seconds seconds elapse, the
545 client should discard the cookie. A value of zero means the
546 cookie should be discarded immediately.
549 strstore(&co->maxage, whatptr);
555 else if(Curl_raw_equal("expires", name)) {
556 strstore(&co->expirestr, whatptr);
563 else this is the second (or more) name we don't know
567 /* this is an "illegal" <what>=<this> pair */
570 if(!semiptr || !*semiptr) {
571 /* we already know there are no more cookies */
577 while(*ptr && ISBLANK(*ptr))
579 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
582 /* There are no more semicolons, but there's a final name=value pair
584 semiptr=strchr(ptr, '\0');
589 curlx_strtoofft((*co->maxage=='\"')?
590 &co->maxage[1]:&co->maxage[0], NULL, 10);
591 if(CURL_OFF_T_MAX - now < co->expires)
593 co->expires = CURL_OFF_T_MAX;
597 else if(co->expirestr) {
598 /* Note that if the date couldn't get parsed for whatever reason,
599 the cookie will be treated as a session cookie */
600 co->expires = curl_getdate(co->expirestr, NULL);
602 /* Session cookies have expires set to 0 so if we get that back
603 from the date parser let's add a second to make it a
604 non-session cookie */
607 else if(co->expires < 0)
611 if(!badcookie && !co->domain) {
613 /* no domain was given in the header line, set the default */
614 co->domain=strdup(domain);
620 if(!badcookie && !co->path && path) {
621 /* No path was given in the header line, set the default.
622 Note that the passed-in path to this function MAY have a '?' and
623 following part that MUST not be stored as part of the path. */
624 char *queryp = strchr(path, '?');
626 /* queryp is where the interesting part of the path ends, so now we
627 want to the find the last */
630 endslash = strrchr(path, '/');
632 endslash = memrchr(path, '/', (size_t)(queryp - path));
634 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
635 co->path=malloc(pathlen+1); /* one extra for the zero byte */
637 memcpy(co->path, path, pathlen);
638 co->path[pathlen]=0; /* zero terminate */
639 co->spath = sanitize_cookie_path(co->path);
641 badcookie = TRUE; /* out of memory bad */
650 if(badcookie || !co->name) {
651 /* we didn't get a cookie name or a bad one,
652 this is an illegal line, bail out */
659 /* This line is NOT a HTTP header style line, we do offer support for
660 reading the odd netscape cookies-file format here */
666 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
667 marked with httpOnly after the domain name are not accessible
668 from javascripts, but since curl does not operate at javascript
669 level, we include them anyway. In Firefox's cookie files, these
670 lines are preceded with #HttpOnly_ and then everything is
671 as usual, so we skip 10 characters of the line..
673 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
678 if(lineptr[0]=='#') {
679 /* don't even try the comments */
683 /* strip off the possible end-of-line characters */
684 ptr=strchr(lineptr, '\r');
686 *ptr=0; /* clear it */
687 ptr=strchr(lineptr, '\n');
689 *ptr=0; /* clear it */
691 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
693 /* Now loop through the fields and init the struct we already have
695 for(ptr=firstptr, fields=0; ptr && !badcookie;
696 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
699 if(ptr[0]=='.') /* skip preceding dots */
701 co->domain = strdup(ptr);
706 /* This field got its explanation on the 23rd of May 2001 by
709 flag: A TRUE/FALSE value indicating if all machines within a given
710 domain can access the variable. This value is set automatically by
711 the browser, depending on the value you set for the domain.
713 As far as I can see, it is set to true when the cookie says
714 .domain.com and to false when the domain is complete www.domain.com
716 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
719 /* It turns out, that sometimes the file format allows the path
720 field to remain not filled in, we try to detect this and work
721 around it! Andrés GarcÃa made us aware of this... */
722 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
723 /* only if the path doesn't look like a boolean option! */
724 co->path = strdup(ptr);
728 co->spath = sanitize_cookie_path(co->path);
730 badcookie = TRUE; /* out of memory bad */
735 /* this doesn't look like a path, make one up! */
736 co->path = strdup("/");
739 co->spath = strdup("/");
742 fields++; /* add a field and fall down to secure */
745 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
748 co->expires = curlx_strtoofft(ptr, NULL, 10);
751 co->name = strdup(ptr);
756 co->value = strdup(ptr);
763 /* we got a cookie with blank contents, fix it */
764 co->value = strdup("");
771 if(!badcookie && (7 != fields))
772 /* we did not find the sufficient number of fields */
782 if(!c->running && /* read from a file */
783 c->newsession && /* clean session cookies */
784 !co->expires) { /* this is a session cookie since it doesn't expire! */
789 co->livecookie = c->running;
791 /* now, we have parsed the incoming line, we must now check if this
792 superceeds an already existing cookie, which it may if the previous have
793 the same domain and path as this */
795 /* at first, remove expired cookies */
799 /* Check if the domain is a Public Suffix and if yes, ignore the cookie.
800 This needs a libpsl compiled with builtin data. */
801 if(domain && co->domain && !isip(co->domain)) {
802 if(((psl = psl_builtin()) != NULL)
803 && !psl_is_cookie_domain_acceptable(psl, domain, co->domain)) {
805 "cookie '%s' dropped, domain '%s' must not set cookies for '%s'\n",
806 co->name, domain, co->domain);
816 if(Curl_raw_equal(clist->name, co->name)) {
817 /* the names are identical */
819 if(clist->domain && co->domain) {
820 if(Curl_raw_equal(clist->domain, co->domain))
821 /* The domains are identical */
824 else if(!clist->domain && !co->domain)
828 /* the domains were identical */
830 if(clist->spath && co->spath) {
831 if(Curl_raw_equal(clist->spath, co->spath)) {
837 else if(!clist->spath && !co->spath)
844 if(replace_old && !co->livecookie && clist->livecookie) {
845 /* Both cookies matched fine, except that the already present
846 cookie is "live", which means it was set from a header, while
847 the new one isn't "live" and thus only read from a file. We let
848 live cookies stay alive */
850 /* Free the newcomer and get out of here! */
856 co->next = clist->next; /* get the next-pointer first */
858 /* then free all the old pointers */
864 free(clist->expirestr);
865 free(clist->version);
868 *clist = *co; /* then store all the new data */
870 free(co); /* free the newly alloced memory */
871 co = clist; /* point to the previous struct instead */
873 /* We have replaced a cookie, now skip the rest of the list but
874 make sure the 'lastc' pointer is properly set */
887 /* Only show this when NOT reading the cookies from a file */
888 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
889 "expire %" CURL_FORMAT_CURL_OFF_T "\n",
890 replace_old?"Replaced":"Added", co->name, co->value,
891 co->domain, co->path, co->expires);
894 /* then make the last item point on this new one */
899 c->numcookies++; /* one more cookie in the jar */
905 /*****************************************************************************
909 * Inits a cookie struct to read data from a local file. This is always
910 * called before any cookies are set. File may be NULL.
912 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
914 * Returns NULL on out of memory. Invalid cookies are ignored.
915 ****************************************************************************/
916 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
918 struct CookieInfo *inc,
921 struct CookieInfo *c;
927 /* we didn't get a struct, create one */
928 c = calloc(1, sizeof(struct CookieInfo));
930 return NULL; /* failed to get memory */
931 c->filename = strdup(file?file:"none"); /* copy the name just in case */
933 goto fail; /* failed to get memory */
936 /* we got an already existing one, use that */
939 c->running = FALSE; /* this is not running, this is init */
941 if(file && strequal(file, "-")) {
945 else if(file && !*file) {
946 /* points to a "" string */
950 fp = file?fopen(file, FOPEN_READTEXT):NULL;
952 c->newsession = newsession; /* new session? */
958 line = malloc(MAX_COOKIE_LINE);
961 while(fgets(line, MAX_COOKIE_LINE, fp)) {
962 if(checkprefix("Set-Cookie:", line)) {
963 /* This is a cookie line, get it! */
971 while(*lineptr && ISBLANK(*lineptr))
974 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
976 free(line); /* free the line buffer */
982 c->running = TRUE; /* now, we're running */
989 /* Only clean up if we allocated it here, as the original could still be in
990 * use by a share handle */
991 Curl_cookie_cleanup(c);
994 return NULL; /* out of memory */
997 /* sort this so that the longest path gets before the shorter path */
998 static int cookie_sort(const void *p1, const void *p2)
1000 struct Cookie *c1 = *(struct Cookie **)p1;
1001 struct Cookie *c2 = *(struct Cookie **)p2;
1004 /* 1 - compare cookie path lengths */
1005 l1 = c1->path ? strlen(c1->path) : 0;
1006 l2 = c2->path ? strlen(c2->path) : 0;
1009 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1011 /* 2 - compare cookie domain lengths */
1012 l1 = c1->domain ? strlen(c1->domain) : 0;
1013 l2 = c2->domain ? strlen(c2->domain) : 0;
1016 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1018 /* 3 - compare cookie names */
1019 if(c1->name && c2->name)
1020 return strcmp(c1->name, c2->name);
1022 /* sorry, can't be more deterministic */
1026 /*****************************************************************************
1028 * Curl_cookie_getlist()
1030 * For a given host and path, return a linked list of cookies that the
1031 * client should send to the server if used now. The secure boolean informs
1032 * the cookie if a secure connection is achieved or not.
1034 * It shall only return cookies that haven't expired.
1036 ****************************************************************************/
1038 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1039 const char *host, const char *path,
1042 struct Cookie *newco;
1044 time_t now = time(NULL);
1045 struct Cookie *mainco=NULL;
1049 if(!c || !c->cookies)
1050 return NULL; /* no cookie struct or no cookies in the struct */
1052 /* at first, remove expired cookies */
1055 /* check if host is an IP(v4|v6) address */
1061 /* only process this cookie if it is not expired or had no expire
1062 date AND that if the cookie requires we're secure we must only
1063 continue if we are! */
1064 if((!co->expires || (co->expires > now)) &&
1065 (co->secure?secure:TRUE)) {
1067 /* now check if the domain is correct */
1069 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
1070 ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
1071 /* the right part of the host matches the domain stuff in the
1074 /* now check the left part of the path with the cookies path
1076 if(!co->spath || pathmatch(co->spath, path) ) {
1078 /* and now, we know this is a match and we should create an
1079 entry for the return-linked-list */
1081 newco = malloc(sizeof(struct Cookie));
1083 /* first, copy the whole source cookie: */
1084 memcpy(newco, co, sizeof(struct Cookie));
1086 /* then modify our next */
1087 newco->next = mainco;
1089 /* point the main to us */
1096 /* failure, clear up the allocated chain and return NULL */
1112 /* Now we need to make sure that if there is a name appearing more than
1113 once, the longest specified path version comes first. To make this
1114 the swiftest way, we just sort them all based on path length. */
1115 struct Cookie **array;
1118 /* alloc an array and store all cookie pointers */
1119 array = malloc(sizeof(struct Cookie *) * matches);
1125 for(i=0; co; co = co->next)
1128 /* now sort the cookie pointers in path length order */
1129 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1131 /* remake the linked list order according to the new order */
1133 mainco = array[0]; /* start here */
1134 for(i=0; i<matches-1; i++)
1135 array[i]->next = array[i+1];
1136 array[matches-1]->next = NULL; /* terminate the list */
1138 free(array); /* remove the temporary data again */
1141 return mainco; /* return the new list */
1144 /*****************************************************************************
1146 * Curl_cookie_clearall()
1148 * Clear all existing cookies and reset the counter.
1150 ****************************************************************************/
1151 void Curl_cookie_clearall(struct CookieInfo *cookies)
1154 Curl_cookie_freelist(cookies->cookies, TRUE);
1155 cookies->cookies = NULL;
1156 cookies->numcookies = 0;
1160 /*****************************************************************************
1162 * Curl_cookie_freelist()
1164 * Free a list of cookies previously returned by Curl_cookie_getlist();
1166 * The 'cookiestoo' argument tells this function whether to just free the
1167 * list or actually also free all cookies within the list as well.
1169 ****************************************************************************/
1171 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
1173 struct Cookie *next;
1179 free(co); /* we only free the struct since the "members" are all just
1180 pointed out in the main cookie list! */
1186 /*****************************************************************************
1188 * Curl_cookie_clearsess()
1190 * Free all session cookies in the cookies list.
1192 ****************************************************************************/
1193 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1195 struct Cookie *first, *curr, *next, *prev = NULL;
1197 if(!cookies || !cookies->cookies)
1200 first = curr = prev = cookies->cookies;
1202 for(; curr; curr = next) {
1204 if(!curr->expires) {
1214 cookies->numcookies--;
1220 cookies->cookies = first;
1224 /*****************************************************************************
1226 * Curl_cookie_cleanup()
1228 * Free a "cookie object" previous created with Curl_cookie_init().
1230 ****************************************************************************/
1231 void Curl_cookie_cleanup(struct CookieInfo *c)
1235 Curl_cookie_freelist(c->cookies, TRUE);
1236 free(c); /* free the base struct as well */
1240 /* get_netscape_format()
1242 * Formats a string for Netscape output file, w/o a newline at the end.
1244 * Function returns a char * to a formatted line. Has to be free()d
1246 static char *get_netscape_format(const struct Cookie *co)
1249 "%s" /* httponly preamble */
1250 "%s%s\t" /* domain */
1251 "%s\t" /* tailmatch */
1254 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1257 co->httponly?"#HttpOnly_":"",
1258 /* Make sure all domains are prefixed with a dot if they allow
1259 tailmatching. This is Mozilla-style. */
1260 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1261 co->domain?co->domain:"unknown",
1262 co->tailmatch?"TRUE":"FALSE",
1263 co->path?co->path:"/",
1264 co->secure?"TRUE":"FALSE",
1267 co->value?co->value:"");
1273 * Writes all internally known cookies to the specified file. Specify
1274 * "-" as file name to write to stdout.
1276 * The function returns non-zero on write failure.
1278 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1282 bool use_stdout=FALSE;
1285 if((NULL == c) || (0 == c->numcookies))
1286 /* If there are no known cookies, we don't write or even create any
1290 /* at first, remove expired cookies */
1293 if(strequal("-", dumphere)) {
1299 out = fopen(dumphere, FOPEN_WRITETEXT);
1301 return 1; /* failure */
1304 fputs("# Netscape HTTP Cookie File\n"
1305 "# https://curl.haxx.se/docs/http-cookies.html\n"
1306 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1309 for(co = c->cookies; co; co = co->next) {
1312 format_ptr = get_netscape_format(co);
1313 if(format_ptr == NULL) {
1314 fprintf(out, "#\n# Fatal libcurl error\n");
1319 fprintf(out, "%s\n", format_ptr);
1329 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
1331 struct curl_slist *list = NULL;
1332 struct curl_slist *beg;
1336 if((data->cookies == NULL) ||
1337 (data->cookies->numcookies == 0))
1340 for(c = data->cookies->cookies; c; c = c->next) {
1343 line = get_netscape_format(c);
1345 curl_slist_free_all(list);
1348 beg = Curl_slist_append_nodup(list, line);
1351 curl_slist_free_all(list);
1360 void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
1362 if(data->set.str[STRING_COOKIEJAR]) {
1363 if(data->change.cookielist) {
1364 /* If there is a list of cookie files to read, do it first so that
1365 we have all the told files read before we write the new jar.
1366 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1367 Curl_cookie_loadfiles(data);
1370 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1372 /* if we have a destination file for all the cookies to get dumped to */
1373 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1374 infof(data, "WARNING: failed to save cookies in %s\n",
1375 data->set.str[STRING_COOKIEJAR]);
1378 if(cleanup && data->change.cookielist) {
1379 /* since nothing is written, we can just free the list of cookie file
1381 curl_slist_free_all(data->change.cookielist); /* clean up list */
1382 data->change.cookielist = NULL;
1384 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1387 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1388 Curl_cookie_cleanup(data->cookies);
1390 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1393 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */