1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.
22 ***************************************************************************/
27 RECEIVING COOKIE INFORMATION
28 ============================
30 struct CookieInfo *cookie_init(char *file);
32 Inits a cookie struct to store data in a local file. This is always
33 called before any cookies are set.
35 int cookies_set(struct CookieInfo *cookie, char *cookie_line);
37 The 'cookie_line' parameter is a full "Set-cookie:" line as
38 received from a server.
40 The function need to replace previously stored lines that this new
43 It may remove lines that are expired.
45 It should return an indication of success/error.
48 SENDING COOKIE INFORMATION
49 ==========================
51 struct Cookies *cookie_getlist(struct CookieInfo *cookie,
52 char *host, char *path, bool secure);
54 For a given host and path, return a linked list of cookies that
55 the client should send to the server if used now. The secure
56 boolean informs the cookie if a secure connection is achieved or
59 It shall only return cookies that haven't expired.
62 Example set of cookies:
64 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
65 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
66 domain=.fidelity.com; path=/ftgw; secure
67 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
68 domain=.fidelity.com; path=/; secure
69 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
70 domain=.fidelity.com; path=/; secure
71 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
72 domain=.fidelity.com; path=/; secure
73 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
74 domain=.fidelity.com; path=/; secure
76 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
77 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
83 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
88 #define _MPRINTF_REPLACE /* without this on windows OS we get undefined reference to snprintf */
89 #include <curl/mprintf.h>
98 #include "strtoofft.h"
100 /* The last #include file should be: */
102 #include "memdebug.h"
106 static void freecookie(struct Cookie *co)
126 static bool tailmatch(const char *little, const char *bigone)
128 size_t littlelen = strlen(little);
129 size_t biglen = strlen(bigone);
131 if(littlelen > biglen)
134 return (bool)strequal(little, bigone+biglen-littlelen);
138 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
140 void Curl_cookie_loadfiles(struct SessionHandle *data)
142 struct curl_slist *list = data->change.cookielist;
144 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
146 data->cookies = Curl_cookie_init(data,
149 data->set.cookiesession);
152 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
153 curl_slist_free_all(data->change.cookielist); /* clean up list */
154 data->change.cookielist = NULL; /* don't do this again! */
158 /****************************************************************************
162 * Add a single cookie line to the cookie keeping object.
164 ***************************************************************************/
167 Curl_cookie_add(struct SessionHandle *data,
168 /* The 'data' pointer here may be NULL at times, and thus
169 must only be used very carefully for things that can deal
170 with data being NULL. Such as infof() and similar */
172 struct CookieInfo *c,
173 bool httpheader, /* TRUE if HTTP header-style line */
174 char *lineptr, /* first character of the line */
175 char *domain, /* default domain */
176 char *path) /* full path used when this cookie is set,
177 used to get default path for the cookie
180 struct Cookie *clist;
186 struct Cookie *lastc=NULL;
187 time_t now = time(NULL);
188 bool replace_old = FALSE;
189 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
191 #ifdef CURL_DISABLE_VERBOSE_STRINGS
195 /* First, alloc and init a new struct for it */
196 co = (struct Cookie *)calloc(sizeof(struct Cookie), 1);
198 return NULL; /* bail out if we're this low on memory */
201 /* This line was read off a HTTP-header */
204 what = malloc(MAX_COOKIE_LINE);
210 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
212 while(*lineptr && ISBLANK(*lineptr))
217 /* we have a <what>=<this> pair or a 'secure' word here */
218 sep = strchr(ptr, '=');
219 if(sep && (!semiptr || (semiptr>sep)) ) {
221 * There is a = sign and if there was a semicolon too, which make sure
222 * that the semicolon comes _after_ the equal sign.
225 name[0]=what[0]=0; /* init the buffers */
226 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
227 MAX_COOKIE_LINE_TXT "[^;\r\n]",
229 /* this is a <name>=<what> pair */
233 /* Strip off trailing whitespace from the 'what' */
234 size_t len=strlen(what);
235 while(len && ISBLANK(what[len-1])) {
240 /* Skip leading whitespace from the 'what' */
242 while(*whatptr && ISBLANK(*whatptr)) {
246 if(strequal("path", name)) {
247 co->path=strdup(whatptr);
249 badcookie = TRUE; /* out of memory bad */
253 else if(strequal("domain", name)) {
254 /* note that this name may or may not have a preceeding dot, but
255 we don't care about that, we treat the names the same anyway */
257 const char *domptr=whatptr;
260 /* Count the dots, we need to make sure that there are enough
263 if('.' == whatptr[0])
264 /* don't count the initial dot, assume it */
268 domptr = strchr(domptr, '.');
275 /* The original Netscape cookie spec defined that this domain name
276 MUST have three dots (or two if one of the seven holy TLDs),
277 but it seems that these kinds of cookies are in use "out there"
278 so we cannot be that strict. I've therefore lowered the check
279 to not allow less than two dots. */
282 /* Received and skipped a cookie with a domain using too few
284 badcookie=TRUE; /* mark this as a bad cookie */
285 infof(data, "skipped cookie with illegal dotcount domain: %s\n",
289 /* Now, we make sure that our host is within the given domain,
290 or the given domain is not valid and thus cannot be set. */
292 if('.' == whatptr[0])
293 whatptr++; /* ignore preceeding dot */
295 if(!domain || tailmatch(whatptr, domain)) {
296 const char *tailptr=whatptr;
297 if(tailptr[0] == '.')
299 co->domain=strdup(tailptr); /* don't prefix w/dots
305 co->tailmatch=TRUE; /* we always do that if the domain name was
309 /* we did not get a tailmatch and then the attempted set domain
310 is not a domain to which the current host belongs. Mark as
313 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
318 else if(strequal("version", name)) {
319 co->version=strdup(whatptr);
325 else if(strequal("max-age", name)) {
326 /* Defined in RFC2109:
328 Optional. The Max-Age attribute defines the lifetime of the
329 cookie, in seconds. The delta-seconds value is a decimal non-
330 negative integer. After delta-seconds seconds elapse, the
331 client should discard the cookie. A value of zero means the
332 cookie should be discarded immediately.
335 co->maxage = strdup(whatptr);
341 atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + (long)now;
343 else if(strequal("expires", name)) {
344 co->expirestr=strdup(whatptr);
349 co->expires = curl_getdate(what, &now);
352 co->name = strdup(name);
353 co->value = strdup(whatptr);
354 if(!co->name || !co->value) {
360 else this is the second (or more) name we don't know
364 /* this is an "illegal" <what>=<this> pair */
368 if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
370 if(strequal("secure", what))
373 unsupported keyword without assign! */
377 if(!semiptr || !*semiptr) {
378 /* we already know there are no more cookies */
384 while(ptr && *ptr && ISBLANK(*ptr))
386 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
389 /* There are no more semicolons, but there's a final name=value pair
391 semiptr=strchr(ptr, '\0');
394 if(!badcookie && !co->domain) {
396 /* no domain was given in the header line, set the default */
397 co->domain=strdup(domain);
403 if(!badcookie && !co->path && path) {
404 /* no path was given in the header line, set the default */
405 char *endslash = strrchr(path, '/');
407 size_t pathlen = endslash-path+1; /* include the ending slash */
408 co->path=malloc(pathlen+1); /* one extra for the zero byte */
410 memcpy(co->path, path, pathlen);
411 co->path[pathlen]=0; /* zero terminate */
420 if(badcookie || !co->name) {
421 /* we didn't get a cookie name or a bad one,
422 this is an illegal line, bail out */
429 /* This line is NOT a HTTP header style line, we do offer support for
430 reading the odd netscape cookies-file format here */
435 if(lineptr[0]=='#') {
436 /* don't even try the comments */
440 /* strip off the possible end-of-line characters */
441 ptr=strchr(lineptr, '\r');
443 *ptr=0; /* clear it */
444 ptr=strchr(lineptr, '\n');
446 *ptr=0; /* clear it */
448 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
450 /* Here's a quick check to eliminate normal HTTP-headers from this */
451 if(!firstptr || strchr(firstptr, ':')) {
456 /* Now loop through the fields and init the struct we already have
458 for(ptr=firstptr, fields=0; ptr && !badcookie;
459 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
462 if(ptr[0]=='.') /* skip preceeding dots */
464 co->domain = strdup(ptr);
469 /* This field got its explanation on the 23rd of May 2001 by
472 flag: A TRUE/FALSE value indicating if all machines within a given
473 domain can access the variable. This value is set automatically by
474 the browser, depending on the value you set for the domain.
476 As far as I can see, it is set to true when the cookie says
477 .domain.com and to false when the domain is complete www.domain.com
479 co->tailmatch=(bool)strequal(ptr, "TRUE"); /* store information */
482 /* It turns out, that sometimes the file format allows the path
483 field to remain not filled in, we try to detect this and work
484 around it! Andrés GarcÃa made us aware of this... */
485 if (strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
486 /* only if the path doesn't look like a boolean option! */
487 co->path = strdup(ptr);
492 /* this doesn't look like a path, make one up! */
493 co->path = strdup("/");
496 fields++; /* add a field and fall down to secure */
499 co->secure = (bool)strequal(ptr, "TRUE");
502 co->expires = curlx_strtoofft(ptr, NULL, 10);
505 co->name = strdup(ptr);
510 co->value = strdup(ptr);
517 /* we got a cookie with blank contents, fix it */
518 co->value = strdup("");
525 if(!badcookie && (7 != fields))
526 /* we did not find the sufficient number of fields */
536 if(!c->running && /* read from a file */
537 c->newsession && /* clean session cookies */
538 !co->expires) { /* this is a session cookie since it doesn't expire! */
543 co->livecookie = c->running;
545 /* now, we have parsed the incoming line, we must now check if this
546 superceeds an already existing cookie, which it may if the previous have
547 the same domain and path as this */
552 if(strequal(clist->name, co->name)) {
553 /* the names are identical */
555 if(clist->domain && co->domain) {
556 if(strequal(clist->domain, co->domain))
557 /* The domains are identical */
560 else if(!clist->domain && !co->domain)
564 /* the domains were identical */
566 if(clist->path && co->path) {
567 if(strequal(clist->path, co->path)) {
573 else if(!clist->path && !co->path)
580 if(replace_old && !co->livecookie && clist->livecookie) {
581 /* Both cookies matched fine, except that the already present
582 cookie is "live", which means it was set from a header, while
583 the new one isn't "live" and thus only read from a file. We let
584 live cookies stay alive */
586 /* Free the newcomer and get out of here! */
592 co->next = clist->next; /* get the next-pointer first */
594 /* then free all the old pointers */
604 free(clist->expirestr);
607 free(clist->version);
611 *clist = *co; /* then store all the new data */
613 free(co); /* free the newly alloced memory */
614 co = clist; /* point to the previous struct instead */
616 /* We have replaced a cookie, now skip the rest of the list but
617 make sure the 'lastc' pointer is properly set */
630 /* Only show this when NOT reading the cookies from a file */
631 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, expire %d\n",
632 replace_old?"Replaced":"Added", co->name, co->value,
633 co->domain, co->path, co->expires);
636 /* then make the last item point on this new one */
643 c->numcookies++; /* one more cookie in the jar */
647 /*****************************************************************************
651 * Inits a cookie struct to read data from a local file. This is always
652 * called before any cookies are set. File may be NULL.
654 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
656 ****************************************************************************/
657 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
659 struct CookieInfo *inc,
662 struct CookieInfo *c;
667 /* we didn't get a struct, create one */
668 c = (struct CookieInfo *)calloc(1, sizeof(struct CookieInfo));
670 return NULL; /* failed to get memory */
671 c->filename = strdup(file?file:"none"); /* copy the name just in case */
674 /* we got an already existing one, use that */
677 c->running = FALSE; /* this is not running, this is init */
679 if(file && strequal(file, "-")) {
683 else if(file && !*file) {
684 /* points to a "" string */
688 fp = file?fopen(file, "r"):NULL;
690 c->newsession = newsession; /* new session? */
696 char *line = (char *)malloc(MAX_COOKIE_LINE);
698 while(fgets(line, MAX_COOKIE_LINE, fp)) {
699 if(checkprefix("Set-Cookie:", line)) {
700 /* This is a cookie line, get it! */
708 while(*lineptr && ISBLANK(*lineptr))
711 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
713 free(line); /* free the line buffer */
719 c->running = TRUE; /* now, we're running */
724 /*****************************************************************************
726 * Curl_cookie_getlist()
728 * For a given host and path, return a linked list of cookies that the
729 * client should send to the server if used now. The secure boolean informs
730 * the cookie if a secure connection is achieved or not.
732 * It shall only return cookies that haven't expired.
734 ****************************************************************************/
736 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
737 char *host, char *path, bool secure)
739 struct Cookie *newco;
741 time_t now = time(NULL);
742 struct Cookie *mainco=NULL;
744 if(!c || !c->cookies)
745 return NULL; /* no cookie struct or no cookies in the struct */
750 /* only process this cookie if it is not expired or had no expire
751 date AND that if the cookie requires we're secure we must only
752 continue if we are! */
753 if( (co->expires<=0 || (co->expires> now)) &&
754 (co->secure?secure:TRUE) ) {
756 /* now check if the domain is correct */
758 (co->tailmatch && tailmatch(co->domain, host)) ||
759 (!co->tailmatch && strequal(host, co->domain)) ) {
760 /* the right part of the host matches the domain stuff in the
763 /* now check the left part of the path with the cookies path
766 /* not using checkprefix() because matching should be
768 !strncmp(co->path, path, strlen(co->path)) ) {
770 /* and now, we know this is a match and we should create an
771 entry for the return-linked-list */
773 newco = (struct Cookie *)malloc(sizeof(struct Cookie));
775 /* first, copy the whole source cookie: */
776 memcpy(newco, co, sizeof(struct Cookie));
778 /* then modify our next */
779 newco->next = mainco;
781 /* point the main to us */
785 /* failure, clear up the allocated chain and return NULL */
800 return mainco; /* return the new list */
803 /*****************************************************************************
805 * Curl_cookie_clearall()
807 * Clear all existing cookies and reset the counter.
809 ****************************************************************************/
810 void Curl_cookie_clearall(struct CookieInfo *cookies)
813 Curl_cookie_freelist(cookies->cookies);
814 cookies->cookies = NULL;
815 cookies->numcookies = 0;
819 /*****************************************************************************
821 * Curl_cookie_freelist()
823 * Free a list of cookies previously returned by Curl_cookie_getlist();
825 ****************************************************************************/
827 void Curl_cookie_freelist(struct Cookie *co)
833 free(co); /* we only free the struct since the "members" are all
841 /*****************************************************************************
843 * Curl_cookie_clearsess()
845 * Free all session cookies in the cookies list.
847 ****************************************************************************/
848 void Curl_cookie_clearsess(struct CookieInfo *cookies)
850 struct Cookie *first, *curr, *next, *prev = NULL;
852 if(!cookies->cookies)
855 first = curr = prev = cookies->cookies;
857 for(; curr; curr = next) {
869 cookies->numcookies--;
875 cookies->cookies = first;
879 /*****************************************************************************
881 * Curl_cookie_cleanup()
883 * Free a "cookie object" previous created with cookie_init().
885 ****************************************************************************/
886 void Curl_cookie_cleanup(struct CookieInfo *c)
900 free(c); /* free the base struct as well */
904 /* get_netscape_format()
906 * Formats a string for Netscape output file, w/o a newline at the end.
908 * Function returns a char * to a formatted line. Has to be free()d
910 static char *get_netscape_format(const struct Cookie *co)
913 "%s%s\t" /* domain */
914 "%s\t" /* tailmatch */
917 "%" FORMAT_OFF_T "\t" /* expires */
920 /* Make sure all domains are prefixed with a dot if they allow
921 tailmatching. This is Mozilla-style. */
922 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
923 co->domain?co->domain:"unknown",
924 co->tailmatch?"TRUE":"FALSE",
925 co->path?co->path:"/",
926 co->secure?"TRUE":"FALSE",
929 co->value?co->value:"");
933 * Curl_cookie_output()
935 * Writes all internally known cookies to the specified file. Specify
936 * "-" as file name to write to stdout.
938 * The function returns non-zero on write failure.
940 int Curl_cookie_output(struct CookieInfo *c, char *dumphere)
944 bool use_stdout=FALSE;
946 if((NULL == c) || (0 == c->numcookies))
947 /* If there are no known cookies, we don't write or even create any
951 if(strequal("-", dumphere)) {
957 out = fopen(dumphere, "w");
959 return 1; /* failure */
965 fputs("# Netscape HTTP Cookie File\n"
966 "# http://curlm.haxx.se/rfc/cookie_spec.html\n"
967 "# This file was generated by libcurl! Edit at your own risk.\n\n",
972 format_ptr = get_netscape_format(co);
973 if (format_ptr == NULL) {
974 fprintf(out, "#\n# Fatal libcurl error\n");
978 fprintf(out, "%s\n", format_ptr);
990 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
992 struct curl_slist *list = NULL;
993 struct curl_slist *beg;
997 if ((data->cookies == NULL) ||
998 (data->cookies->numcookies == 0))
1001 c = data->cookies->cookies;
1005 /* fill the list with _all_ the cookies we know */
1006 line = get_netscape_format(c);
1008 curl_slist_free_all(beg);
1011 list = curl_slist_append(list, line);
1014 curl_slist_free_all(beg);
1017 else if (beg == NULL) {
1026 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */