1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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 SessionHandle *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 SessionHandle *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)
87 #define _MPRINTF_REPLACE
88 #include <curl/mprintf.h>
96 #include "curl_memory.h"
98 #include "strtoofft.h"
100 #include "curl_memrchr.h"
101 #include "inet_pton.h"
103 /* The last #include file should be: */
104 #include "memdebug.h"
106 static void freecookie(struct Cookie *co)
128 static bool tailmatch(const char *cooke_domain, const char *hostname)
130 size_t cookie_domain_len = strlen(cooke_domain);
131 size_t hostname_len = strlen(hostname);
133 if(hostname_len < cookie_domain_len)
136 if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len))
139 /* A lead char of cookie_domain is not '.'.
140 RFC6265 4.1.2.3. The Domain Attribute says:
141 For example, if the value of the Domain attribute is
142 "example.com", the user agent will include the cookie in the Cookie
143 header when making HTTP requests to example.com, www.example.com, and
144 www.corp.example.com.
146 if(hostname_len == cookie_domain_len)
148 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
154 * matching cookie path and url path
155 * RFC6265 5.1.4 Paths and Path-Match
157 static bool pathmatch(const char* cookie_path, const char* request_uri)
159 size_t cookie_path_len;
161 char* uri_path = NULL;
165 /* cookie_path must not have last '/' separator. ex: /sample */
166 cookie_path_len = strlen(cookie_path);
167 if(1 == cookie_path_len) {
168 /* cookie_path must be '/' */
172 uri_path = strdup(request_uri);
175 pos = strchr(uri_path, '?');
179 /* #-fragments are already cut off! */
180 if(0 == strlen(uri_path) || uri_path[0] != '/') {
182 uri_path = strdup("/");
187 /* here, RFC6265 5.1.4 says
188 4. Output the characters of the uri-path from the first character up
189 to, but not including, the right-most %x2F ("/").
190 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
192 Ignore this algorithm because /hoge is uri path for this case
196 uri_path_len = strlen(uri_path);
198 if(uri_path_len < cookie_path_len) {
203 /* not using checkprefix() because matching should be case-sensitive */
204 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
209 /* The cookie-path and the uri-path are identical. */
210 if(cookie_path_len == uri_path_len) {
215 /* here, cookie_path_len < url_path_len */
216 if(uri_path[cookie_path_len] == '/') {
229 * cookie path sanitize
231 static char *sanitize_cookie_path(const char *cookie_path)
234 char *new_path = strdup(cookie_path);
238 /* some stupid site sends path attribute with '"'. */
239 if(new_path[0] == '\"') {
240 memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path));
242 if(new_path[strlen(new_path) - 1] == '\"') {
243 new_path[strlen(new_path) - 1] = 0x0;
246 /* RFC6265 5.2.4 The Path Attribute */
247 if(new_path[0] != '/') {
248 /* Let cookie-path be the default-path. */
250 new_path = strdup("/");
254 /* convert /hoge/ to /hoge */
255 len = strlen(new_path);
256 if(1 < len && new_path[len - 1] == '/') {
257 new_path[len - 1] = 0x0;
264 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
266 * NOTE: OOM or cookie parsing failures are ignored.
268 void Curl_cookie_loadfiles(struct SessionHandle *data)
270 struct curl_slist *list = data->change.cookielist;
272 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
274 struct CookieInfo *newcookies = Curl_cookie_init(data,
277 data->set.cookiesession);
279 /* Failure may be due to OOM or a bad cookie; both are ignored
280 * but only the first should be
282 infof(data, "ignoring failed cookie_init for %s\n", list->data);
284 data->cookies = newcookies;
287 curl_slist_free_all(data->change.cookielist); /* clean up list */
288 data->change.cookielist = NULL; /* don't do this again! */
289 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
294 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
295 * that will be freed before the allocated string is stored there.
297 * It is meant to easily replace strdup()
299 static void strstore(char **str, const char *newstr)
303 *str = strdup(newstr);
307 * remove_expired() removes expired cookies.
309 static void remove_expired(struct CookieInfo *cookies)
311 struct Cookie *co, *nx, *pv;
312 curl_off_t now = (curl_off_t)time(NULL);
314 co = cookies->cookies;
318 if((co->expirestr || co->maxage) && co->expires < now) {
319 if(co == cookies->cookies) {
320 cookies->cookies = co->next;
325 cookies->numcookies--;
336 * Return true if the given string is an IP(v4|v6) address.
338 static bool isip(const char *domain)
342 struct in6_addr addr6;
345 if(Curl_inet_pton(AF_INET, domain, &addr)
347 || Curl_inet_pton(AF_INET6, domain, &addr6)
350 /* domain name given as IP address */
357 /****************************************************************************
361 * Add a single cookie line to the cookie keeping object.
363 * Be aware that sometimes we get an IP-only host name, and that might also be
364 * a numerical IPv6 address.
366 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
367 * as they should be treated separately.
368 ***************************************************************************/
371 Curl_cookie_add(struct SessionHandle *data,
372 /* The 'data' pointer here may be NULL at times, and thus
373 must only be used very carefully for things that can deal
374 with data being NULL. Such as infof() and similar */
376 struct CookieInfo *c,
377 bool httpheader, /* TRUE if HTTP header-style line */
378 char *lineptr, /* first character of the line */
379 const char *domain, /* default domain */
380 const char *path) /* full path used when this cookie is set,
381 used to get default path for the cookie
384 struct Cookie *clist;
387 struct Cookie *lastc=NULL;
388 time_t now = time(NULL);
389 bool replace_old = FALSE;
390 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
392 #ifdef CURL_DISABLE_VERBOSE_STRINGS
396 /* First, alloc and init a new struct for it */
397 co = calloc(1, sizeof(struct Cookie));
399 return NULL; /* bail out if we're this low on memory */
402 /* This line was read off a HTTP-header */
407 what = malloc(MAX_COOKIE_LINE);
413 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
415 while(*lineptr && ISBLANK(*lineptr))
420 /* we have a <what>=<this> pair or a stand-alone word here */
421 name[0]=what[0]=0; /* init the buffers */
422 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%"
423 MAX_COOKIE_LINE_TXT "[^;\r\n]",
425 /* Use strstore() below to properly deal with received cookie
426 headers that have the same string property set more than once,
427 and then we use the last one. */
431 size_t len=strlen(what);
432 const char *endofn = &ptr[ strlen(name) ];
434 /* skip trailing spaces in name */
435 while(*endofn && ISBLANK(*endofn))
438 /* name ends with a '=' ? */
439 sep = (*endofn == '=')?TRUE:FALSE;
441 /* Strip off trailing whitespace from the 'what' */
442 while(len && ISBLANK(what[len-1])) {
447 /* Skip leading whitespace from the 'what' */
449 while(*whatptr && ISBLANK(*whatptr))
453 /* this was a "<name>=" with no content, and we must allow
454 'secure' and 'httponly' specified this weirdly */
456 if(Curl_raw_equal("secure", name))
458 else if(Curl_raw_equal("httponly", name))
461 /* there was a '=' so we're not done parsing this field */
466 else if(Curl_raw_equal("path", name)) {
467 strstore(&co->path, whatptr);
469 badcookie = TRUE; /* out of memory bad */
472 co->spath = sanitize_cookie_path(co->path);
474 badcookie = TRUE; /* out of memory bad */
478 else if(Curl_raw_equal("domain", name)) {
482 /* Now, we make sure that our host is within the given domain,
483 or the given domain is not valid and thus cannot be set. */
485 if('.' == whatptr[0])
486 whatptr++; /* ignore preceding dot */
488 is_ip = isip(domain ? domain : whatptr);
490 /* check for more dots */
491 dotp = strchr(whatptr, '.');
496 || (is_ip && !strcmp(whatptr, domain))
497 || (!is_ip && tailmatch(whatptr, domain))) {
498 strstore(&co->domain, whatptr);
504 co->tailmatch=TRUE; /* we always do that if the domain name was
508 /* we did not get a tailmatch and then the attempted set domain
509 is not a domain to which the current host belongs. Mark as
512 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
516 else if(Curl_raw_equal("version", name)) {
517 strstore(&co->version, whatptr);
523 else if(Curl_raw_equal("max-age", name)) {
524 /* Defined in RFC2109:
526 Optional. The Max-Age attribute defines the lifetime of the
527 cookie, in seconds. The delta-seconds value is a decimal non-
528 negative integer. After delta-seconds seconds elapse, the
529 client should discard the cookie. A value of zero means the
530 cookie should be discarded immediately.
533 strstore(&co->maxage, whatptr);
539 else if(Curl_raw_equal("expires", name)) {
540 strstore(&co->expirestr, whatptr);
547 co->name = strdup(name);
548 co->value = strdup(whatptr);
549 if(!co->name || !co->value) {
555 else this is the second (or more) name we don't know
559 /* this is an "illegal" <what>=<this> pair */
562 if(!semiptr || !*semiptr) {
563 /* we already know there are no more cookies */
569 while(*ptr && ISBLANK(*ptr))
571 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
574 /* There are no more semicolons, but there's a final name=value pair
576 semiptr=strchr(ptr, '\0');
581 curlx_strtoofft((*co->maxage=='\"')?
582 &co->maxage[1]:&co->maxage[0], NULL, 10);
583 if(CURL_OFF_T_MAX - now < co->expires)
585 co->expires = CURL_OFF_T_MAX;
589 else if(co->expirestr) {
590 /* Note that if the date couldn't get parsed for whatever reason,
591 the cookie will be treated as a session cookie */
592 co->expires = curl_getdate(co->expirestr, NULL);
594 /* Session cookies have expires set to 0 so if we get that back
595 from the date parser let's add a second to make it a
596 non-session cookie */
599 else if(co->expires < 0)
603 if(!badcookie && !co->domain) {
605 /* no domain was given in the header line, set the default */
606 co->domain=strdup(domain);
612 if(!badcookie && !co->path && path) {
613 /* No path was given in the header line, set the default.
614 Note that the passed-in path to this function MAY have a '?' and
615 following part that MUST not be stored as part of the path. */
616 char *queryp = strchr(path, '?');
618 /* queryp is where the interesting part of the path ends, so now we
619 want to the find the last */
622 endslash = strrchr(path, '/');
624 endslash = memrchr(path, '/', (size_t)(queryp - path));
626 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
627 co->path=malloc(pathlen+1); /* one extra for the zero byte */
629 memcpy(co->path, path, pathlen);
630 co->path[pathlen]=0; /* zero terminate */
631 co->spath = sanitize_cookie_path(co->path);
633 badcookie = TRUE; /* out of memory bad */
642 if(badcookie || !co->name) {
643 /* we didn't get a cookie name or a bad one,
644 this is an illegal line, bail out */
651 /* This line is NOT a HTTP header style line, we do offer support for
652 reading the odd netscape cookies-file format here */
658 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
659 marked with httpOnly after the domain name are not accessible
660 from javascripts, but since curl does not operate at javascript
661 level, we include them anyway. In Firefox's cookie files, these
662 lines are preceded with #HttpOnly_ and then everything is
663 as usual, so we skip 10 characters of the line..
665 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
670 if(lineptr[0]=='#') {
671 /* don't even try the comments */
675 /* strip off the possible end-of-line characters */
676 ptr=strchr(lineptr, '\r');
678 *ptr=0; /* clear it */
679 ptr=strchr(lineptr, '\n');
681 *ptr=0; /* clear it */
683 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
685 /* Now loop through the fields and init the struct we already have
687 for(ptr=firstptr, fields=0; ptr && !badcookie;
688 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
691 if(ptr[0]=='.') /* skip preceding dots */
693 co->domain = strdup(ptr);
698 /* This field got its explanation on the 23rd of May 2001 by
701 flag: A TRUE/FALSE value indicating if all machines within a given
702 domain can access the variable. This value is set automatically by
703 the browser, depending on the value you set for the domain.
705 As far as I can see, it is set to true when the cookie says
706 .domain.com and to false when the domain is complete www.domain.com
708 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
711 /* It turns out, that sometimes the file format allows the path
712 field to remain not filled in, we try to detect this and work
713 around it! Andrés GarcÃa made us aware of this... */
714 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
715 /* only if the path doesn't look like a boolean option! */
716 co->path = strdup(ptr);
720 co->spath = sanitize_cookie_path(co->path);
722 badcookie = TRUE; /* out of memory bad */
727 /* this doesn't look like a path, make one up! */
728 co->path = strdup("/");
731 co->spath = strdup("/");
734 fields++; /* add a field and fall down to secure */
737 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
740 co->expires = curlx_strtoofft(ptr, NULL, 10);
743 co->name = strdup(ptr);
748 co->value = strdup(ptr);
755 /* we got a cookie with blank contents, fix it */
756 co->value = strdup("");
763 if(!badcookie && (7 != fields))
764 /* we did not find the sufficient number of fields */
774 if(!c->running && /* read from a file */
775 c->newsession && /* clean session cookies */
776 !co->expires) { /* this is a session cookie since it doesn't expire! */
781 co->livecookie = c->running;
783 /* now, we have parsed the incoming line, we must now check if this
784 superceeds an already existing cookie, which it may if the previous have
785 the same domain and path as this */
787 /* at first, remove expired cookies */
793 if(Curl_raw_equal(clist->name, co->name)) {
794 /* the names are identical */
796 if(clist->domain && co->domain) {
797 if(Curl_raw_equal(clist->domain, co->domain))
798 /* The domains are identical */
801 else if(!clist->domain && !co->domain)
805 /* the domains were identical */
807 if(clist->spath && co->spath) {
808 if(Curl_raw_equal(clist->spath, co->spath)) {
814 else if(!clist->spath && !co->spath)
821 if(replace_old && !co->livecookie && clist->livecookie) {
822 /* Both cookies matched fine, except that the already present
823 cookie is "live", which means it was set from a header, while
824 the new one isn't "live" and thus only read from a file. We let
825 live cookies stay alive */
827 /* Free the newcomer and get out of here! */
833 co->next = clist->next; /* get the next-pointer first */
835 /* then free all the old pointers */
846 free(clist->expirestr);
849 free(clist->version);
853 *clist = *co; /* then store all the new data */
855 free(co); /* free the newly alloced memory */
856 co = clist; /* point to the previous struct instead */
858 /* We have replaced a cookie, now skip the rest of the list but
859 make sure the 'lastc' pointer is properly set */
872 /* Only show this when NOT reading the cookies from a file */
873 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
874 "expire %" CURL_FORMAT_CURL_OFF_T "\n",
875 replace_old?"Replaced":"Added", co->name, co->value,
876 co->domain, co->path, co->expires);
879 /* then make the last item point on this new one */
884 c->numcookies++; /* one more cookie in the jar */
890 /*****************************************************************************
894 * Inits a cookie struct to read data from a local file. This is always
895 * called before any cookies are set. File may be NULL.
897 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
899 * Returns NULL on out of memory. Invalid cookies are ignored.
900 ****************************************************************************/
901 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
903 struct CookieInfo *inc,
906 struct CookieInfo *c;
912 /* we didn't get a struct, create one */
913 c = calloc(1, sizeof(struct CookieInfo));
915 return NULL; /* failed to get memory */
916 c->filename = strdup(file?file:"none"); /* copy the name just in case */
918 goto fail; /* failed to get memory */
921 /* we got an already existing one, use that */
924 c->running = FALSE; /* this is not running, this is init */
926 if(file && strequal(file, "-")) {
930 else if(file && !*file) {
931 /* points to a "" string */
935 fp = file?fopen(file, "r"):NULL;
937 c->newsession = newsession; /* new session? */
943 line = malloc(MAX_COOKIE_LINE);
946 while(fgets(line, MAX_COOKIE_LINE, fp)) {
947 if(checkprefix("Set-Cookie:", line)) {
948 /* This is a cookie line, get it! */
956 while(*lineptr && ISBLANK(*lineptr))
959 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
961 free(line); /* free the line buffer */
967 c->running = TRUE; /* now, we're running */
974 /* Only clean up if we allocated it here, as the original could still be in
975 * use by a share handle */
976 Curl_cookie_cleanup(c);
979 return NULL; /* out of memory */
982 /* sort this so that the longest path gets before the shorter path */
983 static int cookie_sort(const void *p1, const void *p2)
985 struct Cookie *c1 = *(struct Cookie **)p1;
986 struct Cookie *c2 = *(struct Cookie **)p2;
989 /* 1 - compare cookie path lengths */
990 l1 = c1->path ? strlen(c1->path) : 0;
991 l2 = c2->path ? strlen(c2->path) : 0;
994 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
996 /* 2 - compare cookie domain lengths */
997 l1 = c1->domain ? strlen(c1->domain) : 0;
998 l2 = c2->domain ? strlen(c2->domain) : 0;
1001 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
1003 /* 3 - compare cookie names */
1004 if(c1->name && c2->name)
1005 return strcmp(c1->name, c2->name);
1007 /* sorry, can't be more deterministic */
1011 /*****************************************************************************
1013 * Curl_cookie_getlist()
1015 * For a given host and path, return a linked list of cookies that the
1016 * client should send to the server if used now. The secure boolean informs
1017 * the cookie if a secure connection is achieved or not.
1019 * It shall only return cookies that haven't expired.
1021 ****************************************************************************/
1023 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1024 const char *host, const char *path,
1027 struct Cookie *newco;
1029 time_t now = time(NULL);
1030 struct Cookie *mainco=NULL;
1034 if(!c || !c->cookies)
1035 return NULL; /* no cookie struct or no cookies in the struct */
1037 /* at first, remove expired cookies */
1040 /* check if host is an IP(v4|v6) address */
1046 /* only process this cookie if it is not expired or had no expire
1047 date AND that if the cookie requires we're secure we must only
1048 continue if we are! */
1049 if((!co->expires || (co->expires > now)) &&
1050 (co->secure?secure:TRUE)) {
1052 /* now check if the domain is correct */
1054 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
1055 ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
1056 /* the right part of the host matches the domain stuff in the
1059 /* now check the left part of the path with the cookies path
1061 if(!co->spath || pathmatch(co->spath, path) ) {
1063 /* and now, we know this is a match and we should create an
1064 entry for the return-linked-list */
1066 newco = malloc(sizeof(struct Cookie));
1068 /* first, copy the whole source cookie: */
1069 memcpy(newco, co, sizeof(struct Cookie));
1071 /* then modify our next */
1072 newco->next = mainco;
1074 /* point the main to us */
1081 /* failure, clear up the allocated chain and return NULL */
1097 /* Now we need to make sure that if there is a name appearing more than
1098 once, the longest specified path version comes first. To make this
1099 the swiftest way, we just sort them all based on path length. */
1100 struct Cookie **array;
1103 /* alloc an array and store all cookie pointers */
1104 array = malloc(sizeof(struct Cookie *) * matches);
1110 for(i=0; co; co = co->next)
1113 /* now sort the cookie pointers in path length order */
1114 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1116 /* remake the linked list order according to the new order */
1118 mainco = array[0]; /* start here */
1119 for(i=0; i<matches-1; i++)
1120 array[i]->next = array[i+1];
1121 array[matches-1]->next = NULL; /* terminate the list */
1123 free(array); /* remove the temporary data again */
1126 return mainco; /* return the new list */
1129 /*****************************************************************************
1131 * Curl_cookie_clearall()
1133 * Clear all existing cookies and reset the counter.
1135 ****************************************************************************/
1136 void Curl_cookie_clearall(struct CookieInfo *cookies)
1139 Curl_cookie_freelist(cookies->cookies, TRUE);
1140 cookies->cookies = NULL;
1141 cookies->numcookies = 0;
1145 /*****************************************************************************
1147 * Curl_cookie_freelist()
1149 * Free a list of cookies previously returned by Curl_cookie_getlist();
1151 * The 'cookiestoo' argument tells this function whether to just free the
1152 * list or actually also free all cookies within the list as well.
1154 ****************************************************************************/
1156 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
1158 struct Cookie *next;
1164 free(co); /* we only free the struct since the "members" are all just
1165 pointed out in the main cookie list! */
1171 /*****************************************************************************
1173 * Curl_cookie_clearsess()
1175 * Free all session cookies in the cookies list.
1177 ****************************************************************************/
1178 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1180 struct Cookie *first, *curr, *next, *prev = NULL;
1182 if(!cookies || !cookies->cookies)
1185 first = curr = prev = cookies->cookies;
1187 for(; curr; curr = next) {
1189 if(!curr->expires) {
1199 cookies->numcookies--;
1205 cookies->cookies = first;
1209 /*****************************************************************************
1211 * Curl_cookie_cleanup()
1213 * Free a "cookie object" previous created with Curl_cookie_init().
1215 ****************************************************************************/
1216 void Curl_cookie_cleanup(struct CookieInfo *c)
1221 Curl_cookie_freelist(c->cookies, TRUE);
1222 free(c); /* free the base struct as well */
1226 /* get_netscape_format()
1228 * Formats a string for Netscape output file, w/o a newline at the end.
1230 * Function returns a char * to a formatted line. Has to be free()d
1232 static char *get_netscape_format(const struct Cookie *co)
1235 "%s" /* httponly preamble */
1236 "%s%s\t" /* domain */
1237 "%s\t" /* tailmatch */
1240 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1243 co->httponly?"#HttpOnly_":"",
1244 /* Make sure all domains are prefixed with a dot if they allow
1245 tailmatching. This is Mozilla-style. */
1246 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1247 co->domain?co->domain:"unknown",
1248 co->tailmatch?"TRUE":"FALSE",
1249 co->path?co->path:"/",
1250 co->secure?"TRUE":"FALSE",
1253 co->value?co->value:"");
1259 * Writes all internally known cookies to the specified file. Specify
1260 * "-" as file name to write to stdout.
1262 * The function returns non-zero on write failure.
1264 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1268 bool use_stdout=FALSE;
1270 if((NULL == c) || (0 == c->numcookies))
1271 /* If there are no known cookies, we don't write or even create any
1275 /* at first, remove expired cookies */
1278 if(strequal("-", dumphere)) {
1284 out = fopen(dumphere, "w");
1286 return 1; /* failure */
1292 fputs("# Netscape HTTP Cookie File\n"
1293 "# http://curl.haxx.se/docs/http-cookies.html\n"
1294 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1299 format_ptr = get_netscape_format(co);
1300 if(format_ptr == NULL) {
1301 fprintf(out, "#\n# Fatal libcurl error\n");
1306 fprintf(out, "%s\n", format_ptr);
1318 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1320 struct curl_slist *list = NULL;
1321 struct curl_slist *beg;
1325 if((data->cookies == NULL) ||
1326 (data->cookies->numcookies == 0))
1329 c = data->cookies->cookies;
1332 /* fill the list with _all_ the cookies we know */
1333 line = get_netscape_format(c);
1335 curl_slist_free_all(list);
1338 beg = Curl_slist_append_nodup(list, line);
1341 curl_slist_free_all(list);
1351 void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1353 if(data->set.str[STRING_COOKIEJAR]) {
1354 if(data->change.cookielist) {
1355 /* If there is a list of cookie files to read, do it first so that
1356 we have all the told files read before we write the new jar.
1357 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1358 Curl_cookie_loadfiles(data);
1361 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1363 /* if we have a destination file for all the cookies to get dumped to */
1364 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1365 infof(data, "WARNING: failed to save cookies in %s\n",
1366 data->set.str[STRING_COOKIEJAR]);
1369 if(cleanup && data->change.cookielist) {
1370 /* since nothing is written, we can just free the list of cookie file
1372 curl_slist_free_all(data->change.cookielist); /* clean up list */
1373 data->change.cookielist = NULL;
1375 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1378 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1379 Curl_cookie_cleanup(data->cookies);
1381 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1384 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */