blob: 3c4034c423765ed78cefc020295d1457176eb541 [file] [log] [blame]
Daniel Stenberg24dee482001-01-03 09:29:33 +00001/*****************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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.
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * $Id$
22 *****************************************************************************/
Daniel Stenbergae1912c1999-12-29 14:20:26 +000023
24/***
25
26
27RECEIVING COOKIE INFORMATION
28============================
29
30struct CookieInfo *cookie_init(char *file);
31
32 Inits a cookie struct to store data in a local file. This is always
33 called before any cookies are set.
34
35int cookies_set(struct CookieInfo *cookie, char *cookie_line);
36
37 The 'cookie_line' parameter is a full "Set-cookie:" line as
38 received from a server.
39
40 The function need to replace previously stored lines that this new
41 line superceeds.
42
43 It may remove lines that are expired.
44
45 It should return an indication of success/error.
46
47
48SENDING COOKIE INFORMATION
49==========================
50
51struct Cookies *cookie_getlist(struct CookieInfo *cookie,
52 char *host, char *path, bool secure);
53
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
57 not.
58
59 It shall only return cookies that haven't expired.
60
61
62Example set of cookies:
63
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
75 Set-cookie:
76 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
77 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
78****/
79
Daniel Stenbergb6e18f22000-08-24 14:26:33 +000080#include "setup.h"
81
Daniel Stenbergae1912c1999-12-29 14:20:26 +000082#include <stdlib.h>
83#include <string.h>
84#include <ctype.h>
85
86#include "cookie.h"
Daniel Stenbergae1912c1999-12-29 14:20:26 +000087#include "getdate.h"
Daniel Stenberg96dde762000-05-22 14:12:12 +000088#include "strequal.h"
Daniel Stenbergae1912c1999-12-29 14:20:26 +000089
Daniel Stenberg0f8facb2000-10-09 11:12:34 +000090/* The last #include file should be: */
91#ifdef MALLOCDEBUG
92#include "memdebug.h"
93#endif
94
Daniel Stenbergae1912c1999-12-29 14:20:26 +000095/****************************************************************************
96 *
97 * cookie_add()
98 *
99 * Add a single cookie line to the cookie keeping object.
100 *
101 ***************************************************************************/
102
Daniel Stenberg40311042001-01-05 10:11:41 +0000103struct Cookie *
104Curl_cookie_add(struct CookieInfo *c,
105 bool httpheader, /* TRUE if HTTP header-style line */
106 char *lineptr) /* first non-space of the line */
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000107{
108 struct Cookie *clist;
109 char what[MAX_COOKIE_LINE];
110 char name[MAX_NAME];
111 char *ptr;
112 char *semiptr;
113 struct Cookie *co;
114 time_t now = time(NULL);
115 bool replace_old = FALSE;
116
117 /* First, alloc and init a new struct for it */
118 co = (struct Cookie *)malloc(sizeof(struct Cookie));
119 if(!co)
120 return NULL; /* bail out if we're this low on memory */
121
122 /* clear the whole struct first */
123 memset(co, 0, sizeof(struct Cookie));
124
125 if(httpheader) {
126 /* This line was read off a HTTP-header */
127
128 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
129 ptr = lineptr;
Daniel Stenberg28ad7dc2000-09-25 22:14:42 +0000130 do {
131 if(semiptr)
132 *semiptr='\0'; /* zero terminate for a while */
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000133 /* we have a <what>=<this> pair or a 'secure' word here */
134 if(strchr(ptr, '=')) {
Daniel Stenbergc6a8bb32000-02-01 23:54:51 +0000135 name[0]=what[0]=0; /* init the buffers */
Daniel Stenberg9280c202000-02-10 23:14:53 +0000136 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^=]=%"
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000137 MAX_COOKIE_LINE_TXT "[^\r\n]",
138 name, what)) {
139 /* this is a legal <what>=<this> pair */
140 if(strequal("path", name)) {
141 co->path=strdup(what);
142 }
143 else if(strequal("domain", name)) {
144 co->domain=strdup(what);
145 }
Daniel Stenbergc6a8bb32000-02-01 23:54:51 +0000146 else if(strequal("version", name)) {
147 co->version=strdup(what);
148 }
149 else if(strequal("max-age", name)) {
150 /* Defined in RFC2109:
151
152 Optional. The Max-Age attribute defines the lifetime of the
153 cookie, in seconds. The delta-seconds value is a decimal non-
154 negative integer. After delta-seconds seconds elapse, the
155 client should discard the cookie. A value of zero means the
156 cookie should be discarded immediately.
157
158 */
159 co->maxage = strdup(what);
160 co->expires =
161 atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]);
162 }
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000163 else if(strequal("expires", name)) {
164 co->expirestr=strdup(what);
Daniel Stenberg96dde762000-05-22 14:12:12 +0000165 co->expires = curl_getdate(what, &now);
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000166 }
167 else if(!co->name) {
168 co->name = strdup(name);
169 co->value = strdup(what);
170 }
171 else
172 ;/* this is the second (or more) name we don't know
173 about! */
174 }
175 else {
176 /* this is an "illegal" <what>=<this> pair */
177 }
178 }
179 else {
180 if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^\r\n]",
181 what)) {
182 if(strequal("secure", what))
183 co->secure = TRUE;
184 else
185 ; /* unsupported keyword without assign! */
186 }
187 }
Daniel Stenberg28ad7dc2000-09-25 22:14:42 +0000188 if(!semiptr)
189 continue; /* we already know there are no more cookies */
190
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000191 *semiptr=';'; /* put the semicolon back */
192 ptr=semiptr+1;
193 while(ptr && *ptr && isspace((int)*ptr))
194 ptr++;
195 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
Daniel Stenberg28ad7dc2000-09-25 22:14:42 +0000196 } while(semiptr);
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000197 }
198 else {
199 /* This line is NOT a HTTP header style line, we do offer support for
200 reading the odd netscape cookies-file format here */
201 char *firstptr;
202 int fields;
203
204 if(lineptr[0]=='#') {
205 /* don't even try the comments */
206 free(co);
207 return NULL;
208 }
209 /* strip off the possible end-of-line characters */
Daniel Stenberg96dde762000-05-22 14:12:12 +0000210 ptr=strchr(lineptr, '\r');
211 if(ptr)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000212 *ptr=0; /* clear it */
Daniel Stenberg96dde762000-05-22 14:12:12 +0000213 ptr=strchr(lineptr, '\n');
214 if(ptr)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000215 *ptr=0; /* clear it */
216
217 firstptr=strtok(lineptr, "\t"); /* first tokenize it on the TAB */
218
219 /* Here's a quick check to eliminate normal HTTP-headers from this */
220 if(!firstptr || strchr(firstptr, ':')) {
221 free(co);
222 return NULL;
223 }
224
225 /* Now loop through the fields and init the struct we already have
226 allocated */
227 for(ptr=firstptr, fields=0; ptr; ptr=strtok(NULL, "\t"), fields++) {
228 switch(fields) {
229 case 0:
230 co->domain = strdup(ptr);
231 break;
232 case 1:
233 /* what _is_ this field for? */
234 break;
235 case 2:
Daniel Stenberg8dc9f432001-05-23 09:26:45 +0000236 /* It turns out, that sometimes the file format allows the path
237 field to remain not filled in, we try to detect this and work
238 around it! Andrés García made us aware of this... */
239 if (strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
240 /* only if the path doesn't look like a boolean option! */
241 co->path = strdup(ptr);
242 break;
243 }
244 /* this doesn't look like a path, make one up! */
245 co->path = strdup("/");
246 fields++; /* add a field and fall down to secure */
247 /* FALLTHROUGH */
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000248 case 3:
249 co->secure = strequal(ptr, "TRUE");
250 break;
251 case 4:
252 co->expires = atoi(ptr);
253 break;
254 case 5:
255 co->name = strdup(ptr);
256 break;
257 case 6:
258 co->value = strdup(ptr);
259 break;
260 }
261 }
262
263 if(7 != fields) {
264 /* we did not find the sufficient number of fields to recognize this
265 as a valid line, abort and go home */
266
267 if(co->domain)
268 free(co->domain);
269 if(co->path)
270 free(co->path);
271 if(co->name)
272 free(co->name);
273 if(co->value)
274 free(co->value);
275
276 free(co);
277 return NULL;
278 }
279
280 }
281
282 /* now, we have parsed the incoming line, we must now check if this
283 superceeds an already existing cookie, which it may if the previous have
284 the same domain and path as this */
285
286 clist = c->cookies;
287 replace_old = FALSE;
288 while(clist) {
289 if(strequal(clist->name, co->name)) {
290 /* the names are identical */
291
292 if(clist->domain && co->domain) {
293 if(strequal(clist->domain, co->domain))
294 replace_old=TRUE;
295 }
296 else if(!clist->domain && !co->domain)
297 replace_old = TRUE;
298
299 if(replace_old) {
300 /* the domains were identical */
301
302 if(clist->path && co->path) {
303 if(strequal(clist->path, co->path)) {
304 replace_old = TRUE;
305 }
306 else
307 replace_old = FALSE;
308 }
309 else if(!clist->path && !co->path)
310 replace_old = TRUE;
311 else
312 replace_old = FALSE;
313
314 }
315
316 if(replace_old) {
317 co->next = clist->next; /* get the next-pointer first */
318
319 /* then free all the old pointers */
320 if(clist->name)
321 free(clist->name);
322 if(clist->value)
323 free(clist->value);
324 if(clist->domain)
325 free(clist->domain);
326 if(clist->path)
327 free(clist->path);
328 if(clist->expirestr)
329 free(clist->expirestr);
330
Daniel Stenbergc6a8bb32000-02-01 23:54:51 +0000331 if(clist->version)
332 free(clist->version);
333 if(clist->maxage)
334 free(clist->maxage);
335
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000336 *clist = *co; /* then store all the new data */
337 }
338
339 }
340 clist = clist->next;
341 }
342
343 if(!replace_old) {
344
345 /* first, point to our "next" */
346 co->next = c->cookies;
347 /* then make ourselves first in the list */
348 c->cookies = co;
349 }
350 return co;
351}
352
353/*****************************************************************************
354 *
355 * cookie_init()
356 *
357 * Inits a cookie struct to read data from a local file. This is always
358 * called before any cookies are set. File may be NULL.
359 *
360 ****************************************************************************/
Daniel Stenberg40311042001-01-05 10:11:41 +0000361struct CookieInfo *Curl_cookie_init(char *file)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000362{
363 char line[MAX_COOKIE_LINE];
364 struct CookieInfo *c;
365 FILE *fp;
Daniel Stenberg9280c202000-02-10 23:14:53 +0000366 bool fromfile=TRUE;
367
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000368 c = (struct CookieInfo *)malloc(sizeof(struct CookieInfo));
369 if(!c)
370 return NULL; /* failed to get memory */
371 memset(c, 0, sizeof(struct CookieInfo));
372 c->filename = strdup(file?file:"none"); /* copy the name just in case */
373
Daniel Stenberg9280c202000-02-10 23:14:53 +0000374 if(strequal(file, "-")) {
375 fp = stdin;
376 fromfile=FALSE;
377 }
378 else
379 fp = file?fopen(file, "r"):NULL;
380
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000381 if(fp) {
382 while(fgets(line, MAX_COOKIE_LINE, fp)) {
383 if(strnequal("Set-Cookie:", line, 11)) {
384 /* This is a cookie line, get it! */
385 char *lineptr=&line[11];
386 while(*lineptr && isspace((int)*lineptr))
387 lineptr++;
388
Daniel Stenberg40311042001-01-05 10:11:41 +0000389 Curl_cookie_add(c, TRUE, lineptr);
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000390 }
391 else {
392 /* This might be a netscape cookie-file line, get it! */
393 char *lineptr=line;
394 while(*lineptr && isspace((int)*lineptr))
395 lineptr++;
396
Daniel Stenberg40311042001-01-05 10:11:41 +0000397 Curl_cookie_add(c, FALSE, lineptr);
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000398 }
399 }
Daniel Stenberg9280c202000-02-10 23:14:53 +0000400 if(fromfile)
401 fclose(fp);
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000402 }
403
404 return c;
405}
406
407/*****************************************************************************
408 *
409 * cookie_getlist()
410 *
411 * For a given host and path, return a linked list of cookies that the
412 * client should send to the server if used now. The secure boolean informs
413 * the cookie if a secure connection is achieved or not.
414 *
415 * It shall only return cookies that haven't expired.
416 *
417 ****************************************************************************/
418
Daniel Stenberg40311042001-01-05 10:11:41 +0000419struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
420 char *host, char *path, bool secure)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000421{
422 struct Cookie *newco;
423 struct Cookie *co;
424 time_t now = time(NULL);
425 int hostlen=strlen(host);
426 int domlen;
427
428 struct Cookie *mainco=NULL;
429
430 if(!c || !c->cookies)
431 return NULL; /* no cookie struct or no cookies in the struct */
432
433 co = c->cookies;
434
435 while(co) {
436 /* only process this cookie if it is not expired or had no expire
437 date AND that if the cookie requires we're secure we must only
438 continue if we are! */
439 if( (co->expires<=0 || (co->expires> now)) &&
440 (co->secure?secure:TRUE) ) {
441
442 /* now check if the domain is correct */
443 domlen=co->domain?strlen(co->domain):0;
444 if(!co->domain ||
Daniel Stenberg78423c52000-11-10 08:10:04 +0000445 ((domlen<=hostlen) &&
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000446 strequal(host+(hostlen-domlen), co->domain)) ) {
447 /* the right part of the host matches the domain stuff in the
448 cookie data */
449
450 /* now check the left part of the path with the cookies path
451 requirement */
452 if(!co->path ||
453 strnequal(path, co->path, strlen(co->path))) {
454
455 /* and now, we know this is a match and we should create an
456 entry for the return-linked-list */
457
458 newco = (struct Cookie *)malloc(sizeof(struct Cookie));
459 if(newco) {
460 /* first, copy the whole source cookie: */
461 memcpy(newco, co, sizeof(struct Cookie));
462
463 /* then modify our next */
464 newco->next = mainco;
465
466 /* point the main to us */
467 mainco = newco;
468 }
469 }
470 }
471 }
472 co = co->next;
473 }
474
475 return mainco; /* return the new list */
476}
477
478
479/*****************************************************************************
480 *
481 * cookie_freelist()
482 *
483 * Free a list previously returned by cookie_getlist();
484 *
485 ****************************************************************************/
486
Daniel Stenberg40311042001-01-05 10:11:41 +0000487void Curl_cookie_freelist(struct Cookie *co)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000488{
489 struct Cookie *next;
490 if(co) {
491 while(co) {
492 next = co->next;
493 free(co); /* we only free the struct since the "members" are all
494 just copied! */
495 co = next;
496 }
497 }
498}
499
500/*****************************************************************************
501 *
502 * cookie_cleanup()
503 *
504 * Free a "cookie object" previous created with cookie_init().
505 *
506 ****************************************************************************/
Daniel Stenberg40311042001-01-05 10:11:41 +0000507void Curl_cookie_cleanup(struct CookieInfo *c)
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000508{
509 struct Cookie *co;
510 struct Cookie *next;
511 if(c) {
512 if(c->filename)
513 free(c->filename);
514 co = c->cookies;
515
516 while(co) {
517 if(co->name)
518 free(co->name);
519 if(co->value)
520 free(co->value);
521 if(co->domain)
522 free(co->domain);
523 if(co->path)
524 free(co->path);
525 if(co->expirestr)
526 free(co->expirestr);
527
Daniel Stenbergc6a8bb32000-02-01 23:54:51 +0000528 if(co->version)
529 free(co->version);
530 if(co->maxage)
531 free(co->maxage);
532
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000533 next = co->next;
534 free(co);
535 co = next;
536 }
Daniel Stenberg0f8facb2000-10-09 11:12:34 +0000537 free(c); /* free the base struct as well */
Daniel Stenbergae1912c1999-12-29 14:20:26 +0000538 }
539}
540