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 *cookie_init(char *file);
31 Inits a cookie struct to store data in a local file. This is always
32 called before any cookies are set.
34 int cookies_set(struct CookieInfo *cookie, char *cookie_line);
36 The 'cookie_line' parameter is a full "Set-cookie:" line as
37 received from a server.
39 The function need to replace previously stored lines that this new
42 It may remove lines that are expired.
44 It should return an indication of success/error.
47 SENDING COOKIE INFORMATION
48 ==========================
50 struct Cookies *cookie_getlist(struct CookieInfo *cookie,
51 char *host, char *path, bool secure);
53 For a given host and path, return a linked list of cookies that
54 the client should send to the server if used now. The secure
55 boolean informs the cookie if a secure connection is achieved or
58 It shall only return cookies that haven't expired.
61 Example set of cookies:
63 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
64 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
65 domain=.fidelity.com; path=/ftgw; secure
66 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
67 domain=.fidelity.com; path=/; secure
68 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
69 domain=.fidelity.com; path=/; secure
70 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
71 domain=.fidelity.com; path=/; secure
72 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
73 domain=.fidelity.com; path=/; secure
75 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
76 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
80 #include "curl_setup.h"
82 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
84 #define _MPRINTF_REPLACE
85 #include <curl/mprintf.h>
93 #include "curl_memory.h"
95 #include "strtoofft.h"
97 #include "curl_memrchr.h"
99 /* The last #include file should be: */
100 #include "memdebug.h"
102 static void freecookie(struct Cookie *co)
124 static bool tailmatch(const char *cooke_domain, const char *hostname)
126 size_t cookie_domain_len = strlen(cooke_domain);
127 size_t hostname_len = strlen(hostname);
129 if(hostname_len < cookie_domain_len)
132 if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len))
135 /* A lead char of cookie_domain is not '.'.
136 RFC6265 4.1.2.3. The Domain Attribute says:
137 For example, if the value of the Domain attribute is
138 "example.com", the user agent will include the cookie in the Cookie
139 header when making HTTP requests to example.com, www.example.com, and
140 www.corp.example.com.
142 if(hostname_len == cookie_domain_len)
144 if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
150 * matching cookie path and url path
151 * RFC6265 5.1.4 Paths and Path-Match
153 static bool pathmatch(const char* cookie_path, const char* request_uri)
155 size_t cookie_path_len;
157 char* uri_path = NULL;
161 /* cookie_path must not have last '/' separator. ex: /sample */
162 cookie_path_len = strlen(cookie_path);
163 if(1 == cookie_path_len) {
164 /* cookie_path must be '/' */
168 uri_path = strdup(request_uri);
171 pos = strchr(uri_path, '?');
175 /* #-fragments are already cut off! */
176 if(0 == strlen(uri_path) || uri_path[0] != '/') {
178 uri_path = strdup("/");
183 /* here, RFC6265 5.1.4 says
184 4. Output the characters of the uri-path from the first character up
185 to, but not including, the right-most %x2F ("/").
186 but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
188 Ignore this algorithm because /hoge is uri path for this case
192 uri_path_len = strlen(uri_path);
194 if(uri_path_len < cookie_path_len) {
199 /* not using checkprefix() because matching should be case-sensitive */
200 if(strncmp(cookie_path, uri_path, cookie_path_len)) {
205 /* The cookie-path and the uri-path are identical. */
206 if(cookie_path_len == uri_path_len) {
211 /* here, cookie_path_len < url_path_len */
212 if(uri_path[cookie_path_len] == '/') {
225 * cookie path sanitize
227 static char *sanitize_cookie_path(const char *cookie_path)
230 char *new_path = strdup(cookie_path);
234 /* some stupid site sends path attribute with '"'. */
235 if(new_path[0] == '\"') {
236 memmove((void *)new_path, (const void *)(new_path + 1), strlen(new_path));
238 if(new_path[strlen(new_path) - 1] == '\"') {
239 new_path[strlen(new_path) - 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 len = strlen(new_path);
252 if(1 < len && new_path[len - 1] == '/') {
253 new_path[len - 1] = 0x0;
260 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
262 void Curl_cookie_loadfiles(struct SessionHandle *data)
264 struct curl_slist *list = data->change.cookielist;
266 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
268 data->cookies = Curl_cookie_init(data,
271 data->set.cookiesession);
274 curl_slist_free_all(data->change.cookielist); /* clean up list */
275 data->change.cookielist = NULL; /* don't do this again! */
276 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
281 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
282 * that will be freed before the allocated string is stored there.
284 * It is meant to easily replace strdup()
286 static void strstore(char **str, const char *newstr)
290 *str = strdup(newstr);
294 * remove_expired() removes expired cookies.
296 static void remove_expired(struct CookieInfo *cookies)
298 struct Cookie *co, *nx, *pv;
299 curl_off_t now = (curl_off_t)time(NULL);
301 co = cookies->cookies;
305 if((co->expirestr || co->maxage) && co->expires < now) {
306 if(co == cookies->cookies) {
307 cookies->cookies = co->next;
312 cookies->numcookies--;
322 /****************************************************************************
326 * Add a single cookie line to the cookie keeping object.
328 * Be aware that sometimes we get an IP-only host name, and that might also be
329 * a numerical IPv6 address.
331 ***************************************************************************/
334 Curl_cookie_add(struct SessionHandle *data,
335 /* The 'data' pointer here may be NULL at times, and thus
336 must only be used very carefully for things that can deal
337 with data being NULL. Such as infof() and similar */
339 struct CookieInfo *c,
340 bool httpheader, /* TRUE if HTTP header-style line */
341 char *lineptr, /* first character of the line */
342 const char *domain, /* default domain */
343 const char *path) /* full path used when this cookie is set,
344 used to get default path for the cookie
347 struct Cookie *clist;
350 struct Cookie *lastc=NULL;
351 time_t now = time(NULL);
352 bool replace_old = FALSE;
353 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
355 #ifdef CURL_DISABLE_VERBOSE_STRINGS
359 /* First, alloc and init a new struct for it */
360 co = calloc(1, sizeof(struct Cookie));
362 return NULL; /* bail out if we're this low on memory */
365 /* This line was read off a HTTP-header */
370 what = malloc(MAX_COOKIE_LINE);
376 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
378 while(*lineptr && ISBLANK(*lineptr))
383 /* we have a <what>=<this> pair or a stand-alone word here */
384 name[0]=what[0]=0; /* init the buffers */
385 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%"
386 MAX_COOKIE_LINE_TXT "[^;\r\n]",
388 /* Use strstore() below to properly deal with received cookie
389 headers that have the same string property set more than once,
390 and then we use the last one. */
394 size_t len=strlen(what);
395 const char *endofn = &ptr[ strlen(name) ];
397 /* skip trailing spaces in name */
398 while(*endofn && ISBLANK(*endofn))
401 /* name ends with a '=' ? */
402 sep = (*endofn == '=')?TRUE:FALSE;
404 /* Strip off trailing whitespace from the 'what' */
405 while(len && ISBLANK(what[len-1])) {
410 /* Skip leading whitespace from the 'what' */
412 while(*whatptr && ISBLANK(*whatptr))
416 /* this was a "<name>=" with no content, and we must allow
417 'secure' and 'httponly' specified this weirdly */
419 if(Curl_raw_equal("secure", name))
421 else if(Curl_raw_equal("httponly", name))
424 /* there was a '=' so we're not done parsing this field */
429 else if(Curl_raw_equal("path", name)) {
430 strstore(&co->path, whatptr);
432 badcookie = TRUE; /* out of memory bad */
435 co->spath = sanitize_cookie_path(co->path);
437 badcookie = TRUE; /* out of memory bad */
441 else if(Curl_raw_equal("domain", name)) {
442 /* Now, we make sure that our host is within the given domain,
443 or the given domain is not valid and thus cannot be set. */
445 if('.' == whatptr[0])
446 whatptr++; /* ignore preceding dot */
448 if(!domain || tailmatch(whatptr, domain)) {
449 const char *tailptr=whatptr;
450 if(tailptr[0] == '.')
452 strstore(&co->domain, tailptr); /* don't prefix w/dots
458 co->tailmatch=TRUE; /* we always do that if the domain name was
462 /* we did not get a tailmatch and then the attempted set domain
463 is not a domain to which the current host belongs. Mark as
466 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
470 else if(Curl_raw_equal("version", name)) {
471 strstore(&co->version, whatptr);
477 else if(Curl_raw_equal("max-age", name)) {
478 /* Defined in RFC2109:
480 Optional. The Max-Age attribute defines the lifetime of the
481 cookie, in seconds. The delta-seconds value is a decimal non-
482 negative integer. After delta-seconds seconds elapse, the
483 client should discard the cookie. A value of zero means the
484 cookie should be discarded immediately.
487 strstore(&co->maxage, whatptr);
493 else if(Curl_raw_equal("expires", name)) {
494 strstore(&co->expirestr, whatptr);
501 co->name = strdup(name);
502 co->value = strdup(whatptr);
503 if(!co->name || !co->value) {
509 else this is the second (or more) name we don't know
513 /* this is an "illegal" <what>=<this> pair */
516 if(!semiptr || !*semiptr) {
517 /* we already know there are no more cookies */
523 while(*ptr && ISBLANK(*ptr))
525 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
528 /* There are no more semicolons, but there's a final name=value pair
530 semiptr=strchr(ptr, '\0');
535 curlx_strtoofft((*co->maxage=='\"')?
536 &co->maxage[1]:&co->maxage[0], NULL, 10);
537 if(CURL_OFF_T_MAX - now < co->expires)
539 co->expires = CURL_OFF_T_MAX;
543 else if(co->expirestr) {
544 /* Note that if the date couldn't get parsed for whatever reason,
545 the cookie will be treated as a session cookie */
546 co->expires = curl_getdate(co->expirestr, NULL);
548 /* Session cookies have expires set to 0 so if we get that back
549 from the date parser let's add a second to make it a
550 non-session cookie */
553 else if(co->expires < 0)
557 if(!badcookie && !co->domain) {
559 /* no domain was given in the header line, set the default */
560 co->domain=strdup(domain);
566 if(!badcookie && !co->path && path) {
567 /* No path was given in the header line, set the default.
568 Note that the passed-in path to this function MAY have a '?' and
569 following part that MUST not be stored as part of the path. */
570 char *queryp = strchr(path, '?');
572 /* queryp is where the interesting part of the path ends, so now we
573 want to the find the last */
576 endslash = strrchr(path, '/');
578 endslash = memrchr(path, '/', (size_t)(queryp - path));
580 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
581 co->path=malloc(pathlen+1); /* one extra for the zero byte */
583 memcpy(co->path, path, pathlen);
584 co->path[pathlen]=0; /* zero terminate */
585 co->spath = sanitize_cookie_path(co->path);
587 badcookie = TRUE; /* out of memory bad */
596 if(badcookie || !co->name) {
597 /* we didn't get a cookie name or a bad one,
598 this is an illegal line, bail out */
605 /* This line is NOT a HTTP header style line, we do offer support for
606 reading the odd netscape cookies-file format here */
612 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
613 marked with httpOnly after the domain name are not accessible
614 from javascripts, but since curl does not operate at javascript
615 level, we include them anyway. In Firefox's cookie files, these
616 lines are preceded with #HttpOnly_ and then everything is
617 as usual, so we skip 10 characters of the line..
619 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
624 if(lineptr[0]=='#') {
625 /* don't even try the comments */
629 /* strip off the possible end-of-line characters */
630 ptr=strchr(lineptr, '\r');
632 *ptr=0; /* clear it */
633 ptr=strchr(lineptr, '\n');
635 *ptr=0; /* clear it */
637 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
639 /* Now loop through the fields and init the struct we already have
641 for(ptr=firstptr, fields=0; ptr && !badcookie;
642 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
645 if(ptr[0]=='.') /* skip preceding dots */
647 co->domain = strdup(ptr);
652 /* This field got its explanation on the 23rd of May 2001 by
655 flag: A TRUE/FALSE value indicating if all machines within a given
656 domain can access the variable. This value is set automatically by
657 the browser, depending on the value you set for the domain.
659 As far as I can see, it is set to true when the cookie says
660 .domain.com and to false when the domain is complete www.domain.com
662 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
665 /* It turns out, that sometimes the file format allows the path
666 field to remain not filled in, we try to detect this and work
667 around it! Andrés GarcÃa made us aware of this... */
668 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
669 /* only if the path doesn't look like a boolean option! */
670 co->path = strdup(ptr);
674 co->spath = sanitize_cookie_path(co->path);
676 badcookie = TRUE; /* out of memory bad */
681 /* this doesn't look like a path, make one up! */
682 co->path = strdup("/");
685 co->spath = strdup("/");
688 fields++; /* add a field and fall down to secure */
691 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
694 co->expires = curlx_strtoofft(ptr, NULL, 10);
697 co->name = strdup(ptr);
702 co->value = strdup(ptr);
709 /* we got a cookie with blank contents, fix it */
710 co->value = strdup("");
717 if(!badcookie && (7 != fields))
718 /* we did not find the sufficient number of fields */
728 if(!c->running && /* read from a file */
729 c->newsession && /* clean session cookies */
730 !co->expires) { /* this is a session cookie since it doesn't expire! */
735 co->livecookie = c->running;
737 /* now, we have parsed the incoming line, we must now check if this
738 superceeds an already existing cookie, which it may if the previous have
739 the same domain and path as this */
741 /* at first, remove expired cookies */
747 if(Curl_raw_equal(clist->name, co->name)) {
748 /* the names are identical */
750 if(clist->domain && co->domain) {
751 if(Curl_raw_equal(clist->domain, co->domain))
752 /* The domains are identical */
755 else if(!clist->domain && !co->domain)
759 /* the domains were identical */
761 if(clist->spath && co->spath) {
762 if(Curl_raw_equal(clist->spath, co->spath)) {
768 else if(!clist->spath && !co->spath)
775 if(replace_old && !co->livecookie && clist->livecookie) {
776 /* Both cookies matched fine, except that the already present
777 cookie is "live", which means it was set from a header, while
778 the new one isn't "live" and thus only read from a file. We let
779 live cookies stay alive */
781 /* Free the newcomer and get out of here! */
787 co->next = clist->next; /* get the next-pointer first */
789 /* then free all the old pointers */
800 free(clist->expirestr);
803 free(clist->version);
807 *clist = *co; /* then store all the new data */
809 free(co); /* free the newly alloced memory */
810 co = clist; /* point to the previous struct instead */
812 /* We have replaced a cookie, now skip the rest of the list but
813 make sure the 'lastc' pointer is properly set */
826 /* Only show this when NOT reading the cookies from a file */
827 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
828 "expire %" CURL_FORMAT_CURL_OFF_T "\n",
829 replace_old?"Replaced":"Added", co->name, co->value,
830 co->domain, co->path, co->expires);
833 /* then make the last item point on this new one */
838 c->numcookies++; /* one more cookie in the jar */
844 /*****************************************************************************
848 * Inits a cookie struct to read data from a local file. This is always
849 * called before any cookies are set. File may be NULL.
851 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
853 ****************************************************************************/
854 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
856 struct CookieInfo *inc,
859 struct CookieInfo *c;
864 /* we didn't get a struct, create one */
865 c = calloc(1, sizeof(struct CookieInfo));
867 return NULL; /* failed to get memory */
868 c->filename = strdup(file?file:"none"); /* copy the name just in case */
871 /* we got an already existing one, use that */
874 c->running = FALSE; /* this is not running, this is init */
876 if(file && strequal(file, "-")) {
880 else if(file && !*file) {
881 /* points to a "" string */
885 fp = file?fopen(file, "r"):NULL;
887 c->newsession = newsession; /* new session? */
893 char *line = malloc(MAX_COOKIE_LINE);
895 while(fgets(line, MAX_COOKIE_LINE, fp)) {
896 if(checkprefix("Set-Cookie:", line)) {
897 /* This is a cookie line, get it! */
905 while(*lineptr && ISBLANK(*lineptr))
908 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
910 free(line); /* free the line buffer */
916 c->running = TRUE; /* now, we're running */
921 /* sort this so that the longest path gets before the shorter path */
922 static int cookie_sort(const void *p1, const void *p2)
924 struct Cookie *c1 = *(struct Cookie **)p1;
925 struct Cookie *c2 = *(struct Cookie **)p2;
928 /* 1 - compare cookie path lengths */
929 l1 = c1->path ? strlen(c1->path) : 0;
930 l2 = c2->path ? strlen(c2->path) : 0;
933 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
935 /* 2 - compare cookie domain lengths */
936 l1 = c1->domain ? strlen(c1->domain) : 0;
937 l2 = c2->domain ? strlen(c2->domain) : 0;
940 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
942 /* 3 - compare cookie names */
943 if(c1->name && c2->name)
944 return strcmp(c1->name, c2->name);
946 /* sorry, can't be more deterministic */
950 /*****************************************************************************
952 * Curl_cookie_getlist()
954 * For a given host and path, return a linked list of cookies that the
955 * client should send to the server if used now. The secure boolean informs
956 * the cookie if a secure connection is achieved or not.
958 * It shall only return cookies that haven't expired.
960 ****************************************************************************/
962 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
963 const char *host, const char *path,
966 struct Cookie *newco;
968 time_t now = time(NULL);
969 struct Cookie *mainco=NULL;
972 if(!c || !c->cookies)
973 return NULL; /* no cookie struct or no cookies in the struct */
975 /* at first, remove expired cookies */
981 /* only process this cookie if it is not expired or had no expire
982 date AND that if the cookie requires we're secure we must only
983 continue if we are! */
984 if((!co->expires || (co->expires > now)) &&
985 (co->secure?secure:TRUE)) {
987 /* now check if the domain is correct */
989 (co->tailmatch && tailmatch(co->domain, host)) ||
990 (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
991 /* the right part of the host matches the domain stuff in the
994 /* now check the left part of the path with the cookies path
996 if(!co->spath || pathmatch(co->spath, path) ) {
998 /* and now, we know this is a match and we should create an
999 entry for the return-linked-list */
1001 newco = malloc(sizeof(struct Cookie));
1003 /* first, copy the whole source cookie: */
1004 memcpy(newco, co, sizeof(struct Cookie));
1006 /* then modify our next */
1007 newco->next = mainco;
1009 /* point the main to us */
1016 /* failure, clear up the allocated chain and return NULL */
1032 /* Now we need to make sure that if there is a name appearing more than
1033 once, the longest specified path version comes first. To make this
1034 the swiftest way, we just sort them all based on path length. */
1035 struct Cookie **array;
1038 /* alloc an array and store all cookie pointers */
1039 array = malloc(sizeof(struct Cookie *) * matches);
1045 for(i=0; co; co = co->next)
1048 /* now sort the cookie pointers in path length order */
1049 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1051 /* remake the linked list order according to the new order */
1053 mainco = array[0]; /* start here */
1054 for(i=0; i<matches-1; i++)
1055 array[i]->next = array[i+1];
1056 array[matches-1]->next = NULL; /* terminate the list */
1058 free(array); /* remove the temporary data again */
1061 return mainco; /* return the new list */
1064 /*****************************************************************************
1066 * Curl_cookie_clearall()
1068 * Clear all existing cookies and reset the counter.
1070 ****************************************************************************/
1071 void Curl_cookie_clearall(struct CookieInfo *cookies)
1074 Curl_cookie_freelist(cookies->cookies, TRUE);
1075 cookies->cookies = NULL;
1076 cookies->numcookies = 0;
1080 /*****************************************************************************
1082 * Curl_cookie_freelist()
1084 * Free a list of cookies previously returned by Curl_cookie_getlist();
1086 * The 'cookiestoo' argument tells this function whether to just free the
1087 * list or actually also free all cookies within the list as well.
1089 ****************************************************************************/
1091 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
1093 struct Cookie *next;
1100 free(co); /* we only free the struct since the "members" are all just
1101 pointed out in the main cookie list! */
1108 /*****************************************************************************
1110 * Curl_cookie_clearsess()
1112 * Free all session cookies in the cookies list.
1114 ****************************************************************************/
1115 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1117 struct Cookie *first, *curr, *next, *prev = NULL;
1119 if(!cookies || !cookies->cookies)
1122 first = curr = prev = cookies->cookies;
1124 for(; curr; curr = next) {
1126 if(!curr->expires) {
1136 cookies->numcookies--;
1142 cookies->cookies = first;
1146 /*****************************************************************************
1148 * Curl_cookie_cleanup()
1150 * Free a "cookie object" previous created with cookie_init().
1152 ****************************************************************************/
1153 void Curl_cookie_cleanup(struct CookieInfo *c)
1156 struct Cookie *next;
1167 free(c); /* free the base struct as well */
1171 /* get_netscape_format()
1173 * Formats a string for Netscape output file, w/o a newline at the end.
1175 * Function returns a char * to a formatted line. Has to be free()d
1177 static char *get_netscape_format(const struct Cookie *co)
1180 "%s" /* httponly preamble */
1181 "%s%s\t" /* domain */
1182 "%s\t" /* tailmatch */
1185 "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
1188 co->httponly?"#HttpOnly_":"",
1189 /* Make sure all domains are prefixed with a dot if they allow
1190 tailmatching. This is Mozilla-style. */
1191 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1192 co->domain?co->domain:"unknown",
1193 co->tailmatch?"TRUE":"FALSE",
1194 co->path?co->path:"/",
1195 co->secure?"TRUE":"FALSE",
1198 co->value?co->value:"");
1204 * Writes all internally known cookies to the specified file. Specify
1205 * "-" as file name to write to stdout.
1207 * The function returns non-zero on write failure.
1209 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1213 bool use_stdout=FALSE;
1215 if((NULL == c) || (0 == c->numcookies))
1216 /* If there are no known cookies, we don't write or even create any
1220 /* at first, remove expired cookies */
1223 if(strequal("-", dumphere)) {
1229 out = fopen(dumphere, "w");
1231 return 1; /* failure */
1237 fputs("# Netscape HTTP Cookie File\n"
1238 "# http://curl.haxx.se/docs/http-cookies.html\n"
1239 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1244 format_ptr = get_netscape_format(co);
1245 if(format_ptr == NULL) {
1246 fprintf(out, "#\n# Fatal libcurl error\n");
1251 fprintf(out, "%s\n", format_ptr);
1263 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1265 struct curl_slist *list = NULL;
1266 struct curl_slist *beg;
1270 if((data->cookies == NULL) ||
1271 (data->cookies->numcookies == 0))
1274 c = data->cookies->cookies;
1277 /* fill the list with _all_ the cookies we know */
1278 line = get_netscape_format(c);
1280 curl_slist_free_all(list);
1283 beg = Curl_slist_append_nodup(list, line);
1286 curl_slist_free_all(list);
1296 void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1298 if(data->set.str[STRING_COOKIEJAR]) {
1299 if(data->change.cookielist) {
1300 /* If there is a list of cookie files to read, do it first so that
1301 we have all the told files read before we write the new jar.
1302 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1303 Curl_cookie_loadfiles(data);
1306 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1308 /* if we have a destination file for all the cookies to get dumped to */
1309 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1310 infof(data, "WARNING: failed to save cookies in %s\n",
1311 data->set.str[STRING_COOKIEJAR]);
1314 if(cleanup && data->change.cookielist) {
1315 /* since nothing is written, we can just free the list of cookie file
1317 curl_slist_free_all(data->change.cookielist); /* clean up list */
1318 data->change.cookielist = NULL;
1320 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1323 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1324 Curl_cookie_cleanup(data->cookies);
1326 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1329 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */