Annotation of libwww/Library/src/HTCookie.c, revision 2.6
2.1 frystyk 1: /*
2: ** SIMPLE HTTP COOKIE FILTER HANDLER
3: **
4: ** (c) COPYRIGHT MIT 1999.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.6 ! vbancrof 6: ** @(#) $Id: HTCookie.c,v 2.5 1999/07/31 01:30:16 raff Exp $
2.1 frystyk 7: **
8: ** Written based on Jim Raven's code sent to the <www-lib@w3.org> list
9: **
10: ** History:
11: ** JR: Sent code to the list
12: ** HFN: Made a libwww module
13: **
14: */
15:
16: /* Library include files */
17: #include "wwwsys.h"
18: #include "WWWUtil.h"
19: #include "WWWCore.h"
20: #include "WWWHTTP.h"
21: #include "WWWMIME.h"
22: #include "HTCookie.h" /* Implemented here */
23:
24: /* Interface to persistent cookie jar */
25: PRIVATE HTCookieSetCallback * SetCookie = NULL;
26: PRIVATE void * SetCookieContext = NULL;
27:
28: PRIVATE HTCookieFindCallback * FindCookie = NULL;
29: PRIVATE void * FindCookieContext = NULL;
30:
31: /* Are cookies enabled */
32: PRIVATE BOOL baking_cookies = NO;
33:
34: /* Our cookies */
35: struct _HTCookie {
36: char * name;
37: char * value;
38: char * domain;
39: char * path;
40: time_t expiration;
41: BOOL secure;
42: };
43:
44: /* Hold all cookies found for a single request */
45: typedef struct _HTCookieHolder {
46: HTRequest * request;
47: HTList * cookies;
48: } HTCookieHolder;
49:
50: /* List of current cookie holders */
51: PRIVATE HTList * cookie_holder = NULL;
52:
53: /* What should we do with cookies? */
2.2 frystyk 54: PRIVATE HTCookieMode CookieMode = HT_COOKIE_PROMPT | HT_COOKIE_ACCEPT | HT_COOKIE_SEND;
2.1 frystyk 55:
56: /* ------------------------------------------------------------------------- */
57:
58: PRIVATE HTCookie * HTCookie_new (void)
59: {
60: HTCookie * me = NULL;
61: if ((me = (HTCookie *) HT_CALLOC(1, sizeof(HTCookie))) == NULL)
62: HT_OUTOFMEM("HTCookie_new");
63: return me;
64: }
65:
66: PRIVATE BOOL HTCookie_delete (HTCookie * me)
67: {
68: if (me) {
69: HT_FREE(me->name);
70: HT_FREE(me->value);
71: HT_FREE(me->domain);
72: HT_FREE(me->path);
73: HT_FREE(me);
74: return YES;
75: }
76: return NO;
77: }
78:
79: PUBLIC BOOL HTCookie_setName (HTCookie * me, const char * name)
80: {
81: if (me && name) {
82: me->name = StrAllocCopy(me->name, name);
83: return YES;
84: }
85: return NO;
86: }
87:
88: PUBLIC char * HTCookie_name (HTCookie * me)
89: {
90: return me ? me->name : NULL;
91: }
92:
93: PUBLIC BOOL HTCookie_setValue (HTCookie * me, const char * value)
94: {
95: if (me && value) {
96: me->value = StrAllocCopy(me->value, value);
97: return YES;
98: }
99: return NO;
100: }
101:
102: PUBLIC char * HTCookie_value (HTCookie * me)
103: {
104: return me ? me->value : NULL;
105: }
106:
107: PUBLIC BOOL HTCookie_setDomain (HTCookie * me, const char * domain)
108: {
109: if (me && domain) {
110: me->domain = StrAllocCopy(me->domain, domain);
111: return YES;
112: }
113: return NO;
114: }
115:
116: PUBLIC char * HTCookie_domain (HTCookie * me)
117: {
118: return me ? me->domain : NULL;
119: }
120:
121: PUBLIC BOOL HTCookie_setPath (HTCookie * me, const char * path)
122: {
123: if (me && path) {
124: me->path = StrAllocCopy(me->path, path);
125: return YES;
126: }
127: return NO;
128: }
129:
130: PUBLIC char * HTCookie_path (HTCookie * me)
131: {
132: return me ? me->path : NULL;
133: }
134:
135: PUBLIC time_t HTCookie_setExpiration (HTCookie * me, time_t expiration)
136: {
137: if (me) {
138: me->expiration = expiration;
139: return YES;
140: }
141: return NO;
142: }
143:
144: PUBLIC time_t HTCookie_expiration (HTCookie * me)
145: {
146: return me ? me->expiration : -1;
147: }
148:
149: PUBLIC time_t HTCookie_setSecure (HTCookie * me, BOOL secure)
150: {
151: if (me) {
152: me->secure = secure;
153: return YES;
154: }
155: return NO;
156: }
157:
158: PUBLIC BOOL HTCookie_isSecure (HTCookie * me)
159: {
160: return me ? me->secure : NO;
161: }
162:
163: /* ------------------------------------------------------------------------- */
164:
165: PRIVATE BOOL HTCookieHolder_addCookie (HTRequest * request, HTCookie * cookie)
166: {
167: if (request && cookie) {
168: HTList * cur = cookie_holder;
169: HTCookieHolder * pres = NULL;
170:
171: /* Make sure that we have a cookie holder list */
172: if (!cookie_holder) cookie_holder = HTList_new();
173:
174: /* See if we already have a cookie holder for this request */
175: while ((pres = (HTCookieHolder *) HTList_nextObject(cur))) {
176: if (pres->request == request) break;
177: }
178:
179: /* If found then use existing cookie holder, otherwise create new one */
180: if (!pres) {
181: if ((pres = (HTCookieHolder *) HT_CALLOC(1, sizeof(HTCookieHolder))) == NULL)
182: HT_OUTOFMEM("HTCookieHolder_newCookie");
183: pres->request = request;
184: pres->cookies = HTList_new();
185:
186: /* Add to cookie holder list */
187: HTList_addObject(cookie_holder, pres);
188: }
189:
190: /* Now add the cookie */
191: HTList_addObject(pres->cookies, cookie);
192:
193: return YES;
194: }
195: return NO;
196: }
197:
198: PRIVATE HTCookieHolder * HTCookieHolder_find (HTRequest * request)
199: {
200: if (request) {
201: HTList * cur = cookie_holder;
202: HTCookieHolder * pres = NULL;
203: while ((pres = (HTCookieHolder *) HTList_nextObject(cur))) {
204: if (pres->request == request) return pres;
205: }
206: }
207: return NULL;
208: }
209:
210: PRIVATE BOOL HTCookieHolder_delete (HTCookieHolder * me)
211: {
212: if (me) {
213: if (me->cookies) {
214: HTList * cookies = me->cookies;
215: HTCookie * cookie;
216: while ((cookie = (HTCookie *) HTList_nextObject(cookies)))
2.2 frystyk 217: HTCookie_delete(cookie);
218: HTList_delete(me->cookies);
2.1 frystyk 219: }
220: HTList_removeObject(cookie_holder, me);
221: HT_FREE(me);
222: return YES;
223: }
224: return NO;
225: }
226:
227: PRIVATE BOOL HTCookieHolder_deleteAll (void)
228: {
229: if (cookie_holder) {
230: HTList * cur = cookie_holder;
231: HTCookieHolder * pres = NULL;
232: while ((pres = (HTCookieHolder *) HTList_nextObject(cur))) {
233: HTCookieHolder_delete(pres);
234: }
235: HTList_delete(cookie_holder);
236: cookie_holder = NULL;
237: return YES;
238: }
239: return NO;
240: }
241:
242: /* ------------------------------------------------------------------------- */
243:
244: /*
2.6 ! vbancrof 245: ** Added By Jesse Morgan <jesse@jesterpm.net> on 2006-05-22
! 246: ** Splits a KEY=VALUE pair into a KEY and VALUE
! 247: */
! 248: PRIVATE int HTCookie_splitPair (char * pair, char ** key, char ** value)
! 249: {
! 250: char * index = strchr(pair, '=');
! 251:
! 252: if (index == NULL) {
! 253: return HT_ERROR;
! 254: }
! 255:
! 256: *key = pair;
! 257: *index = '\0';
! 258: *value = ++index;
! 259:
! 260: return HT_OK;
! 261: }
! 262:
! 263: /*
2.1 frystyk 264: ** MIME header parser for the Set-Cookie header field. We parse the cookies
265: ** and create HTCookie objects and store them in the cookie holder so that
266: ** the cookie after filter can deal with them accordingly.
267: */
268: PRIVATE int HTCookie_parseSetCookie (HTRequest * request, HTResponse * response,
269: char * token, char * value)
270:
271: {
2.6 ! vbancrof 272: char * cookie_name = NULL;
! 273: char * cookie_value = NULL;
! 274:
! 275: if (HTCookie_splitPair(HTNextParam(&value), &cookie_name, &cookie_value) != HT_OK) {
! 276: return HT_ERROR; /* Malformed Cookie */
! 277: }
! 278:
2.1 frystyk 279: if (cookie_name && *cookie_name && cookie_value) {
280: HTCookie * cookie = HTCookie_new();
281: char * param_pair;
282:
283: HTCookie_setName(cookie, cookie_name);
284: HTCookie_setValue(cookie, cookie_value);
285:
286: /* Add the cookie to our holder */
287: HTCookieHolder_addCookie(request, cookie);
288:
289: /* Parse cookie parameters */
290: while ((param_pair = HTNextParam(&value))) {
2.6 ! vbancrof 291: char * tok = NULL;
! 292: char * val = NULL;
! 293:
! 294: if (HTCookie_splitPair(param_pair, &tok, &val) != HT_OK) {
! 295: return HT_ERROR; /* Malformed Cookie */
! 296: }
! 297:
2.1 frystyk 298: if (tok) {
299: if (!strcasecomp(tok, "expires") && val && *val) {
300: HTTRACE(STREAM_TRACE, "Cookie...... Expires `%s\'\n" _ val);
301: HTCookie_setExpiration(cookie, HTParseTime(val, NULL, YES));
302: } else if (!strcasecomp(tok, "domain") && val && *val) {
303: HTTRACE(STREAM_TRACE, "Cookie...... Domain `%s\'\n" _ val);
304: HTCookie_setDomain(cookie, val);
305: } else if (!strcasecomp(tok, "path") && val && *val) {
306: HTTRACE(STREAM_TRACE, "Cookie...... Path `%s\'\n" _ val);
307: HTCookie_setPath(cookie, val);
308: } else if (!strcasecomp(tok, "secure")) {
309: HTTRACE(STREAM_TRACE, "Cookie...... Secure `%s\'\n" _ val);
310: HTCookie_setSecure(cookie, YES);
311: } else
312: HTTRACE(STREAM_TRACE, "Cookie...... Unknown `%s\' with value `%s\'\n" _
313: tok _ val ? val : "<null>");
314: }
315: }
316: }
317: return HT_OK;
318: }
319:
320: /*
321: ** Check whether the application provides us with a cookie or more.
322: */
323: PRIVATE int HTCookie_beforeFilter (HTRequest * request, void * param, int mode)
324: {
2.2 frystyk 325: if ((CookieMode & HT_COOKIE_SEND) && FindCookie) {
2.1 frystyk 326: HTAssocList * cookies = (*FindCookie)(request, FindCookieContext);
2.2 frystyk 327: if (cookies) {
2.1 frystyk 328: HTChunk * cookie_header = HTChunk_new(64);
2.2 frystyk 329: HTAssocList * cur = cookies;
2.1 frystyk 330: HTAssoc * pres;
331: BOOL first=YES;
2.2 frystyk 332: while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
2.3 frystyk 333: if (!first) HTChunk_putc(cookie_header, ';');
2.1 frystyk 334: HTChunk_puts(cookie_header, HTAssoc_name(pres));
335: HTChunk_putc(cookie_header, '=');
336: HTChunk_puts(cookie_header, HTAssoc_value(pres));
337: first = NO;
338: }
339: HTRequest_addExtraHeader(request, "Cookie", HTChunk_data(cookie_header));
340: HTChunk_delete(cookie_header);
341:
342: /* Also delete the association list */
343: HTAssocList_delete(cookies);
344: }
345: }
346: return HT_OK;
347: }
348:
349: /*
350: ** Check the response to see if we got a cookie or more.
351: ** If so then figure out what to do with it (prompt user, store, etc.)
352: */
353: PRIVATE int HTCookie_afterFilter (HTRequest * request, HTResponse * response,
354: void * param, int status)
355: {
2.2 frystyk 356: if ((CookieMode & HT_COOKIE_ACCEPT) && SetCookie) {
2.1 frystyk 357: HTCookieHolder * holder = HTCookieHolder_find(request);
358: if (holder) {
359: HTList * cookies = holder->cookies;
360: HTCookie * pres;
361: while ((pres = (HTCookie *) HTAssocList_nextObject(cookies))) {
362:
2.2 frystyk 363: /* Should we check to see if hosts match? */
2.5 raff 364: if (CookieMode & (HT_COOKIE_SAME_HOST|HT_COOKIE_SAME_DOMAIN)) {
2.2 frystyk 365: char * cookie_host = HTCookie_domain(pres);
366: if (cookie_host) {
2.5 raff 367: int res;
2.2 frystyk 368: char * addr = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
369: char * host = HTParse(addr, "", PARSE_HOST);
2.5 raff 370:
371: if (CookieMode & HT_COOKIE_SAME_DOMAIN)
372: res = tailcasecomp(cookie_host, host);
373: else
374: res = strcasecomp(cookie_host, host);
375:
376: if (res != 0) {
2.2 frystyk 377: HTTRACE(APP_TRACE, "Cookie...... Host `%s\' doesn't match what is sent in cookie `%s\'\n" _ host _ cookie_host);
378: HT_FREE(addr);
379: continue;
380: }
381: HT_FREE(addr);
382: }
383: }
384:
385: /* Should we prompt the user? */
386: if (CookieMode & HT_COOKIE_PROMPT) {
387: HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
388: if (prompt) {
389: if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_ACCEPT_COOKIE,
390: NULL, NULL, NULL) != YES)
391: continue;
392: } else
393: continue;
394: }
2.1 frystyk 395:
2.2 frystyk 396: /* Call the application with our new cookie */
2.1 frystyk 397: (*SetCookie)(request, pres, SetCookieContext);
398: }
399:
400: /* Delete cookie holder */
401: HTCookieHolder_delete(holder);
402: }
403: }
404: return HT_OK;
405: }
406:
407: /* ------------------------------------------------------------------------- */
408:
409: /*
410: ** Start and stop the cookie engine
411: */
412: PUBLIC BOOL HTCookie_init (void)
413: {
414: if (!baking_cookies) {
415:
416: /* Register the SetCookie header parser */
417: HTHeader_addParser("Set-Cookie", NO, HTCookie_parseSetCookie);
418:
419: /* Register the cookie before and after filters */
420: HTNet_addBefore(HTCookie_beforeFilter, "http://*", NULL, HT_FILTER_MIDDLE);
421: HTNet_addAfter(HTCookie_afterFilter, "http://*", NULL, HT_ALL, HT_FILTER_MIDDLE);
422:
423: baking_cookies = YES;
424: return YES;
425: }
426: return NO;
427: }
428:
429: PUBLIC BOOL HTCookie_terminate (void)
430: {
431: /* Unregister Set-Cookie header parser */
432: HTHeader_deleteParser("Set-Cookie");
433:
434: /* Unregister the cookie before and after filters */
435: HTNet_deleteBefore(HTCookie_beforeFilter);
436: HTNet_deleteAfter(HTCookie_afterFilter);
437:
438: /* Delete all pending cookies */
439: HTCookieHolder_deleteAll();
2.4 raff 440:
441: baking_cookies = NO;
2.1 frystyk 442: return YES;
443: }
444:
445: PUBLIC BOOL HTCookie_setCookieMode (HTCookieMode mode)
446: {
447: CookieMode = mode;
448: return YES;
2.2 frystyk 449: }
450:
451: PUBLIC HTCookieMode HTCookie_cookieMode (void)
452: {
453: return CookieMode;
2.1 frystyk 454: }
455:
456: /*
457: ** Callbacks can be used by the application to set and retrieve cookies
458: ** from a persistent cookie jar as libwww doesn't provide a persistent
459: ** storage for this kind of thing and they probably should be secured
460: ** anyway.
461: */
462: PUBLIC BOOL HTCookie_setCallbacks (HTCookieSetCallback * setCookie,
463: void * setCookieContext,
464: HTCookieFindCallback * findCookie,
465: void * findCookieContext)
466: {
467: if (setCookie && findCookie) {
468: HTTRACE(APP_TRACE, "Cookie...... Registering cookie callbacks\n");
469: SetCookie = setCookie;
470: SetCookieContext = setCookieContext;
471:
472: FindCookie = findCookie;
473: FindCookieContext = findCookieContext;
474: return YES;
475: }
476: return NO;
477: }
478:
479: PUBLIC BOOL HTCookie_deleteCallbacks (void)
480: {
481: HTTRACE(APP_TRACE, "Cookie...... Unregistering cookie callbacks\n");
482: SetCookie = NULL;
483: SetCookieContext = NULL;
484: FindCookie = NULL;
485: FindCookieContext = NULL;
486: return YES;
487: }
Webmaster