1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, 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 * $Id: cookie.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
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 const char *domain
, /* default domain */
176 const char *path
) /* full path used when this cookie is set,
177 used to get default path for the cookie
180 struct Cookie
*clist
;
183 struct Cookie
*lastc
=NULL
;
184 time_t now
= time(NULL
);
185 bool replace_old
= FALSE
;
186 bool badcookie
= FALSE
; /* cookies are good by default. mmmmm yummy */
188 #ifdef CURL_DISABLE_VERBOSE_STRINGS
192 /* First, alloc and init a new struct for it */
193 co
= (struct Cookie
*)calloc(sizeof(struct Cookie
), 1);
195 return NULL
; /* bail out if we're this low on memory */
198 /* 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 else if (strequal("httponly", what
)) {
377 unsupported keyword without assign! */
381 if(!semiptr
|| !*semiptr
) {
382 /* we already know there are no more cookies */
388 while(ptr
&& *ptr
&& ISBLANK(*ptr
))
390 semiptr
=strchr(ptr
, ';'); /* now, find the next semicolon */
393 /* There are no more semicolons, but there's a final name=value pair
395 semiptr
=strchr(ptr
, '\0');
398 if(!badcookie
&& !co
->domain
) {
400 /* no domain was given in the header line, set the default */
401 co
->domain
=strdup(domain
);
407 if(!badcookie
&& !co
->path
&& path
) {
408 /* no path was given in the header line, set the default */
409 char *endslash
= strrchr(path
, '/');
411 size_t pathlen
= endslash
-path
+1; /* include the ending slash */
412 co
->path
=malloc(pathlen
+1); /* one extra for the zero byte */
414 memcpy(co
->path
, path
, pathlen
);
415 co
->path
[pathlen
]=0; /* zero terminate */
424 if(badcookie
|| !co
->name
) {
425 /* we didn't get a cookie name or a bad one,
426 this is an illegal line, bail out */
433 /* This line is NOT a HTTP header style line, we do offer support for
434 reading the odd netscape cookies-file format here */
440 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
441 marked with httpOnly after the domain name are not accessible
442 from javascripts, but since curl does not operate at javascript
443 level, we include them anyway. In Firefox's cookie files, these
444 lines are preceeded with #HttpOnly_ and then everything is
445 as usual, so we skip 10 characters of the line..
447 if (strncmp(lineptr
, "#HttpOnly_", 10) == 0) {
453 if(lineptr
[0]=='#') {
454 /* don't even try the comments */
458 /* strip off the possible end-of-line characters */
459 ptr
=strchr(lineptr
, '\r');
461 *ptr
=0; /* clear it */
462 ptr
=strchr(lineptr
, '\n');
464 *ptr
=0; /* clear it */
466 firstptr
=strtok_r(lineptr
, "\t", &tok_buf
); /* tokenize it on the TAB */
468 /* Here's a quick check to eliminate normal HTTP-headers from this */
469 if(!firstptr
|| strchr(firstptr
, ':')) {
474 /* Now loop through the fields and init the struct we already have
476 for(ptr
=firstptr
, fields
=0; ptr
&& !badcookie
;
477 ptr
=strtok_r(NULL
, "\t", &tok_buf
), fields
++) {
480 if(ptr
[0]=='.') /* skip preceeding dots */
482 co
->domain
= strdup(ptr
);
487 /* This field got its explanation on the 23rd of May 2001 by
490 flag: A TRUE/FALSE value indicating if all machines within a given
491 domain can access the variable. This value is set automatically by
492 the browser, depending on the value you set for the domain.
494 As far as I can see, it is set to true when the cookie says
495 .domain.com and to false when the domain is complete www.domain.com
497 co
->tailmatch
=(bool)strequal(ptr
, "TRUE"); /* store information */
500 /* It turns out, that sometimes the file format allows the path
501 field to remain not filled in, we try to detect this and work
502 around it! Andrés GarcÃa made us aware of this... */
503 if(strcmp("TRUE", ptr
) && strcmp("FALSE", ptr
)) {
504 /* only if the path doesn't look like a boolean option! */
505 co
->path
= strdup(ptr
);
510 /* this doesn't look like a path, make one up! */
511 co
->path
= strdup("/");
514 fields
++; /* add a field and fall down to secure */
517 co
->secure
= (bool)strequal(ptr
, "TRUE");
520 co
->expires
= curlx_strtoofft(ptr
, NULL
, 10);
523 co
->name
= strdup(ptr
);
528 co
->value
= strdup(ptr
);
535 /* we got a cookie with blank contents, fix it */
536 co
->value
= strdup("");
543 if(!badcookie
&& (7 != fields
))
544 /* we did not find the sufficient number of fields */
554 if(!c
->running
&& /* read from a file */
555 c
->newsession
&& /* clean session cookies */
556 !co
->expires
) { /* this is a session cookie since it doesn't expire! */
561 co
->livecookie
= c
->running
;
563 /* now, we have parsed the incoming line, we must now check if this
564 superceeds an already existing cookie, which it may if the previous have
565 the same domain and path as this */
570 if(strequal(clist
->name
, co
->name
)) {
571 /* the names are identical */
573 if(clist
->domain
&& co
->domain
) {
574 if(strequal(clist
->domain
, co
->domain
))
575 /* The domains are identical */
578 else if(!clist
->domain
&& !co
->domain
)
582 /* the domains were identical */
584 if(clist
->path
&& co
->path
) {
585 if(strequal(clist
->path
, co
->path
)) {
591 else if(!clist
->path
&& !co
->path
)
598 if(replace_old
&& !co
->livecookie
&& clist
->livecookie
) {
599 /* Both cookies matched fine, except that the already present
600 cookie is "live", which means it was set from a header, while
601 the new one isn't "live" and thus only read from a file. We let
602 live cookies stay alive */
604 /* Free the newcomer and get out of here! */
610 co
->next
= clist
->next
; /* get the next-pointer first */
612 /* then free all the old pointers */
622 free(clist
->expirestr
);
625 free(clist
->version
);
629 *clist
= *co
; /* then store all the new data */
631 free(co
); /* free the newly alloced memory */
632 co
= clist
; /* point to the previous struct instead */
634 /* We have replaced a cookie, now skip the rest of the list but
635 make sure the 'lastc' pointer is properly set */
648 /* Only show this when NOT reading the cookies from a file */
649 infof(data
, "%s cookie %s=\"%s\" for domain %s, path %s, expire %d\n",
650 replace_old
?"Replaced":"Added", co
->name
, co
->value
,
651 co
->domain
, co
->path
, co
->expires
);
654 /* then make the last item point on this new one */
661 c
->numcookies
++; /* one more cookie in the jar */
665 /*****************************************************************************
669 * Inits a cookie struct to read data from a local file. This is always
670 * called before any cookies are set. File may be NULL.
672 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
674 ****************************************************************************/
675 struct CookieInfo
*Curl_cookie_init(struct SessionHandle
*data
,
677 struct CookieInfo
*inc
,
680 struct CookieInfo
*c
;
685 /* we didn't get a struct, create one */
686 c
= (struct CookieInfo
*)calloc(1, sizeof(struct CookieInfo
));
688 return NULL
; /* failed to get memory */
689 c
->filename
= strdup(file
?file
:"none"); /* copy the name just in case */
692 /* we got an already existing one, use that */
695 c
->running
= FALSE
; /* this is not running, this is init */
697 if(file
&& strequal(file
, "-")) {
701 else if(file
&& !*file
) {
702 /* points to a "" string */
706 fp
= file
?fopen(file
, "r"):NULL
;
708 c
->newsession
= newsession
; /* new session? */
714 char *line
= (char *)malloc(MAX_COOKIE_LINE
);
716 while(fgets(line
, MAX_COOKIE_LINE
, fp
)) {
717 if(checkprefix("Set-Cookie:", line
)) {
718 /* This is a cookie line, get it! */
726 while(*lineptr
&& ISBLANK(*lineptr
))
729 Curl_cookie_add(data
, c
, headerline
, lineptr
, NULL
, NULL
);
731 free(line
); /* free the line buffer */
737 c
->running
= TRUE
; /* now, we're running */
742 /*****************************************************************************
744 * Curl_cookie_getlist()
746 * For a given host and path, return a linked list of cookies that the
747 * client should send to the server if used now. The secure boolean informs
748 * the cookie if a secure connection is achieved or not.
750 * It shall only return cookies that haven't expired.
752 ****************************************************************************/
754 struct Cookie
*Curl_cookie_getlist(struct CookieInfo
*c
,
755 const char *host
, const char *path
,
758 struct Cookie
*newco
;
760 time_t now
= time(NULL
);
761 struct Cookie
*mainco
=NULL
;
763 if(!c
|| !c
->cookies
)
764 return NULL
; /* no cookie struct or no cookies in the struct */
769 /* only process this cookie if it is not expired or had no expire
770 date AND that if the cookie requires we're secure we must only
771 continue if we are! */
772 if( (co
->expires
<=0 || (co
->expires
> now
)) &&
773 (co
->secure
?secure
:TRUE
) ) {
775 /* now check if the domain is correct */
777 (co
->tailmatch
&& tailmatch(co
->domain
, host
)) ||
778 (!co
->tailmatch
&& strequal(host
, co
->domain
)) ) {
779 /* the right part of the host matches the domain stuff in the
782 /* now check the left part of the path with the cookies path
785 /* not using checkprefix() because matching should be
787 !strncmp(co
->path
, path
, strlen(co
->path
)) ) {
789 /* and now, we know this is a match and we should create an
790 entry for the return-linked-list */
792 newco
= (struct Cookie
*)malloc(sizeof(struct Cookie
));
794 /* first, copy the whole source cookie: */
795 memcpy(newco
, co
, sizeof(struct Cookie
));
797 /* then modify our next */
798 newco
->next
= mainco
;
800 /* point the main to us */
804 /* failure, clear up the allocated chain and return NULL */
819 return mainco
; /* return the new list */
822 /*****************************************************************************
824 * Curl_cookie_clearall()
826 * Clear all existing cookies and reset the counter.
828 ****************************************************************************/
829 void Curl_cookie_clearall(struct CookieInfo
*cookies
)
832 Curl_cookie_freelist(cookies
->cookies
, TRUE
);
833 cookies
->cookies
= NULL
;
834 cookies
->numcookies
= 0;
838 /*****************************************************************************
840 * Curl_cookie_freelist()
842 * Free a list of cookies previously returned by Curl_cookie_getlist();
844 * The 'cookiestoo' argument tells this function whether to just free the
845 * list or actually also free all cookies within the list as well.
847 ****************************************************************************/
849 void Curl_cookie_freelist(struct Cookie
*co
, bool cookiestoo
)
858 free(co
); /* we only free the struct since the "members" are all just
859 pointed out in the main cookie list! */
866 /*****************************************************************************
868 * Curl_cookie_clearsess()
870 * Free all session cookies in the cookies list.
872 ****************************************************************************/
873 void Curl_cookie_clearsess(struct CookieInfo
*cookies
)
875 struct Cookie
*first
, *curr
, *next
, *prev
= NULL
;
877 if(!cookies
->cookies
)
880 first
= curr
= prev
= cookies
->cookies
;
882 for(; curr
; curr
= next
) {
894 cookies
->numcookies
--;
900 cookies
->cookies
= first
;
904 /*****************************************************************************
906 * Curl_cookie_cleanup()
908 * Free a "cookie object" previous created with cookie_init().
910 ****************************************************************************/
911 void Curl_cookie_cleanup(struct CookieInfo
*c
)
925 free(c
); /* free the base struct as well */
929 /* get_netscape_format()
931 * Formats a string for Netscape output file, w/o a newline at the end.
933 * Function returns a char * to a formatted line. Has to be free()d
935 static char *get_netscape_format(const struct Cookie
*co
)
938 "%s" /* httponly preamble */
939 "%s%s\t" /* domain */
940 "%s\t" /* tailmatch */
943 "%" FORMAT_OFF_T
"\t" /* expires */
946 co
->httponly
?"#HttpOnly_":"",
947 /* Make sure all domains are prefixed with a dot if they allow
948 tailmatching. This is Mozilla-style. */
949 (co
->tailmatch
&& co
->domain
&& co
->domain
[0] != '.')? ".":"",
950 co
->domain
?co
->domain
:"unknown",
951 co
->tailmatch
?"TRUE":"FALSE",
952 co
->path
?co
->path
:"/",
953 co
->secure
?"TRUE":"FALSE",
956 co
->value
?co
->value
:"");
960 * Curl_cookie_output()
962 * Writes all internally known cookies to the specified file. Specify
963 * "-" as file name to write to stdout.
965 * The function returns non-zero on write failure.
967 int Curl_cookie_output(struct CookieInfo
*c
, const char *dumphere
)
971 bool use_stdout
=FALSE
;
973 if((NULL
== c
) || (0 == c
->numcookies
))
974 /* If there are no known cookies, we don't write or even create any
978 if(strequal("-", dumphere
)) {
984 out
= fopen(dumphere
, "w");
986 return 1; /* failure */
992 fputs("# Netscape HTTP Cookie File\n"
993 "# http://curl.haxx.se/rfc/cookie_spec.html\n"
994 "# This file was generated by libcurl! Edit at your own risk.\n\n",
999 format_ptr
= get_netscape_format(co
);
1000 if(format_ptr
== NULL
) {
1001 fprintf(out
, "#\n# Fatal libcurl error\n");
1005 fprintf(out
, "%s\n", format_ptr
);
1017 struct curl_slist
*Curl_cookie_list(struct SessionHandle
*data
)
1019 struct curl_slist
*list
= NULL
;
1020 struct curl_slist
*beg
;
1024 if((data
->cookies
== NULL
) ||
1025 (data
->cookies
->numcookies
== 0))
1028 c
= data
->cookies
->cookies
;
1032 /* fill the list with _all_ the cookies we know */
1033 line
= get_netscape_format(c
);
1035 curl_slist_free_all(beg
);
1038 list
= curl_slist_append(list
, line
);
1041 curl_slist_free_all(beg
);
1044 else if(beg
== NULL
) {
1053 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */