1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, 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 #include "curl_printf.h"
95 #include "strtoofft.h"
97 #include "curl_memrchr.h"
98 #include "inet_pton.h"
100 /* The last #include files should be: */
101 #include "curl_memory.h"
102 #include "memdebug.h"
104 static void freecookie(struct Cookie *co)
117 static bool tailmatch(const char *cooke_domain, const char *hostname)
119 size_t cookie_domain_len = strlen(cooke_domain);
120 size_t hostname_len = strlen(hostname);
122 if(hostname_len < cookie_domain_len)
125 if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len))
128 /* A lead char of cookie_domain is not '.'.
129 RFC6265 4.1.2.3. The Domain Attribute says:
130 For example, if the value of the Domain attribute is
131 "example.com", the user agent will include the cookie in the Cookie
132 header when making HTTP requests to example.com, www.example.com, and
133 www.corp.example.com.
135 if(hostname_len == cookie_domain_len)
137 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
143 * matching cookie path and url path
144 * RFC6265 5.1.4 Paths and Path-Match
146 static bool pathmatch(const char* cookie_path, const char* request_uri)
148 size_t cookie_path_len;
150 char* uri_path = NULL;
154 /* cookie_path must not have last '/' separator. ex: /sample */
155 cookie_path_len = strlen(cookie_path);
156 if(1 == cookie_path_len) {
157 /* cookie_path must be '/' */
161 uri_path = strdup(request_uri);
164 pos = strchr(uri_path, '?');
168 /* #-fragments are already cut off! */
169 if(0 == strlen(uri_path) || uri_path[0] != '/') {
171 uri_path = strdup("/");
176 /* here, RFC6265 5.1.4 says
177 4. Output the characters of the uri-path from the first character up
178 to, but not including, the right-most %x2F ("/").
179 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
181 Ignore this algorithm because /hoge is uri path for this case
185 uri_path_len = strlen(uri_path);
187 if(uri_path_len < cookie_path_len) {
192 /* not using checkprefix() because matching should be case-sensitive */
193 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
198 /* The cookie-path and the uri-path are identical. */
199 if(cookie_path_len == uri_path_len) {
204 /* here, cookie_path_len < url_path_len */
205 if(uri_path[cookie_path_len] == '/') {
218 * cookie path sanitize
220 static char *sanitize_cookie_path(const char *cookie_path)
223 char *new_path = strdup(cookie_path);
227 /* some stupid site sends path attribute with '"'. */
228 len = strlen(new_path);
229 if(new_path[0] == '\"') {
230 memmove((void *)new_path, (const void *)(new_path + 1), len);
233 if(len && (new_path[len - 1] == '\"')) {
234 new_path[len - 1] = 0x0;
238 /* RFC6265 5.2.4 The Path Attribute */
239 if(new_path[0] != '/') {
240 /* Let cookie-path be the default-path. */
242 new_path = strdup("/");
246 /* convert /hoge/ to /hoge */
247 if(len && new_path[len - 1] == '/') {
248 new_path[len - 1] = 0x0;
255 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
257 * NOTE: OOM or cookie parsing failures are ignored.
259 void Curl_cookie_loadfiles(struct SessionHandle *data)
261 struct curl_slist *list = data->change.cookielist;
263 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
265 struct CookieInfo *newcookies = Curl_cookie_init(data,
268 data->set.cookiesession);
270 /* Failure may be due to OOM or a bad cookie; both are ignored
271 * but only the first should be
273 infof(data, "ignoring failed cookie_init for %s\n", list->data);
275 data->cookies = newcookies;
278 curl_slist_free_all(data->change.cookielist); /* clean up list */
279 data->change.cookielist = NULL; /* don't do this again! */
280 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
285 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
286 * that will be freed before the allocated string is stored there.
288 * It is meant to easily replace strdup()
290 static void strstore(char **str, const char *newstr)
293 *str = strdup(newstr);
297 * remove_expired() removes expired cookies.
299 static void remove_expired(struct CookieInfo *cookies)
301 struct Cookie *co, *nx, *pv;
302 curl_off_t now = (curl_off_t)time(NULL);
304 co = cookies->cookies;
308 if((co->expirestr || co->maxage) && co->expires < now) {
309 if(co == cookies->cookies) {
310 cookies->cookies = co->next;
315 cookies->numcookies--;
326 * Return true if the given string is an IP(v4|v6) address.
328 static bool isip(const char *domain)
332 struct in6_addr addr6;
335 if(Curl_inet_pton(AF_INET, domain, &addr)
337 || Curl_inet_pton(AF_INET6, domain, &addr6)
340 /* domain name given as IP address */
347 /****************************************************************************
351 * Add a single cookie line to the cookie keeping object.
353 * Be aware that sometimes we get an IP-only host name, and that might also be
354 * a numerical IPv6 address.
356 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
357 * as they should be treated separately.
358 ***************************************************************************/
361 Curl_cookie_add(struct SessionHandle *data,
362 /* The 'data' pointer here may be NULL at times, and thus
363 must only be used very carefully for things that can deal
364 with data being NULL. Such as infof() and similar */
366 struct CookieInfo *c,
367 bool httpheader, /* TRUE if HTTP header-style line */
368 char *lineptr, /* first character of the line */
369 const char *domain, /* default domain */
370 const char *path) /* full path used when this cookie is set,
371 used to get default path for the cookie
374 struct Cookie *clist;
377 struct Cookie *lastc=NULL;
378 time_t now = time(NULL);
379 bool replace_old = FALSE;
380 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
382 #ifdef CURL_DISABLE_VERBOSE_STRINGS
386 /* First, alloc and init a new struct for it */
387 co = calloc(1, sizeof(struct Cookie));
389 return NULL; /* bail out if we're this low on memory */
392 /* This line was read off a HTTP-header */
397 what = malloc(MAX_COOKIE_LINE);
403 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
405 while(*lineptr && ISBLANK(*lineptr))
410 /* we have a <what>=<this> pair or a stand-alone word here */
411 name[0]=what[0]=0; /* init the buffers */
412 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =] =%"
413 MAX_COOKIE_LINE_TXT "[^;\r\n]",
415 /* Use strstore() below to properly deal with received cookie
416 headers that have the same string property set more than once,
417 and then we use the last one. */
421 size_t len=strlen(what);
422 const char *endofn = &ptr[ strlen(name) ];
424 /* skip trailing spaces in name */
425 while(*endofn && ISBLANK(*endofn))
428 /* name ends with a '=' ? */
429 sep = (*endofn == '=')?TRUE:FALSE;
431 /* Strip off trailing whitespace from the 'what' */
432 while(len && ISBLANK(what[len-1])) {
437 /* Skip leading whitespace from the 'what' */
439 while(*whatptr && ISBLANK(*whatptr))
443 /* this was a "<name>=" with no content, and we must allow
444 'secure' and 'httponly' specified this weirdly */
446 if(Curl_raw_equal("secure", name))
448 else if(Curl_raw_equal("httponly", name))
451 /* there was a '=' so we're not done parsing this field */
456 else if(Curl_raw_equal("path", name)) {
457 strstore(&co->path, whatptr);
459 badcookie = TRUE; /* out of memory bad */
462 co->spath = sanitize_cookie_path(co->path);
464 badcookie = TRUE; /* out of memory bad */
468 else if(Curl_raw_equal("domain", name)) {
472 /* Now, we make sure that our host is within the given domain,
473 or the given domain is not valid and thus cannot be set. */
475 if('.' == whatptr[0])
476 whatptr++; /* ignore preceding dot */
478 is_ip = isip(domain ? domain : whatptr);
480 /* check for more dots */
481 dotp = strchr(whatptr, '.');
486 || (is_ip && !strcmp(whatptr, domain))
487 || (!is_ip && tailmatch(whatptr, domain))) {
488 strstore(&co->domain, whatptr);
494 co->tailmatch=TRUE; /* we always do that if the domain name was
498 /* we did not get a tailmatch and then the attempted set domain
499 is not a domain to which the current host belongs. Mark as
502 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
506 else if(Curl_raw_equal("version", name)) {
507 strstore(&co->version, whatptr);
513 else if(Curl_raw_equal("max-age", name)) {
514 /* Defined in RFC2109:
516 Optional. The Max-Age attribute defines the lifetime of the
517 cookie, in seconds. The delta-seconds value is a decimal non-
518 negative integer. After delta-seconds seconds elapse, the
519 client should discard the cookie. A value of zero means the
520 cookie should be discarded immediately.
523 strstore(&co->maxage, whatptr);
529 else if(Curl_raw_equal("expires", name)) {
530 strstore(&co->expirestr, whatptr);
537 co->name = strdup(name);
538 co->value = strdup(whatptr);
539 if(!co->name || !co->value) {
545 else this is the second (or more) name we don't know
549 /* this is an "illegal" <what>=<this> pair */
552 if(!semiptr || !*semiptr) {
553 /* we already know there are no more cookies */
559 while(*ptr && ISBLANK(*ptr))
561 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
564 /* There are no more semicolons, but there's a final name=value pair
566 semiptr=strchr(ptr, '\0');
571 curlx_strtoofft((*co->maxage=='\"')?
572 &co->maxage[1]:&co->maxage[0], NULL, 10);
573 if(CURL_OFF_T_MAX - now < co->expires)
575 co->expires = CURL_OFF_T_MAX;
579 else if(co->expirestr) {
580 /* Note that if the date couldn't get parsed for whatever reason,
581 the cookie will be treated as a session cookie */
582 co->expires = curl_getdate(co->expirestr, NULL);
584 /* Session cookies have expires set to 0 so if we get that back
585 from the date parser let's add a second to make it a
586 non-session cookie */
589 else if(co->expires < 0)
593 if(!badcookie && !co->domain) {
595 /* no domain was given in the header line, set the default */
596 co->domain=strdup(domain);
602 if(!badcookie && !co->path && path) {
603 /* No path was given in the header line, set the default.
604 Note that the passed-in path to this function MAY have a '?' and
605 following part that MUST not be stored as part of the path. */
606 char *queryp = strchr(path, '?');
608 /* queryp is where the interesting part of the path ends, so now we
609 want to the find the last */
612 endslash = strrchr(path, '/');
614 endslash = memrchr(path, '/', (size_t)(queryp - path));
616 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
617 co->path=malloc(pathlen+1); /* one extra for the zero byte */
619 memcpy(co->path, path, pathlen);
620 co->path[pathlen]=0; /* zero terminate */
621 co->spath = sanitize_cookie_path(co->path);
623 badcookie = TRUE; /* out of memory bad */
632 if(badcookie || !co->name) {
633 /* we didn't get a cookie name or a bad one,
634 this is an illegal line, bail out */
641 /* This line is NOT a HTTP header style line, we do offer support for
642 reading the odd netscape cookies-file format here */
648 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
649 marked with httpOnly after the domain name are not accessible
650 from javascripts, but since curl does not operate at javascript
651 level, we include them anyway. In Firefox's cookie files, these
652 lines are preceded with #HttpOnly_ and then everything is
653 as usual, so we skip 10 characters of the line..
655 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
660 if(lineptr[0]=='#') {
661 /* don't even try the comments */
665 /* strip off the possible end-of-line characters */
666 ptr=strchr(lineptr, '\r');
668 *ptr=0; /* clear it */
669 ptr=strchr(lineptr, '\n');
671 *ptr=0; /* clear it */
673 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
675 /* Now loop through the fields and init the struct we already have
677 for(ptr=firstptr, fields=0; ptr && !badcookie;
678 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
681 if(ptr[0]=='.') /* skip preceding dots */
683 co->domain = strdup(ptr);
688 /* This field got its explanation on the 23rd of May 2001 by
691 flag: A TRUE/FALSE value indicating if all machines within a given
692 domain can access the variable. This value is set automatically by
693 the browser, depending on the value you set for the domain.
695 As far as I can see, it is set to true when the cookie says
696 .domain.com and to false when the domain is complete www.domain.com
698 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
701 /* It turns out, that sometimes the file format allows the path
702 field to remain not filled in, we try to detect this and work
703 around it! Andrés GarcÃa made us aware of this... */
704 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
705 /* only if the path doesn't look like a boolean option! */
706 co->path = strdup(ptr);
710 co->spath = sanitize_cookie_path(co->path);
712 badcookie = TRUE; /* out of memory bad */
717 /* this doesn't look like a path, make one up! */
718 co->path = strdup("/");
721 co->spath = strdup("/");
724 fields++; /* add a field and fall down to secure */
727 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
730 co->expires = curlx_strtoofft(ptr, NULL, 10);
733 co->name = strdup(ptr);
738 co->value = strdup(ptr);
745 /* we got a cookie with blank contents, fix it */
746 co->value = strdup("");
753 if(!badcookie && (7 != fields))
754 /* we did not find the sufficient number of fields */
764 if(!c->running && /* read from a file */
765 c->newsession && /* clean session cookies */
766 !co->expires) { /* this is a session cookie since it doesn't expire! */
771 co->livecookie = c->running;
773 /* now, we have parsed the incoming line, we must now check if this
774 superceeds an already existing cookie, which it may if the previous have
775 the same domain and path as this */
777 /* at first, remove expired cookies */
783 if(Curl_raw_equal(clist->name, co->name)) {
784 /* the names are identical */
786 if(clist->domain && co->domain) {
787 if(Curl_raw_equal(clist->domain, co->domain))
788 /* The domains are identical */
791 else if(!clist->domain && !co->domain)
795 /* the domains were identical */
797 if(clist->spath && co->spath) {
798 if(Curl_raw_equal(clist->spath, co->spath)) {
804 else if(!clist->spath && !co->spath)
811 if(replace_old && !co->livecookie && clist->livecookie) {
812 /* Both cookies matched fine, except that the already present
813 cookie is "live", which means it was set from a header, while
814 the new one isn't "live" and thus only read from a file. We let
815 live cookies stay alive */
817 /* Free the newcomer and get out of here! */
823 co->next = clist->next; /* get the next-pointer first */
825 /* then free all the old pointers */
831 free(clist->expirestr);
832 free(clist->version);
835 *clist = *co; /* then store all the new data */
837 free(co); /* free the newly alloced memory */
838 co = clist; /* point to the previous struct instead */
840 /* We have replaced a cookie, now skip the rest of the list but
841 make sure the 'lastc' pointer is properly set */
854 /* Only show this when NOT reading the cookies from a file */
855 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
856 "expire %" CURL_FORMAT_CURL_OFF_T "\n",
857 replace_old?"Replaced":"Added", co->name, co->value,
858 co->domain, co->path, co->expires);
861 /* then make the last item point on this new one */
866 c->numcookies++; /* one more cookie in the jar */
872 /*****************************************************************************
876 * Inits a cookie struct to read data from a local file. This is always
877 * called before any cookies are set. File may be NULL.
879 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
881 * Returns NULL on out of memory. Invalid cookies are ignored.
882 ****************************************************************************/
883 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
885 struct CookieInfo *inc,
888 struct CookieInfo *c;
894 /* we didn't get a struct, create one */
895 c = calloc(1, sizeof(struct CookieInfo));
897 return NULL; /* failed to get memory */
898 c->filename = strdup(file?file:"none"); /* copy the name just in case */
900 goto fail; /* failed to get memory */
903 /* we got an already existing one, use that */
906 c->running = FALSE; /* this is not running, this is init */
908 if(file && strequal(file, "-")) {
912 else if(file && !*file) {
913 /* points to a "" string */
917 fp = file?fopen(file, FOPEN_READTEXT):NULL;
919 c->newsession = newsession; /* new session? */
925 line = malloc(MAX_COOKIE_LINE);
928 while(fgets(line, MAX_COOKIE_LINE, fp)) {
929 if(checkprefix("Set-Cookie:", line)) {
930 /* This is a cookie line, get it! */
938 while(*lineptr && ISBLANK(*lineptr))
941 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
943 free(line); /* free the line buffer */
949 c->running = TRUE; /* now, we're running */
956 /* Only clean up if we allocated it here, as the original could still be in
957 * use by a share handle */
958 Curl_cookie_cleanup(c);
961 return NULL; /* out of memory */
964 /* sort this so that the longest path gets before the shorter path */
965 static int cookie_sort(const void *p1, const void *p2)
967 struct Cookie *c1 = *(struct Cookie **)p1;
968 struct Cookie *c2 = *(struct Cookie **)p2;
971 /* 1 - compare cookie path lengths */
972 l1 = c1->path ? strlen(c1->path) : 0;
973 l2 = c2->path ? strlen(c2->path) : 0;
976 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
978 /* 2 - compare cookie domain lengths */
979 l1 = c1->domain ? strlen(c1->domain) : 0;
980 l2 = c2->domain ? strlen(c2->domain) : 0;
983 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
985 /* 3 - compare cookie names */
986 if(c1->name && c2->name)
987 return strcmp(c1->name, c2->name);
989 /* sorry, can't be more deterministic */
993 /*****************************************************************************
995 * Curl_cookie_getlist()
997 * For a given host and path, return a linked list of cookies that the
998 * client should send to the server if used now. The secure boolean informs
999 * the cookie if a secure connection is achieved or not.
1001 * It shall only return cookies that haven't expired.
1003 ****************************************************************************/
1005 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
1006 const char *host, const char *path,
1009 struct Cookie *newco;
1011 time_t now = time(NULL);
1012 struct Cookie *mainco=NULL;
1016 if(!c || !c->cookies)
1017 return NULL; /* no cookie struct or no cookies in the struct */
1019 /* at first, remove expired cookies */
1022 /* check if host is an IP(v4|v6) address */
1028 /* only process this cookie if it is not expired or had no expire
1029 date AND that if the cookie requires we're secure we must only
1030 continue if we are! */
1031 if((!co->expires || (co->expires > now)) &&
1032 (co->secure?secure:TRUE)) {
1034 /* now check if the domain is correct */
1036 (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
1037 ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
1038 /* the right part of the host matches the domain stuff in the
1041 /* now check the left part of the path with the cookies path
1043 if(!co->spath || pathmatch(co->spath, path) ) {
1045 /* and now, we know this is a match and we should create an
1046 entry for the return-linked-list */
1048 newco = malloc(sizeof(struct Cookie));
1050 /* first, copy the whole source cookie: */
1051 memcpy(newco, co, sizeof(struct Cookie));
1053 /* then modify our next */
1054 newco->next = mainco;
1056 /* point the main to us */
1063 /* failure, clear up the allocated chain and return NULL */
1079 /* Now we need to make sure that if there is a name appearing more than
1080 once, the longest specified path version comes first. To make this
1081 the swiftest way, we just sort them all based on path length. */
1082 struct Cookie **array;
1085 /* alloc an array and store all cookie pointers */
1086 array = malloc(sizeof(struct Cookie *) * matches);
1092 for(i=0; co; co = co->next)
1095 /* now sort the cookie pointers in path length order */
1096 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1098 /* remake the linked list order according to the new order */
1100 mainco = array[0]; /* start here */
1101 for(i=0; i<matches-1; i++)
1102 array[i]->next = array[i+1];
1103 array[matches-1]->next = NULL; /* terminate the list */
1105 free(array); /* remove the temporary data again */
1108 return mainco; /* return the new list */
1111 /*****************************************************************************
1113 * Curl_cookie_clearall()
1115 * Clear all existing cookies and reset the counter.
1117 ****************************************************************************/
1118 void Curl_cookie_clearall(struct CookieInfo *cookies)
1121 Curl_cookie_freelist(cookies->cookies, TRUE);
1122 cookies->cookies = NULL;
1123 cookies->numcookies = 0;
1127 /*****************************************************************************
1129 * Curl_cookie_freelist()
1131 * Free a list of cookies previously returned by Curl_cookie_getlist();
1133 * The 'cookiestoo' argument tells this function whether to just free the
1134 * list or actually also free all cookies within the list as well.
1136 ****************************************************************************/
1138 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
1140 struct Cookie *next;
1146 free(co); /* we only free the struct since the "members" are all just
1147 pointed out in the main cookie list! */
1153 /*****************************************************************************
1155 * Curl_cookie_clearsess()
1157 * Free all session cookies in the cookies list.
1159 ****************************************************************************/
1160 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1162 struct Cookie *first, *curr, *next, *prev = NULL;
1164 if(!cookies || !cookies->cookies)
1167 first = curr = prev = cookies->cookies;
1169 for(; curr; curr = next) {
1171 if(!curr->expires) {
1181 cookies->numcookies--;
1187 cookies->cookies = first;
1191 /*****************************************************************************
1193 * Curl_cookie_cleanup()
1195 * Free a "cookie object" previous created with Curl_cookie_init().
1197 ****************************************************************************/
1198 void Curl_cookie_cleanup(struct CookieInfo *c)
1202 Curl_cookie_freelist(c->cookies, TRUE);
1203 free(c); /* free the base struct as well */
1207 /* get_netscape_format()
1209 * Formats a string for Netscape output file, w/o a newline at the end.
1211 * Function returns a char * to a formatted line. Has to be free()d
1213 static char *get_netscape_format(const struct Cookie *co)
1216 "%s" /* httponly preamble */
1217 "%s%s\t" /* domain */
1218 "%s\t" /* tailmatch */
1221 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1224 co->httponly?"#HttpOnly_":"",
1225 /* Make sure all domains are prefixed with a dot if they allow
1226 tailmatching. This is Mozilla-style. */
1227 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1228 co->domain?co->domain:"unknown",
1229 co->tailmatch?"TRUE":"FALSE",
1230 co->path?co->path:"/",
1231 co->secure?"TRUE":"FALSE",
1234 co->value?co->value:"");
1240 * Writes all internally known cookies to the specified file. Specify
1241 * "-" as file name to write to stdout.
1243 * The function returns non-zero on write failure.
1245 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1249 bool use_stdout=FALSE;
1251 if((NULL == c) || (0 == c->numcookies))
1252 /* If there are no known cookies, we don't write or even create any
1256 /* at first, remove expired cookies */
1259 if(strequal("-", dumphere)) {
1265 out = fopen(dumphere, FOPEN_WRITETEXT);
1267 return 1; /* failure */
1273 fputs("# Netscape HTTP Cookie File\n"
1274 "# http://curl.haxx.se/docs/http-cookies.html\n"
1275 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1278 for(co = c->cookies; co; co = co->next) {
1281 format_ptr = get_netscape_format(co);
1282 if(format_ptr == NULL) {
1283 fprintf(out, "#\n# Fatal libcurl error\n");
1288 fprintf(out, "%s\n", format_ptr);
1299 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1301 struct curl_slist *list = NULL;
1302 struct curl_slist *beg;
1306 if((data->cookies == NULL) ||
1307 (data->cookies->numcookies == 0))
1310 for(c = data->cookies->cookies; c; c = c->next) {
1313 line = get_netscape_format(c);
1315 curl_slist_free_all(list);
1318 beg = Curl_slist_append_nodup(list, line);
1321 curl_slist_free_all(list);
1330 void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1332 if(data->set.str[STRING_COOKIEJAR]) {
1333 if(data->change.cookielist) {
1334 /* If there is a list of cookie files to read, do it first so that
1335 we have all the told files read before we write the new jar.
1336 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1337 Curl_cookie_loadfiles(data);
1340 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1342 /* if we have a destination file for all the cookies to get dumped to */
1343 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1344 infof(data, "WARNING: failed to save cookies in %s\n",
1345 data->set.str[STRING_COOKIEJAR]);
1348 if(cleanup && data->change.cookielist) {
1349 /* since nothing is written, we can just free the list of cookie file
1351 curl_slist_free_all(data->change.cookielist); /* clean up list */
1352 data->change.cookielist = NULL;
1354 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1357 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1358 Curl_cookie_cleanup(data->cookies);
1360 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1363 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */