Initial revision
[libcurl.git] / lib / cookie.c
blobdde3350421563949ccda4568012fa8581a26cdd1
2 /***
5 RECEIVING COOKIE INFORMATION
6 ============================
8 struct CookieInfo *cookie_init(char *file);
10 Inits a cookie struct to store data in a local file. This is always
11 called before any cookies are set.
13 int cookies_set(struct CookieInfo *cookie, char *cookie_line);
15 The 'cookie_line' parameter is a full "Set-cookie:" line as
16 received from a server.
18 The function need to replace previously stored lines that this new
19 line superceeds.
21 It may remove lines that are expired.
23 It should return an indication of success/error.
26 SENDING COOKIE INFORMATION
27 ==========================
29 struct Cookies *cookie_getlist(struct CookieInfo *cookie,
30 char *host, char *path, bool secure);
32 For a given host and path, return a linked list of cookies that
33 the client should send to the server if used now. The secure
34 boolean informs the cookie if a secure connection is achieved or
35 not.
37 It shall only return cookies that haven't expired.
40 Example set of cookies:
42 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
43 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
44 domain=.fidelity.com; path=/ftgw; secure
45 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
46 domain=.fidelity.com; path=/; secure
47 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
48 domain=.fidelity.com; path=/; secure
49 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
50 domain=.fidelity.com; path=/; secure
51 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
52 domain=.fidelity.com; path=/; secure
53 Set-cookie:
54 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
55 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
56 ****/
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
62 #include "cookie.h"
63 #include "setup.h"
64 #include "getdate.h"
66 /****************************************************************************
68 * cookie_add()
70 * Add a single cookie line to the cookie keeping object.
72 ***************************************************************************/
74 struct Cookie *cookie_add(struct CookieInfo *c,
75 bool httpheader, /* TRUE if HTTP header-style line */
76 char *lineptr) /* first non-space of the line */
78 struct Cookie *clist;
79 char what[MAX_COOKIE_LINE];
80 char name[MAX_NAME];
81 char *ptr;
82 char *semiptr;
83 struct Cookie *co;
84 time_t now = time(NULL);
85 bool replace_old = FALSE;
87 /* First, alloc and init a new struct for it */
88 co = (struct Cookie *)malloc(sizeof(struct Cookie));
89 if(!co)
90 return NULL; /* bail out if we're this low on memory */
92 /* clear the whole struct first */
93 memset(co, 0, sizeof(struct Cookie));
95 if(httpheader) {
96 /* This line was read off a HTTP-header */
98 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
99 ptr = lineptr;
100 while(semiptr) {
101 *semiptr='\0'; /* zero terminate for a while */
102 /* we have a <what>=<this> pair or a 'secure' word here */
103 if(strchr(ptr, '=')) {
104 if(2 == sscanf(ptr, "%" MAX_NAME_TXT "[^=]=%"
105 MAX_COOKIE_LINE_TXT "[^\r\n]",
106 name, what)) {
107 /* this is a legal <what>=<this> pair */
108 if(strequal("path", name)) {
109 co->path=strdup(what);
111 else if(strequal("domain", name)) {
112 co->domain=strdup(what);
114 else if(strequal("expires", name)) {
115 co->expirestr=strdup(what);
116 co->expires = get_date(what, &now);
118 else if(!co->name) {
119 co->name = strdup(name);
120 co->value = strdup(what);
122 else
123 ;/* this is the second (or more) name we don't know
124 about! */
126 else {
127 /* this is an "illegal" <what>=<this> pair */
130 else {
131 if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^\r\n]",
132 what)) {
133 if(strequal("secure", what))
134 co->secure = TRUE;
135 else
136 ; /* unsupported keyword without assign! */
139 *semiptr=';'; /* put the semicolon back */
140 ptr=semiptr+1;
141 while(ptr && *ptr && isspace((int)*ptr))
142 ptr++;
143 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
146 else {
147 /* This line is NOT a HTTP header style line, we do offer support for
148 reading the odd netscape cookies-file format here */
149 char *firstptr;
150 int fields;
152 if(lineptr[0]=='#') {
153 /* don't even try the comments */
154 free(co);
155 return NULL;
157 /* strip off the possible end-of-line characters */
158 if(ptr=strchr(lineptr, '\r'))
159 *ptr=0; /* clear it */
160 if(ptr=strchr(lineptr, '\n'))
161 *ptr=0; /* clear it */
163 firstptr=strtok(lineptr, "\t"); /* first tokenize it on the TAB */
165 /* Here's a quick check to eliminate normal HTTP-headers from this */
166 if(!firstptr || strchr(firstptr, ':')) {
167 free(co);
168 return NULL;
171 /* Now loop through the fields and init the struct we already have
172 allocated */
173 for(ptr=firstptr, fields=0; ptr; ptr=strtok(NULL, "\t"), fields++) {
174 switch(fields) {
175 case 0:
176 co->domain = strdup(ptr);
177 break;
178 case 1:
179 /* what _is_ this field for? */
180 break;
181 case 2:
182 co->path = strdup(ptr);
183 break;
184 case 3:
185 co->secure = strequal(ptr, "TRUE");
186 break;
187 case 4:
188 co->expires = atoi(ptr);
189 break;
190 case 5:
191 co->name = strdup(ptr);
192 break;
193 case 6:
194 co->value = strdup(ptr);
195 break;
199 if(7 != fields) {
200 /* we did not find the sufficient number of fields to recognize this
201 as a valid line, abort and go home */
203 if(co->domain)
204 free(co->domain);
205 if(co->path)
206 free(co->path);
207 if(co->name)
208 free(co->name);
209 if(co->value)
210 free(co->value);
212 free(co);
213 return NULL;
218 /* now, we have parsed the incoming line, we must now check if this
219 superceeds an already existing cookie, which it may if the previous have
220 the same domain and path as this */
222 clist = c->cookies;
223 replace_old = FALSE;
224 while(clist) {
225 if(strequal(clist->name, co->name)) {
226 /* the names are identical */
228 if(clist->domain && co->domain) {
229 if(strequal(clist->domain, co->domain))
230 replace_old=TRUE;
232 else if(!clist->domain && !co->domain)
233 replace_old = TRUE;
235 if(replace_old) {
236 /* the domains were identical */
238 if(clist->path && co->path) {
239 if(strequal(clist->path, co->path)) {
240 replace_old = TRUE;
242 else
243 replace_old = FALSE;
245 else if(!clist->path && !co->path)
246 replace_old = TRUE;
247 else
248 replace_old = FALSE;
252 if(replace_old) {
253 co->next = clist->next; /* get the next-pointer first */
255 /* then free all the old pointers */
256 if(clist->name)
257 free(clist->name);
258 if(clist->value)
259 free(clist->value);
260 if(clist->domain)
261 free(clist->domain);
262 if(clist->path)
263 free(clist->path);
264 if(clist->expirestr)
265 free(clist->expirestr);
267 *clist = *co; /* then store all the new data */
271 clist = clist->next;
274 if(!replace_old) {
276 /* first, point to our "next" */
277 co->next = c->cookies;
278 /* then make ourselves first in the list */
279 c->cookies = co;
281 return co;
284 /*****************************************************************************
286 * cookie_init()
288 * Inits a cookie struct to read data from a local file. This is always
289 * called before any cookies are set. File may be NULL.
291 ****************************************************************************/
292 struct CookieInfo *cookie_init(char *file)
294 char line[MAX_COOKIE_LINE];
295 struct CookieInfo *c;
296 FILE *fp;
298 c = (struct CookieInfo *)malloc(sizeof(struct CookieInfo));
299 if(!c)
300 return NULL; /* failed to get memory */
301 memset(c, 0, sizeof(struct CookieInfo));
302 c->filename = strdup(file?file:"none"); /* copy the name just in case */
304 fp = file?fopen(file, "r"):NULL;
305 if(fp) {
306 while(fgets(line, MAX_COOKIE_LINE, fp)) {
307 if(strnequal("Set-Cookie:", line, 11)) {
308 /* This is a cookie line, get it! */
309 char *lineptr=&line[11];
310 while(*lineptr && isspace((int)*lineptr))
311 lineptr++;
313 cookie_add(c, TRUE, lineptr);
315 else {
316 /* This might be a netscape cookie-file line, get it! */
317 char *lineptr=line;
318 while(*lineptr && isspace((int)*lineptr))
319 lineptr++;
321 cookie_add(c, FALSE, lineptr);
324 fclose(fp);
327 return c;
330 /*****************************************************************************
332 * cookie_getlist()
334 * For a given host and path, return a linked list of cookies that the
335 * client should send to the server if used now. The secure boolean informs
336 * the cookie if a secure connection is achieved or not.
338 * It shall only return cookies that haven't expired.
340 ****************************************************************************/
342 struct Cookie *cookie_getlist(struct CookieInfo *c,
343 char *host, char *path, bool secure)
345 struct Cookie *newco;
346 struct Cookie *co;
347 time_t now = time(NULL);
348 int hostlen=strlen(host);
349 int domlen;
351 struct Cookie *mainco=NULL;
353 if(!c || !c->cookies)
354 return NULL; /* no cookie struct or no cookies in the struct */
356 co = c->cookies;
358 while(co) {
359 /* only process this cookie if it is not expired or had no expire
360 date AND that if the cookie requires we're secure we must only
361 continue if we are! */
362 if( (co->expires<=0 || (co->expires> now)) &&
363 (co->secure?secure:TRUE) ) {
365 /* now check if the domain is correct */
366 domlen=co->domain?strlen(co->domain):0;
367 if(!co->domain ||
368 ((domlen<hostlen) &&
369 strequal(host+(hostlen-domlen), co->domain)) ) {
370 /* the right part of the host matches the domain stuff in the
371 cookie data */
373 /* now check the left part of the path with the cookies path
374 requirement */
375 if(!co->path ||
376 strnequal(path, co->path, strlen(co->path))) {
378 /* and now, we know this is a match and we should create an
379 entry for the return-linked-list */
381 newco = (struct Cookie *)malloc(sizeof(struct Cookie));
382 if(newco) {
383 /* first, copy the whole source cookie: */
384 memcpy(newco, co, sizeof(struct Cookie));
386 /* then modify our next */
387 newco->next = mainco;
389 /* point the main to us */
390 mainco = newco;
395 co = co->next;
398 return mainco; /* return the new list */
402 /*****************************************************************************
404 * cookie_freelist()
406 * Free a list previously returned by cookie_getlist();
408 ****************************************************************************/
410 void cookie_freelist(struct Cookie *co)
412 struct Cookie *next;
413 if(co) {
414 while(co) {
415 next = co->next;
416 free(co); /* we only free the struct since the "members" are all
417 just copied! */
418 co = next;
423 /*****************************************************************************
425 * cookie_cleanup()
427 * Free a "cookie object" previous created with cookie_init().
429 ****************************************************************************/
430 void cookie_cleanup(struct CookieInfo *c)
432 struct Cookie *co;
433 struct Cookie *next;
434 if(c) {
435 if(c->filename)
436 free(c->filename);
437 co = c->cookies;
439 while(co) {
440 if(co->name)
441 free(co->name);
442 if(co->value)
443 free(co->value);
444 if(co->domain)
445 free(co->domain);
446 if(co->path)
447 free(co->path);
448 if(co->expirestr)
449 free(co->expirestr);
451 next = co->next;
452 free(co);
453 co = next;