1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2010, 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
82 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
87 #define _MPRINTF_REPLACE /* without this on windows OS we get undefined reference to snprintf */
88 #include <curl/mprintf.h>
95 #include "curl_memory.h"
97 #include "strtoofft.h"
99 #include "curl_memrchr.h"
101 /* The last #include file should be: */
102 #include "memdebug.h"
105 static void freecookie(struct Cookie *co)
125 static bool tailmatch(const char *little, const char *bigone)
127 size_t littlelen = strlen(little);
128 size_t biglen = strlen(bigone);
130 if(littlelen > biglen)
133 return (bool)Curl_raw_equal(little, bigone+biglen-littlelen);
137 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
139 void Curl_cookie_loadfiles(struct SessionHandle *data)
141 struct curl_slist *list = data->change.cookielist;
143 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
145 data->cookies = Curl_cookie_init(data,
148 data->set.cookiesession);
151 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
152 curl_slist_free_all(data->change.cookielist); /* clean up list */
153 data->change.cookielist = NULL; /* don't do this again! */
158 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
159 * that will be freed before the allocated string is stored there.
161 * It is meant to easily replace strdup()
163 static void strstore(char **str, const char *newstr)
167 *str = strdup(newstr);
171 /****************************************************************************
175 * Add a single cookie line to the cookie keeping object.
177 ***************************************************************************/
180 Curl_cookie_add(struct SessionHandle *data,
181 /* The 'data' pointer here may be NULL at times, and thus
182 must only be used very carefully for things that can deal
183 with data being NULL. Such as infof() and similar */
185 struct CookieInfo *c,
186 bool httpheader, /* TRUE if HTTP header-style line */
187 char *lineptr, /* first character of the line */
188 const char *domain, /* default domain */
189 const char *path) /* full path used when this cookie is set,
190 used to get default path for the cookie
193 struct Cookie *clist;
196 struct Cookie *lastc=NULL;
197 time_t now = time(NULL);
198 bool replace_old = FALSE;
199 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
201 #ifdef CURL_DISABLE_VERBOSE_STRINGS
205 /* First, alloc and init a new struct for it */
206 co = calloc(1, sizeof(struct Cookie));
208 return NULL; /* bail out if we're this low on memory */
211 /* This line was read off a HTTP-header */
217 what = malloc(MAX_COOKIE_LINE);
223 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
225 while(*lineptr && ISBLANK(*lineptr))
230 /* we have a <what>=<this> pair or a 'secure' word here */
231 sep = strchr(ptr, '=');
232 if(sep && (!semiptr || (semiptr>sep)) ) {
234 * There is a = sign and if there was a semicolon too, which make sure
235 * that the semicolon comes _after_ the equal sign.
238 name[0]=what[0]=0; /* init the buffers */
239 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
240 MAX_COOKIE_LINE_TXT "[^;\r\n]",
242 /* this is a <name>=<what> pair. We use strstore() below to properly
243 deal with received cookie headers that have the same string
244 property set more than once, and then we use the last one. */
248 /* Strip off trailing whitespace from the 'what' */
249 size_t len=strlen(what);
250 while(len && ISBLANK(what[len-1])) {
255 /* Skip leading whitespace from the 'what' */
257 while(*whatptr && ISBLANK(*whatptr)) {
261 if(Curl_raw_equal("path", name)) {
262 strstore(&co->path, whatptr);
264 badcookie = TRUE; /* out of memory bad */
268 else if(Curl_raw_equal("domain", name)) {
269 /* note that this name may or may not have a preceeding dot, but
270 we don't care about that, we treat the names the same anyway */
272 const char *domptr=whatptr;
275 /* Count the dots, we need to make sure that there are enough
278 if('.' == whatptr[0])
279 /* don't count the initial dot, assume it */
283 domptr = strchr(domptr, '.');
290 /* The original Netscape cookie spec defined that this domain name
291 MUST have three dots (or two if one of the seven holy TLDs),
292 but it seems that these kinds of cookies are in use "out there"
293 so we cannot be that strict. I've therefore lowered the check
294 to not allow less than two dots. */
297 /* Received and skipped a cookie with a domain using too few
299 badcookie=TRUE; /* mark this as a bad cookie */
300 infof(data, "skipped cookie with illegal dotcount domain: %s\n",
304 /* Now, we make sure that our host is within the given domain,
305 or the given domain is not valid and thus cannot be set. */
307 if('.' == whatptr[0])
308 whatptr++; /* ignore preceeding dot */
310 if(!domain || tailmatch(whatptr, domain)) {
311 const char *tailptr=whatptr;
312 if(tailptr[0] == '.')
314 strstore(&co->domain, tailptr); /* don't prefix w/dots
320 co->tailmatch=TRUE; /* we always do that if the domain name was
324 /* we did not get a tailmatch and then the attempted set domain
325 is not a domain to which the current host belongs. Mark as
328 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
333 else if(Curl_raw_equal("version", name)) {
334 strstore(&co->version, whatptr);
340 else if(Curl_raw_equal("max-age", name)) {
341 /* Defined in RFC2109:
343 Optional. The Max-Age attribute defines the lifetime of the
344 cookie, in seconds. The delta-seconds value is a decimal non-
345 negative integer. After delta-seconds seconds elapse, the
346 client should discard the cookie. A value of zero means the
347 cookie should be discarded immediately.
350 strstore(&co->maxage, whatptr);
356 strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
359 else if(Curl_raw_equal("expires", name)) {
360 strstore(&co->expirestr, whatptr);
365 /* Note that if the date couldn't get parsed for whatever reason,
366 the cookie will be treated as a session cookie */
367 co->expires = curl_getdate(what, &now);
369 /* Session cookies have expires set to 0 so if we get that back
370 from the date parser let's add a second to make it a
371 non-session cookie */
372 if (co->expires == 0)
374 else if( co->expires < 0 )
378 co->name = strdup(name);
379 co->value = strdup(whatptr);
380 if(!co->name || !co->value) {
386 else this is the second (or more) name we don't know
390 /* this is an "illegal" <what>=<this> pair */
394 if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
396 if(Curl_raw_equal("secure", what)) {
399 else if (Curl_raw_equal("httponly", what)) {
403 unsupported keyword without assign! */
407 if(!semiptr || !*semiptr) {
408 /* we already know there are no more cookies */
414 while(*ptr && ISBLANK(*ptr))
416 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
419 /* There are no more semicolons, but there's a final name=value pair
421 semiptr=strchr(ptr, '\0');
424 if(!badcookie && !co->domain) {
426 /* no domain was given in the header line, set the default */
427 co->domain=strdup(domain);
433 if(!badcookie && !co->path && path) {
434 /* No path was given in the header line, set the default.
435 Note that the passed-in path to this function MAY have a '?' and
436 following part that MUST not be stored as part of the path. */
437 char *queryp = strchr(path, '?');
439 /* queryp is where the interesting part of the path ends, so now we
440 want to the find the last */
443 endslash = strrchr(path, '/');
445 endslash = memrchr(path, '/', (size_t)(queryp - path));
447 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
448 co->path=malloc(pathlen+1); /* one extra for the zero byte */
450 memcpy(co->path, path, pathlen);
451 co->path[pathlen]=0; /* zero terminate */
460 if(badcookie || !co->name) {
461 /* we didn't get a cookie name or a bad one,
462 this is an illegal line, bail out */
469 /* This line is NOT a HTTP header style line, we do offer support for
470 reading the odd netscape cookies-file format here */
476 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
477 marked with httpOnly after the domain name are not accessible
478 from javascripts, but since curl does not operate at javascript
479 level, we include them anyway. In Firefox's cookie files, these
480 lines are preceeded with #HttpOnly_ and then everything is
481 as usual, so we skip 10 characters of the line..
483 if (strncmp(lineptr, "#HttpOnly_", 10) == 0) {
488 if(lineptr[0]=='#') {
489 /* don't even try the comments */
493 /* strip off the possible end-of-line characters */
494 ptr=strchr(lineptr, '\r');
496 *ptr=0; /* clear it */
497 ptr=strchr(lineptr, '\n');
499 *ptr=0; /* clear it */
501 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
503 /* Here's a quick check to eliminate normal HTTP-headers from this */
504 if(!firstptr || strchr(firstptr, ':')) {
509 /* Now loop through the fields and init the struct we already have
511 for(ptr=firstptr, fields=0; ptr && !badcookie;
512 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
515 if(ptr[0]=='.') /* skip preceeding dots */
517 co->domain = strdup(ptr);
522 /* This field got its explanation on the 23rd of May 2001 by
525 flag: A TRUE/FALSE value indicating if all machines within a given
526 domain can access the variable. This value is set automatically by
527 the browser, depending on the value you set for the domain.
529 As far as I can see, it is set to true when the cookie says
530 .domain.com and to false when the domain is complete www.domain.com
532 co->tailmatch=(bool)Curl_raw_equal(ptr, "TRUE"); /* store information */
535 /* It turns out, that sometimes the file format allows the path
536 field to remain not filled in, we try to detect this and work
537 around it! Andrés GarcÃa made us aware of this... */
538 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
539 /* only if the path doesn't look like a boolean option! */
540 co->path = strdup(ptr);
545 /* this doesn't look like a path, make one up! */
546 co->path = strdup("/");
549 fields++; /* add a field and fall down to secure */
552 co->secure = (bool)Curl_raw_equal(ptr, "TRUE");
555 co->expires = curlx_strtoofft(ptr, NULL, 10);
558 co->name = strdup(ptr);
563 co->value = strdup(ptr);
570 /* we got a cookie with blank contents, fix it */
571 co->value = strdup("");
578 if(!badcookie && (7 != fields))
579 /* we did not find the sufficient number of fields */
589 if(!c->running && /* read from a file */
590 c->newsession && /* clean session cookies */
591 !co->expires) { /* this is a session cookie since it doesn't expire! */
596 co->livecookie = c->running;
598 /* now, we have parsed the incoming line, we must now check if this
599 superceeds an already existing cookie, which it may if the previous have
600 the same domain and path as this */
605 if(Curl_raw_equal(clist->name, co->name)) {
606 /* the names are identical */
608 if(clist->domain && co->domain) {
609 if(Curl_raw_equal(clist->domain, co->domain))
610 /* The domains are identical */
613 else if(!clist->domain && !co->domain)
617 /* the domains were identical */
619 if(clist->path && co->path) {
620 if(Curl_raw_equal(clist->path, co->path)) {
626 else if(!clist->path && !co->path)
633 if(replace_old && !co->livecookie && clist->livecookie) {
634 /* Both cookies matched fine, except that the already present
635 cookie is "live", which means it was set from a header, while
636 the new one isn't "live" and thus only read from a file. We let
637 live cookies stay alive */
639 /* Free the newcomer and get out of here! */
645 co->next = clist->next; /* get the next-pointer first */
647 /* then free all the old pointers */
656 free(clist->expirestr);
659 free(clist->version);
663 *clist = *co; /* then store all the new data */
665 free(co); /* free the newly alloced memory */
666 co = clist; /* point to the previous struct instead */
668 /* We have replaced a cookie, now skip the rest of the list but
669 make sure the 'lastc' pointer is properly set */
682 /* Only show this when NOT reading the cookies from a file */
683 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
684 "expire %" FORMAT_OFF_T "\n",
685 replace_old?"Replaced":"Added", co->name, co->value,
686 co->domain, co->path, co->expires);
689 /* then make the last item point on this new one */
696 c->numcookies++; /* one more cookie in the jar */
700 /*****************************************************************************
704 * Inits a cookie struct to read data from a local file. This is always
705 * called before any cookies are set. File may be NULL.
707 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
709 ****************************************************************************/
710 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
712 struct CookieInfo *inc,
715 struct CookieInfo *c;
720 /* we didn't get a struct, create one */
721 c = calloc(1, sizeof(struct CookieInfo));
723 return NULL; /* failed to get memory */
724 c->filename = strdup(file?file:"none"); /* copy the name just in case */
727 /* we got an already existing one, use that */
730 c->running = FALSE; /* this is not running, this is init */
732 if(file && strequal(file, "-")) {
736 else if(file && !*file) {
737 /* points to a "" string */
741 fp = file?fopen(file, "r"):NULL;
743 c->newsession = newsession; /* new session? */
749 char *line = malloc(MAX_COOKIE_LINE);
751 while(fgets(line, MAX_COOKIE_LINE, fp)) {
752 if(checkprefix("Set-Cookie:", line)) {
753 /* This is a cookie line, get it! */
761 while(*lineptr && ISBLANK(*lineptr))
764 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
766 free(line); /* free the line buffer */
772 c->running = TRUE; /* now, we're running */
777 /* sort this so that the longest path gets before the shorter path */
778 static int cookie_sort(const void *p1, const void *p2)
780 struct Cookie *c1 = *(struct Cookie **)p1;
781 struct Cookie *c2 = *(struct Cookie **)p2;
783 size_t l1 = c1->path?strlen(c1->path):0;
784 size_t l2 = c2->path?strlen(c2->path):0;
786 return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
789 /*****************************************************************************
791 * Curl_cookie_getlist()
793 * For a given host and path, return a linked list of cookies that the
794 * client should send to the server if used now. The secure boolean informs
795 * the cookie if a secure connection is achieved or not.
797 * It shall only return cookies that haven't expired.
799 ****************************************************************************/
801 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
802 const char *host, const char *path,
805 struct Cookie *newco;
807 time_t now = time(NULL);
808 struct Cookie *mainco=NULL;
811 if(!c || !c->cookies)
812 return NULL; /* no cookie struct or no cookies in the struct */
817 /* only process this cookie if it is not expired or had no expire
818 date AND that if the cookie requires we're secure we must only
819 continue if we are! */
820 if( (!co->expires || (co->expires > now)) &&
821 (co->secure?secure:TRUE) ) {
823 /* now check if the domain is correct */
825 (co->tailmatch && tailmatch(co->domain, host)) ||
826 (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
827 /* the right part of the host matches the domain stuff in the
830 /* now check the left part of the path with the cookies path
833 /* not using checkprefix() because matching should be
835 !strncmp(co->path, path, strlen(co->path)) ) {
837 /* and now, we know this is a match and we should create an
838 entry for the return-linked-list */
840 newco = malloc(sizeof(struct Cookie));
842 /* first, copy the whole source cookie: */
843 memcpy(newco, co, sizeof(struct Cookie));
845 /* then modify our next */
846 newco->next = mainco;
848 /* point the main to us */
855 /* failure, clear up the allocated chain and return NULL */
871 /* Now we need to make sure that if there is a name appearing more than
872 once, the longest specified path version comes first. To make this
873 the swiftest way, we just sort them all based on path length. */
874 struct Cookie **array;
877 /* alloc an array and store all cookie pointers */
878 array = (struct Cookie **)malloc(sizeof(struct Cookie *) * matches);
884 for(i=0; co; co = co->next)
887 /* now sort the cookie pointers in path lenth order */
888 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
890 /* remake the linked list order according to the new order */
892 mainco = array[0]; /* start here */
893 for(i=0; i<matches-1; i++)
894 array[i]->next = array[i+1];
895 array[matches-1]->next = NULL; /* terminate the list */
897 free(array); /* remove the temporary data again */
900 return mainco; /* return the new list */
903 /*****************************************************************************
905 * Curl_cookie_clearall()
907 * Clear all existing cookies and reset the counter.
909 ****************************************************************************/
910 void Curl_cookie_clearall(struct CookieInfo *cookies)
913 Curl_cookie_freelist(cookies->cookies, TRUE);
914 cookies->cookies = NULL;
915 cookies->numcookies = 0;
919 /*****************************************************************************
921 * Curl_cookie_freelist()
923 * Free a list of cookies previously returned by Curl_cookie_getlist();
925 * The 'cookiestoo' argument tells this function whether to just free the
926 * list or actually also free all cookies within the list as well.
928 ****************************************************************************/
930 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
939 free(co); /* we only free the struct since the "members" are all just
940 pointed out in the main cookie list! */
947 /*****************************************************************************
949 * Curl_cookie_clearsess()
951 * Free all session cookies in the cookies list.
953 ****************************************************************************/
954 void Curl_cookie_clearsess(struct CookieInfo *cookies)
956 struct Cookie *first, *curr, *next, *prev = NULL;
958 if(!cookies || !cookies->cookies)
961 first = curr = prev = cookies->cookies;
963 for(; curr; curr = next) {
975 cookies->numcookies--;
981 cookies->cookies = first;
985 /*****************************************************************************
987 * Curl_cookie_cleanup()
989 * Free a "cookie object" previous created with cookie_init().
991 ****************************************************************************/
992 void Curl_cookie_cleanup(struct CookieInfo *c)
1006 free(c); /* free the base struct as well */
1010 /* get_netscape_format()
1012 * Formats a string for Netscape output file, w/o a newline at the end.
1014 * Function returns a char * to a formatted line. Has to be free()d
1016 static char *get_netscape_format(const struct Cookie *co)
1019 "%s" /* httponly preamble */
1020 "%s%s\t" /* domain */
1021 "%s\t" /* tailmatch */
1024 "%" FORMAT_OFF_T "\t" /* expires */
1027 co->httponly?"#HttpOnly_":"",
1028 /* Make sure all domains are prefixed with a dot if they allow
1029 tailmatching. This is Mozilla-style. */
1030 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1031 co->domain?co->domain:"unknown",
1032 co->tailmatch?"TRUE":"FALSE",
1033 co->path?co->path:"/",
1034 co->secure?"TRUE":"FALSE",
1037 co->value?co->value:"");
1041 * Curl_cookie_output()
1043 * Writes all internally known cookies to the specified file. Specify
1044 * "-" as file name to write to stdout.
1046 * The function returns non-zero on write failure.
1048 int Curl_cookie_output(struct CookieInfo *c, const char *dumphere)
1052 bool use_stdout=FALSE;
1054 if((NULL == c) || (0 == c->numcookies))
1055 /* If there are no known cookies, we don't write or even create any
1059 if(strequal("-", dumphere)) {
1065 out = fopen(dumphere, "w");
1067 return 1; /* failure */
1073 fputs("# Netscape HTTP Cookie File\n"
1074 "# http://curl.haxx.se/rfc/cookie_spec.html\n"
1075 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1080 format_ptr = get_netscape_format(co);
1081 if(format_ptr == NULL) {
1082 fprintf(out, "#\n# Fatal libcurl error\n");
1087 fprintf(out, "%s\n", format_ptr);
1099 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1101 struct curl_slist *list = NULL;
1102 struct curl_slist *beg;
1106 if((data->cookies == NULL) ||
1107 (data->cookies->numcookies == 0))
1110 c = data->cookies->cookies;
1114 /* fill the list with _all_ the cookies we know */
1115 line = get_netscape_format(c);
1117 curl_slist_free_all(beg);
1120 list = curl_slist_append(list, line);
1123 curl_slist_free_all(beg);
1126 else if(beg == NULL) {
1135 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */