1
/*****************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* Copyright (C) 2002, Daniel Stenberg, <daniel@haxx.se>, et al.
10
* In order to be useful for every potential user, curl and libcurl are
11
* dual-licensed under the MPL and the MIT/X-derivate licenses.
13
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
14
* copies of the Software, and permit persons to whom the Software is
15
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
16
* licenses. You may pick one of these licenses.
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.31 2002/02/27 07:38:04 bagder 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
91
/* The last #include file should be: */
96
/****************************************************************************
100
* Add a single cookie line to the cookie keeping object.
102
***************************************************************************/
105
Curl_cookie_add(struct CookieInfo *c,
106
bool httpheader, /* TRUE if HTTP header-style line */
107
char *lineptr, /* first non-space of the line */
108
char *domain) /* default domain */
110
struct Cookie *clist;
111
char what[MAX_COOKIE_LINE];
116
struct Cookie *lastc=NULL;
117
time_t now = time(NULL);
118
bool replace_old = FALSE;
120
/* First, alloc and init a new struct for it */
121
co = (struct Cookie *)malloc(sizeof(struct Cookie));
123
return NULL; /* bail out if we're this low on memory */
125
/* clear the whole struct first */
126
memset(co, 0, sizeof(struct Cookie));
129
/* This line was read off a HTTP-header */
131
semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
134
/* we have a <what>=<this> pair or a 'secure' word here */
135
sep = strchr(ptr, '=');
136
if(sep && (!semiptr || (semiptr>sep)) ) {
138
* There is a = sign and if there was a semicolon too, which make sure
139
* that the semicolon comes _after_ the equal sign.
142
name[0]=what[0]=0; /* init the buffers */
143
if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
144
MAX_COOKIE_LINE_TXT "[^;\r\n]",
146
/* this is a <name>=<what> pair */
148
/* Strip off trailing whitespace from the 'what' */
149
int len=strlen(what);
150
while(len && isspace((int)what[len-1])) {
155
if(strequal("path", name)) {
156
co->path=strdup(what);
158
else if(strequal("domain", name)) {
159
co->domain=strdup(what);
160
co->field1= (what[0]=='.')?2:1;
162
else if(strequal("version", name)) {
163
co->version=strdup(what);
165
else if(strequal("max-age", name)) {
166
/* Defined in RFC2109:
168
Optional. The Max-Age attribute defines the lifetime of the
169
cookie, in seconds. The delta-seconds value is a decimal non-
170
negative integer. After delta-seconds seconds elapse, the
171
client should discard the cookie. A value of zero means the
172
cookie should be discarded immediately.
175
co->maxage = strdup(what);
177
atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + now;
179
else if(strequal("expires", name)) {
180
co->expirestr=strdup(what);
181
co->expires = curl_getdate(what, &now);
184
co->name = strdup(name);
185
co->value = strdup(what);
188
else this is the second (or more) name we don't know
192
/* this is an "illegal" <what>=<this> pair */
196
if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
198
if(strequal("secure", what))
201
unsupported keyword without assign! */
205
if(!semiptr || !*semiptr) {
206
/* we already know there are no more cookies */
212
while(ptr && *ptr && isspace((int)*ptr))
214
semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
217
/* There are no more semicolons, but there's a final name=value pair
219
semiptr=strchr(ptr, '\0');
222
if(NULL == co->name) {
223
/* we didn't get a cookie name, this is an illegal line, bail out */
236
if(NULL == co->domain)
237
/* no domain given in the header line, set the default now */
238
co->domain=domain?strdup(domain):NULL;
241
/* This line is NOT a HTTP header style line, we do offer support for
242
reading the odd netscape cookies-file format here */
247
if(lineptr[0]=='#') {
248
/* don't even try the comments */
252
/* strip off the possible end-of-line characters */
253
ptr=strchr(lineptr, '\r');
255
*ptr=0; /* clear it */
256
ptr=strchr(lineptr, '\n');
258
*ptr=0; /* clear it */
260
firstptr=strtok_r(lineptr, "\t", &tok_buf); /* first tokenize it on the TAB */
262
/* Here's a quick check to eliminate normal HTTP-headers from this */
263
if(!firstptr || strchr(firstptr, ':')) {
268
/* Now loop through the fields and init the struct we already have
270
for(ptr=firstptr, fields=0; ptr; ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
273
co->domain = strdup(ptr);
276
/* This field got its explanation on the 23rd of May 2001 by
279
flag: A TRUE/FALSE value indicating if all machines within a given
280
domain can access the variable. This value is set automatically by
281
the browser, depending on the value you set for the domain.
283
As far as I can see, it is set to true when the cookie says
284
.domain.com and to false when the domain is complete www.domain.com
286
We don't currently take advantage of this knowledge.
288
co->field1=strequal(ptr, "TRUE")+1; /* store information */
291
/* It turns out, that sometimes the file format allows the path
292
field to remain not filled in, we try to detect this and work
293
around it! Andrļæ½s Garcļæ½a made us aware of this... */
294
if (strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
295
/* only if the path doesn't look like a boolean option! */
296
co->path = strdup(ptr);
299
/* this doesn't look like a path, make one up! */
300
co->path = strdup("/");
301
fields++; /* add a field and fall down to secure */
304
co->secure = strequal(ptr, "TRUE");
307
co->expires = atoi(ptr);
310
co->name = strdup(ptr);
313
co->value = strdup(ptr);
319
/* we did not find the sufficient number of fields to recognize this
320
as a valid line, abort and go home */
337
co->livecookie = c->running;
339
/* now, we have parsed the incoming line, we must now check if this
340
superceeds an already existing cookie, which it may if the previous have
341
the same domain and path as this */
346
if(strequal(clist->name, co->name)) {
347
/* the names are identical */
349
if(clist->domain && co->domain) {
350
if(strequal(clist->domain, co->domain))
353
else if(!clist->domain && !co->domain)
357
/* the domains were identical */
359
if(clist->path && co->path) {
360
if(strequal(clist->path, co->path)) {
366
else if(!clist->path && !co->path)
373
if(replace_old && !co->livecookie && clist->livecookie) {
374
/* Both cookies matched fine, except that the already present
375
cookie is "live", which means it was set from a header, while
376
the new one isn't "live" and thus only read from a file. We let
377
live cookies stay alive */
379
/* Free the newcomer and get out of here! */
394
co->next = clist->next; /* get the next-pointer first */
396
/* then free all the old pointers */
406
free(clist->expirestr);
409
free(clist->version);
413
*clist = *co; /* then store all the new data */
415
free(co); /* free the newly alloced memory */
416
co = clist; /* point to the previous struct instead */
418
/* We have replaced a cookie, now skip the rest of the list but
419
make sure the 'lastc' pointer is properly set */
432
/* then make the last item point on this new one */
439
c->numcookies++; /* one more cookie in the jar */
444
/*****************************************************************************
448
* Inits a cookie struct to read data from a local file. This is always
449
* called before any cookies are set. File may be NULL.
451
****************************************************************************/
452
struct CookieInfo *Curl_cookie_init(char *file, struct CookieInfo *inc)
454
char line[MAX_COOKIE_LINE];
455
struct CookieInfo *c;
460
/* we didn't get a struct, create one */
461
c = (struct CookieInfo *)malloc(sizeof(struct CookieInfo));
463
return NULL; /* failed to get memory */
464
memset(c, 0, sizeof(struct CookieInfo));
465
c->filename = strdup(file?file:"none"); /* copy the name just in case */
468
/* we got an already existing one, use that */
471
c->running = FALSE; /* this is not running, this is init */
473
if(file && strequal(file, "-")) {
478
fp = file?fopen(file, "r"):NULL;
483
while(fgets(line, MAX_COOKIE_LINE, fp)) {
484
if(strnequal("Set-Cookie:", line, 11)) {
485
/* This is a cookie line, get it! */
493
while(*lineptr && isspace((int)*lineptr))
496
Curl_cookie_add(c, headerline, lineptr, NULL);
502
c->running = TRUE; /* now, we're running */
507
/*****************************************************************************
509
* Curl_cookie_getlist()
511
* For a given host and path, return a linked list of cookies that the
512
* client should send to the server if used now. The secure boolean informs
513
* the cookie if a secure connection is achieved or not.
515
* It shall only return cookies that haven't expired.
517
****************************************************************************/
519
struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
520
char *host, char *path, bool secure)
522
struct Cookie *newco;
524
time_t now = time(NULL);
525
int hostlen=strlen(host);
528
struct Cookie *mainco=NULL;
530
if(!c || !c->cookies)
531
return NULL; /* no cookie struct or no cookies in the struct */
536
/* only process this cookie if it is not expired or had no expire
537
date AND that if the cookie requires we're secure we must only
538
continue if we are! */
539
if( (co->expires<=0 || (co->expires> now)) &&
540
(co->secure?secure:TRUE) ) {
542
/* now check if the domain is correct */
543
domlen=co->domain?strlen(co->domain):0;
545
((domlen<=hostlen) &&
546
strequal(host+(hostlen-domlen), co->domain)) ) {
547
/* the right part of the host matches the domain stuff in the
550
/* now check the left part of the path with the cookies path
553
strnequal(path, co->path, strlen(co->path))) {
555
/* and now, we know this is a match and we should create an
556
entry for the return-linked-list */
558
newco = (struct Cookie *)malloc(sizeof(struct Cookie));
560
/* first, copy the whole source cookie: */
561
memcpy(newco, co, sizeof(struct Cookie));
563
/* then modify our next */
564
newco->next = mainco;
566
/* point the main to us */
575
return mainco; /* return the new list */
579
/*****************************************************************************
581
* Curl_cookie_freelist()
583
* Free a list of cookies previously returned by Curl_cookie_getlist();
585
****************************************************************************/
587
void Curl_cookie_freelist(struct Cookie *co)
593
free(co); /* we only free the struct since the "members" are all
600
/*****************************************************************************
602
* Curl_cookie_cleanup()
604
* Free a "cookie object" previous created with cookie_init().
606
****************************************************************************/
607
void Curl_cookie_cleanup(struct CookieInfo *c)
637
free(c); /* free the base struct as well */
642
* Curl_cookie_output()
644
* Writes all internally known cookies to the specified file. Specify
645
* "-" as file name to write to stdout.
647
* The function returns non-zero on write failure.
649
int Curl_cookie_output(struct CookieInfo *c, char *dumphere)
653
bool use_stdout=FALSE;
655
if((NULL == c) || (0 == c->numcookies))
656
/* If there are no known cookies, we don't write or even create any
660
if(strequal("-", dumphere)) {
666
out = fopen(dumphere, "w");
668
return 1; /* failure */
672
fputs("# Netscape HTTP Cookie File\n"
673
"# http://www.netscape.com/newsref/std/cookie_spec.html\n"
674
"# This file was generated by libcurl! Edit at your own risk.\n\n",
687
co->domain?co->domain:"unknown",
688
co->field1==2?"TRUE":"FALSE",
689
co->path?co->path:"/",
690
co->secure?"TRUE":"FALSE",
691
(unsigned int)co->expires,
693
co->value?co->value:"");
705
#ifdef CURL_COOKIE_DEBUG
708
* On my Solaris box, this command line builds this test program:
710
* gcc -g -o cooktest -DCURL_COOKIE_DEBUG -DHAVE_CONFIG_H -I.. -I../include cookie.c strequal.o getdate.o memdebug.o mprintf.o strtok.o -lnsl -lsocket
714
int main(int argc, char **argv)
716
struct CookieInfo *c=NULL;
718
c = Curl_cookie_init(argv[1], c);
719
Curl_cookie_add(c, TRUE, "PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/ftgw; secure");
720
Curl_cookie_add(c, TRUE, "foobar=yes; domain=.haxx.se; path=/looser;");
721
c = Curl_cookie_init(argv[1], c);
723
Curl_cookie_output(c);
724
Curl_cookie_cleanup(c);
734
* eval: (load-file "../curl-mode.el")
737
* vim: et sw=2 ts=2 sts=2 tw=78