1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, 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 /****************************************************************************
298 * Add a single cookie line to the cookie keeping object.
300 * Be aware that sometimes we get an IP-only host name, and that might also be
301 * a numerical IPv6 address.
303 ***************************************************************************/
306 Curl_cookie_add(struct SessionHandle *data,
307 /* The 'data' pointer here may be NULL at times, and thus
308 must only be used very carefully for things that can deal
309 with data being NULL. Such as infof() and similar */
311 struct CookieInfo *c,
312 bool httpheader, /* TRUE if HTTP header-style line */
313 char *lineptr, /* first character of the line */
314 const char *domain, /* default domain */
315 const char *path) /* full path used when this cookie is set,
316 used to get default path for the cookie
319 struct Cookie *clist;
322 struct Cookie *lastc=NULL;
323 time_t now = time(NULL);
324 bool replace_old = FALSE;
325 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
327 #ifdef CURL_DISABLE_VERBOSE_STRINGS
331 /* First, alloc and init a new struct for it */
332 co = calloc(1, sizeof(struct Cookie));
334 return NULL; /* bail out if we're this low on memory */
337 /* This line was read off a HTTP-header */
342 what = malloc(MAX_COOKIE_LINE);
348 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
350 while(*lineptr && ISBLANK(*lineptr))
355 /* we have a <what>=<this> pair or a stand-alone word here */
356 name[0]=what[0]=0; /* init the buffers */
357 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =]=%"
358 MAX_COOKIE_LINE_TXT "[^;\r\n]",
360 /* Use strstore() below to properly deal with received cookie
361 headers that have the same string property set more than once,
362 and then we use the last one. */
366 size_t len=strlen(what);
367 const char *endofn = &ptr[ strlen(name) ];
369 /* skip trailing spaces in name */
370 while(*endofn && ISBLANK(*endofn))
373 /* name ends with a '=' ? */
374 sep = (*endofn == '=')?TRUE:FALSE;
376 /* Strip off trailing whitespace from the 'what' */
377 while(len && ISBLANK(what[len-1])) {
382 /* Skip leading whitespace from the 'what' */
384 while(*whatptr && ISBLANK(*whatptr))
388 /* this was a "<name>=" with no content, and we must allow
389 'secure' and 'httponly' specified this weirdly */
391 if(Curl_raw_equal("secure", name))
393 else if(Curl_raw_equal("httponly", name))
396 /* there was a '=' so we're not done parsing this field */
401 else if(Curl_raw_equal("path", name)) {
402 strstore(&co->path, whatptr);
404 badcookie = TRUE; /* out of memory bad */
407 co->spath = sanitize_cookie_path(co->path);
409 badcookie = TRUE; /* out of memory bad */
413 else if(Curl_raw_equal("domain", name)) {
414 /* Now, we make sure that our host is within the given domain,
415 or the given domain is not valid and thus cannot be set. */
417 if('.' == whatptr[0])
418 whatptr++; /* ignore preceding dot */
420 if(!domain || tailmatch(whatptr, domain)) {
421 const char *tailptr=whatptr;
422 if(tailptr[0] == '.')
424 strstore(&co->domain, tailptr); /* don't prefix w/dots
430 co->tailmatch=TRUE; /* we always do that if the domain name was
434 /* we did not get a tailmatch and then the attempted set domain
435 is not a domain to which the current host belongs. Mark as
438 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
442 else if(Curl_raw_equal("version", name)) {
443 strstore(&co->version, whatptr);
449 else if(Curl_raw_equal("max-age", name)) {
450 /* Defined in RFC2109:
452 Optional. The Max-Age attribute defines the lifetime of the
453 cookie, in seconds. The delta-seconds value is a decimal non-
454 negative integer. After delta-seconds seconds elapse, the
455 client should discard the cookie. A value of zero means the
456 cookie should be discarded immediately.
459 strstore(&co->maxage, whatptr);
465 strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
468 else if(Curl_raw_equal("expires", name)) {
469 strstore(&co->expirestr, whatptr);
474 /* Note that if the date couldn't get parsed for whatever reason,
475 the cookie will be treated as a session cookie */
476 co->expires = curl_getdate(what, &now);
478 /* Session cookies have expires set to 0 so if we get that back
479 from the date parser let's add a second to make it a
480 non-session cookie */
483 else if(co->expires < 0)
487 co->name = strdup(name);
488 co->value = strdup(whatptr);
489 if(!co->name || !co->value) {
495 else this is the second (or more) name we don't know
499 /* this is an "illegal" <what>=<this> pair */
502 if(!semiptr || !*semiptr) {
503 /* we already know there are no more cookies */
509 while(*ptr && ISBLANK(*ptr))
511 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
514 /* There are no more semicolons, but there's a final name=value pair
516 semiptr=strchr(ptr, '\0');
519 if(!badcookie && !co->domain) {
521 /* no domain was given in the header line, set the default */
522 co->domain=strdup(domain);
528 if(!badcookie && !co->path && path) {
529 /* No path was given in the header line, set the default.
530 Note that the passed-in path to this function MAY have a '?' and
531 following part that MUST not be stored as part of the path. */
532 char *queryp = strchr(path, '?');
534 /* queryp is where the interesting part of the path ends, so now we
535 want to the find the last */
538 endslash = strrchr(path, '/');
540 endslash = memrchr(path, '/', (size_t)(queryp - path));
542 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
543 co->path=malloc(pathlen+1); /* one extra for the zero byte */
545 memcpy(co->path, path, pathlen);
546 co->path[pathlen]=0; /* zero terminate */
547 co->spath = sanitize_cookie_path(co->path);
549 badcookie = TRUE; /* out of memory bad */
558 if(badcookie || !co->name) {
559 /* we didn't get a cookie name or a bad one,
560 this is an illegal line, bail out */
567 /* This line is NOT a HTTP header style line, we do offer support for
568 reading the odd netscape cookies-file format here */
574 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
575 marked with httpOnly after the domain name are not accessible
576 from javascripts, but since curl does not operate at javascript
577 level, we include them anyway. In Firefox's cookie files, these
578 lines are preceded with #HttpOnly_ and then everything is
579 as usual, so we skip 10 characters of the line..
581 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
586 if(lineptr[0]=='#') {
587 /* don't even try the comments */
591 /* strip off the possible end-of-line characters */
592 ptr=strchr(lineptr, '\r');
594 *ptr=0; /* clear it */
595 ptr=strchr(lineptr, '\n');
597 *ptr=0; /* clear it */
599 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
601 /* Now loop through the fields and init the struct we already have
603 for(ptr=firstptr, fields=0; ptr && !badcookie;
604 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
607 if(ptr[0]=='.') /* skip preceding dots */
609 co->domain = strdup(ptr);
614 /* This field got its explanation on the 23rd of May 2001 by
617 flag: A TRUE/FALSE value indicating if all machines within a given
618 domain can access the variable. This value is set automatically by
619 the browser, depending on the value you set for the domain.
621 As far as I can see, it is set to true when the cookie says
622 .domain.com and to false when the domain is complete www.domain.com
624 co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
627 /* It turns out, that sometimes the file format allows the path
628 field to remain not filled in, we try to detect this and work
629 around it! Andrés GarcÃa made us aware of this... */
630 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
631 /* only if the path doesn't look like a boolean option! */
632 co->path = strdup(ptr);
636 co->spath = sanitize_cookie_path(co->path);
638 badcookie = TRUE; /* out of memory bad */
643 /* this doesn't look like a path, make one up! */
644 co->path = strdup("/");
647 co->spath = strdup("/");
650 fields++; /* add a field and fall down to secure */
653 co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
656 co->expires = curlx_strtoofft(ptr, NULL, 10);
659 co->name = strdup(ptr);
664 co->value = strdup(ptr);
671 /* we got a cookie with blank contents, fix it */
672 co->value = strdup("");
679 if(!badcookie && (7 != fields))
680 /* we did not find the sufficient number of fields */
690 if(!c->running && /* read from a file */
691 c->newsession && /* clean session cookies */
692 !co->expires) { /* this is a session cookie since it doesn't expire! */
697 co->livecookie = c->running;
699 /* now, we have parsed the incoming line, we must now check if this
700 superceeds an already existing cookie, which it may if the previous have
701 the same domain and path as this */
706 if(Curl_raw_equal(clist->name, co->name)) {
707 /* the names are identical */
709 if(clist->domain && co->domain) {
710 if(Curl_raw_equal(clist->domain, co->domain))
711 /* The domains are identical */
714 else if(!clist->domain && !co->domain)
718 /* the domains were identical */
720 if(clist->spath && co->spath) {
721 if(Curl_raw_equal(clist->spath, co->spath)) {
727 else if(!clist->spath && !co->spath)
734 if(replace_old && !co->livecookie && clist->livecookie) {
735 /* Both cookies matched fine, except that the already present
736 cookie is "live", which means it was set from a header, while
737 the new one isn't "live" and thus only read from a file. We let
738 live cookies stay alive */
740 /* Free the newcomer and get out of here! */
746 co->next = clist->next; /* get the next-pointer first */
748 /* then free all the old pointers */
759 free(clist->expirestr);
762 free(clist->version);
766 *clist = *co; /* then store all the new data */
768 free(co); /* free the newly alloced memory */
769 co = clist; /* point to the previous struct instead */
771 /* We have replaced a cookie, now skip the rest of the list but
772 make sure the 'lastc' pointer is properly set */
785 /* Only show this when NOT reading the cookies from a file */
786 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
787 "expire %" FORMAT_OFF_T "\n",
788 replace_old?"Replaced":"Added", co->name, co->value,
789 co->domain, co->path, co->expires);
792 /* then make the last item point on this new one */
797 c->numcookies++; /* one more cookie in the jar */
803 /*****************************************************************************
807 * Inits a cookie struct to read data from a local file. This is always
808 * called before any cookies are set. File may be NULL.
810 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
812 ****************************************************************************/
813 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
815 struct CookieInfo *inc,
818 struct CookieInfo *c;
823 /* we didn't get a struct, create one */
824 c = calloc(1, sizeof(struct CookieInfo));
826 return NULL; /* failed to get memory */
827 c->filename = strdup(file?file:"none"); /* copy the name just in case */
830 /* we got an already existing one, use that */
833 c->running = FALSE; /* this is not running, this is init */
835 if(file && strequal(file, "-")) {
839 else if(file && !*file) {
840 /* points to a "" string */
844 fp = file?fopen(file, "r"):NULL;
846 c->newsession = newsession; /* new session? */
852 char *line = malloc(MAX_COOKIE_LINE);
854 while(fgets(line, MAX_COOKIE_LINE, fp)) {
855 if(checkprefix("Set-Cookie:", line)) {
856 /* This is a cookie line, get it! */
864 while(*lineptr && ISBLANK(*lineptr))
867 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
869 free(line); /* free the line buffer */
875 c->running = TRUE; /* now, we're running */
880 /* sort this so that the longest path gets before the shorter path */
881 static int cookie_sort(const void *p1, const void *p2)
883 struct Cookie *c1 = *(struct Cookie **)p1;
884 struct Cookie *c2 = *(struct Cookie **)p2;
887 /* 1 - compare cookie path lengths */
888 l1 = c1->path ? strlen(c1->path) : 0;
889 l2 = c2->path ? strlen(c2->path) : 0;
892 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
894 /* 2 - compare cookie domain lengths */
895 l1 = c1->domain ? strlen(c1->domain) : 0;
896 l2 = c2->domain ? strlen(c2->domain) : 0;
899 return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
901 /* 3 - compare cookie names */
902 if(c1->name && c2->name)
903 return strcmp(c1->name, c2->name);
905 /* sorry, can't be more deterministic */
909 /*****************************************************************************
911 * Curl_cookie_getlist()
913 * For a given host and path, return a linked list of cookies that the
914 * client should send to the server if used now. The secure boolean informs
915 * the cookie if a secure connection is achieved or not.
917 * It shall only return cookies that haven't expired.
919 ****************************************************************************/
921 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
922 const char *host, const char *path,
925 struct Cookie *newco;
927 time_t now = time(NULL);
928 struct Cookie *mainco=NULL;
931 if(!c || !c->cookies)
932 return NULL; /* no cookie struct or no cookies in the struct */
937 /* only process this cookie if it is not expired or had no expire
938 date AND that if the cookie requires we're secure we must only
939 continue if we are! */
940 if((!co->expires || (co->expires > now)) &&
941 (co->secure?secure:TRUE)) {
943 /* now check if the domain is correct */
945 (co->tailmatch && tailmatch(co->domain, host)) ||
946 (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
947 /* the right part of the host matches the domain stuff in the
950 /* now check the left part of the path with the cookies path
952 if(!co->spath || pathmatch(co->spath, path) ) {
954 /* and now, we know this is a match and we should create an
955 entry for the return-linked-list */
957 newco = malloc(sizeof(struct Cookie));
959 /* first, copy the whole source cookie: */
960 memcpy(newco, co, sizeof(struct Cookie));
962 /* then modify our next */
963 newco->next = mainco;
965 /* point the main to us */
972 /* failure, clear up the allocated chain and return NULL */
988 /* Now we need to make sure that if there is a name appearing more than
989 once, the longest specified path version comes first. To make this
990 the swiftest way, we just sort them all based on path length. */
991 struct Cookie **array;
994 /* alloc an array and store all cookie pointers */
995 array = malloc(sizeof(struct Cookie *) * matches);
1001 for(i=0; co; co = co->next)
1004 /* now sort the cookie pointers in path length order */
1005 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
1007 /* remake the linked list order according to the new order */
1009 mainco = array[0]; /* start here */
1010 for(i=0; i<matches-1; i++)
1011 array[i]->next = array[i+1];
1012 array[matches-1]->next = NULL; /* terminate the list */
1014 free(array); /* remove the temporary data again */
1017 return mainco; /* return the new list */
1020 /*****************************************************************************
1022 * Curl_cookie_clearall()
1024 * Clear all existing cookies and reset the counter.
1026 ****************************************************************************/
1027 void Curl_cookie_clearall(struct CookieInfo *cookies)
1030 Curl_cookie_freelist(cookies->cookies, TRUE);
1031 cookies->cookies = NULL;
1032 cookies->numcookies = 0;
1036 /*****************************************************************************
1038 * Curl_cookie_freelist()
1040 * Free a list of cookies previously returned by Curl_cookie_getlist();
1042 * The 'cookiestoo' argument tells this function whether to just free the
1043 * list or actually also free all cookies within the list as well.
1045 ****************************************************************************/
1047 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
1049 struct Cookie *next;
1056 free(co); /* we only free the struct since the "members" are all just
1057 pointed out in the main cookie list! */
1064 /*****************************************************************************
1066 * Curl_cookie_clearsess()
1068 * Free all session cookies in the cookies list.
1070 ****************************************************************************/
1071 void Curl_cookie_clearsess(struct CookieInfo *cookies)
1073 struct Cookie *first, *curr, *next, *prev = NULL;
1075 if(!cookies || !cookies->cookies)
1078 first = curr = prev = cookies->cookies;
1080 for(; curr; curr = next) {
1082 if(!curr->expires) {
1092 cookies->numcookies--;
1098 cookies->cookies = first;
1102 /*****************************************************************************
1104 * Curl_cookie_cleanup()
1106 * Free a "cookie object" previous created with cookie_init().
1108 ****************************************************************************/
1109 void Curl_cookie_cleanup(struct CookieInfo *c)
1112 struct Cookie *next;
1123 free(c); /* free the base struct as well */
1127 /* get_netscape_format()
1129 * Formats a string for Netscape output file, w/o a newline at the end.
1131 * Function returns a char * to a formatted line. Has to be free()d
1133 static char *get_netscape_format(const struct Cookie *co)
1136 "%s" /* httponly preamble */
1137 "%s%s\t" /* domain */
1138 "%s\t" /* tailmatch */
1141 "%" FORMAT_OFF_T "\t" /* expires */
1144 co->httponly?"#HttpOnly_":"",
1145 /* Make sure all domains are prefixed with a dot if they allow
1146 tailmatching. This is Mozilla-style. */
1147 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1148 co->domain?co->domain:"unknown",
1149 co->tailmatch?"TRUE":"FALSE",
1150 co->path?co->path:"/",
1151 co->secure?"TRUE":"FALSE",
1154 co->value?co->value:"");
1160 * Writes all internally known cookies to the specified file. Specify
1161 * "-" as file name to write to stdout.
1163 * The function returns non-zero on write failure.
1165 static int cookie_output(struct CookieInfo *c, const char *dumphere)
1169 bool use_stdout=FALSE;
1171 if((NULL == c) || (0 == c->numcookies))
1172 /* If there are no known cookies, we don't write or even create any
1176 if(strequal("-", dumphere)) {
1182 out = fopen(dumphere, "w");
1184 return 1; /* failure */
1190 fputs("# Netscape HTTP Cookie File\n"
1191 "# http://curl.haxx.se/docs/http-cookies.html\n"
1192 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1197 format_ptr = get_netscape_format(co);
1198 if(format_ptr == NULL) {
1199 fprintf(out, "#\n# Fatal libcurl error\n");
1204 fprintf(out, "%s\n", format_ptr);
1216 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1218 struct curl_slist *list = NULL;
1219 struct curl_slist *beg;
1223 if((data->cookies == NULL) ||
1224 (data->cookies->numcookies == 0))
1227 c = data->cookies->cookies;
1230 /* fill the list with _all_ the cookies we know */
1231 line = get_netscape_format(c);
1233 curl_slist_free_all(list);
1236 beg = Curl_slist_append_nodup(list, line);
1239 curl_slist_free_all(list);
1249 void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1251 if(data->set.str[STRING_COOKIEJAR]) {
1252 if(data->change.cookielist) {
1253 /* If there is a list of cookie files to read, do it first so that
1254 we have all the told files read before we write the new jar.
1255 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1256 Curl_cookie_loadfiles(data);
1259 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1261 /* if we have a destination file for all the cookies to get dumped to */
1262 if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1263 infof(data, "WARNING: failed to save cookies in %s\n",
1264 data->set.str[STRING_COOKIEJAR]);
1267 if(cleanup && data->change.cookielist) {
1268 /* since nothing is written, we can just free the list of cookie file
1270 curl_slist_free_all(data->change.cookielist); /* clean up list */
1271 data->change.cookielist = NULL;
1273 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1276 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1277 Curl_cookie_cleanup(data->cookies);
1279 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1282 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */