Annotation of libwww/Library/src/HTCookie.c, revision 2.4
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.4 ! raff 6: ** @(#) $Id: HTCookie.c,v 2.3 1999/06/23 16:23:43 frystyk 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: /*
245: ** MIME header parser for the Set-Cookie header field. We parse the cookies
246: ** and create HTCookie objects and store them in the cookie holder so that
247: ** the cookie after filter can deal with them accordingly.
248: */
249: PRIVATE int HTCookie_parseSetCookie (HTRequest * request, HTResponse * response,
250: char * token, char * value)
251:
252: {
253: char * cookie_name = HTNextField(&value);
254: char * cookie_value = HTNextField(&value);
255: if (cookie_name && *cookie_name && cookie_value) {
256: HTCookie * cookie = HTCookie_new();
257: char * param_pair;
258:
259: HTCookie_setName(cookie, cookie_name);
260: HTCookie_setValue(cookie, cookie_value);
261:
262: /* Add the cookie to our holder */
263: HTCookieHolder_addCookie(request, cookie);
264:
265: /* Parse cookie parameters */
266: while ((param_pair = HTNextParam(&value))) {
267: char * tok = HTNextField(¶m_pair);
268: char * val = param_pair;
269: if (tok) {
270: if (!strcasecomp(tok, "expires") && val && *val) {
271: HTTRACE(STREAM_TRACE, "Cookie...... Expires `%s\'\n" _ val);
272: HTCookie_setExpiration(cookie, HTParseTime(val, NULL, YES));
273: } else if (!strcasecomp(tok, "domain") && val && *val) {
274: HTTRACE(STREAM_TRACE, "Cookie...... Domain `%s\'\n" _ val);
275: HTCookie_setDomain(cookie, val);
276: } else if (!strcasecomp(tok, "path") && val && *val) {
277: HTTRACE(STREAM_TRACE, "Cookie...... Path `%s\'\n" _ val);
278: HTCookie_setPath(cookie, val);
279: } else if (!strcasecomp(tok, "secure")) {
280: HTTRACE(STREAM_TRACE, "Cookie...... Secure `%s\'\n" _ val);
281: HTCookie_setSecure(cookie, YES);
282: } else
283: HTTRACE(STREAM_TRACE, "Cookie...... Unknown `%s\' with value `%s\'\n" _
284: tok _ val ? val : "<null>");
285: }
286: }
287: }
288: return HT_OK;
289: }
290:
291: /*
292: ** Check whether the application provides us with a cookie or more.
293: */
294: PRIVATE int HTCookie_beforeFilter (HTRequest * request, void * param, int mode)
295: {
2.2 frystyk 296: if ((CookieMode & HT_COOKIE_SEND) && FindCookie) {
2.1 frystyk 297: HTAssocList * cookies = (*FindCookie)(request, FindCookieContext);
2.2 frystyk 298: if (cookies) {
2.1 frystyk 299: HTChunk * cookie_header = HTChunk_new(64);
2.2 frystyk 300: HTAssocList * cur = cookies;
2.1 frystyk 301: HTAssoc * pres;
302: BOOL first=YES;
2.2 frystyk 303: while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
2.3 frystyk 304: if (!first) HTChunk_putc(cookie_header, ';');
2.1 frystyk 305: HTChunk_puts(cookie_header, HTAssoc_name(pres));
306: HTChunk_putc(cookie_header, '=');
307: HTChunk_puts(cookie_header, HTAssoc_value(pres));
308: first = NO;
309: }
310: HTRequest_addExtraHeader(request, "Cookie", HTChunk_data(cookie_header));
311: HTChunk_delete(cookie_header);
312:
313: /* Also delete the association list */
314: HTAssocList_delete(cookies);
315: }
316: }
317: return HT_OK;
318: }
319:
320: /*
321: ** Check the response to see if we got a cookie or more.
322: ** If so then figure out what to do with it (prompt user, store, etc.)
323: */
324: PRIVATE int HTCookie_afterFilter (HTRequest * request, HTResponse * response,
325: void * param, int status)
326: {
2.2 frystyk 327: if ((CookieMode & HT_COOKIE_ACCEPT) && SetCookie) {
2.1 frystyk 328: HTCookieHolder * holder = HTCookieHolder_find(request);
329: if (holder) {
330: HTList * cookies = holder->cookies;
331: HTCookie * pres;
332: while ((pres = (HTCookie *) HTAssocList_nextObject(cookies))) {
333:
2.2 frystyk 334: /* Should we check to see if hosts match? */
335: if (CookieMode & HT_COOKIE_SAME_HOST) {
336: char * cookie_host = HTCookie_domain(pres);
337: if (cookie_host) {
338: char * addr = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
339: char * host = HTParse(addr, "", PARSE_HOST);
340: if (strcasecomp(host, cookie_host)) {
341: HTTRACE(APP_TRACE, "Cookie...... Host `%s\' doesn't match what is sent in cookie `%s\'\n" _ host _ cookie_host);
342: HT_FREE(addr);
343: continue;
344: }
345: HT_FREE(addr);
346: }
347: }
348:
349: /* Should we prompt the user? */
350: if (CookieMode & HT_COOKIE_PROMPT) {
351: HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
352: if (prompt) {
353: if ((*prompt)(request, HT_A_CONFIRM, HT_MSG_ACCEPT_COOKIE,
354: NULL, NULL, NULL) != YES)
355: continue;
356: } else
357: continue;
358: }
2.1 frystyk 359:
2.2 frystyk 360: /* Call the application with our new cookie */
2.1 frystyk 361: (*SetCookie)(request, pres, SetCookieContext);
362: }
363:
364: /* Delete cookie holder */
365: HTCookieHolder_delete(holder);
366: }
367: }
368: return HT_OK;
369: }
370:
371: /* ------------------------------------------------------------------------- */
372:
373: /*
374: ** Start and stop the cookie engine
375: */
376: PUBLIC BOOL HTCookie_init (void)
377: {
378: if (!baking_cookies) {
379:
380: /* Register the SetCookie header parser */
381: HTHeader_addParser("Set-Cookie", NO, HTCookie_parseSetCookie);
382:
383: /* Register the cookie before and after filters */
384: HTNet_addBefore(HTCookie_beforeFilter, "http://*", NULL, HT_FILTER_MIDDLE);
385: HTNet_addAfter(HTCookie_afterFilter, "http://*", NULL, HT_ALL, HT_FILTER_MIDDLE);
386:
387: baking_cookies = YES;
388: return YES;
389: }
390: return NO;
391: }
392:
393: PUBLIC BOOL HTCookie_terminate (void)
394: {
395: /* Unregister Set-Cookie header parser */
396: HTHeader_deleteParser("Set-Cookie");
397:
398: /* Unregister the cookie before and after filters */
399: HTNet_deleteBefore(HTCookie_beforeFilter);
400: HTNet_deleteAfter(HTCookie_afterFilter);
401:
402: /* Delete all pending cookies */
403: HTCookieHolder_deleteAll();
2.4 ! raff 404:
! 405: baking_cookies = NO;
2.1 frystyk 406: return YES;
407: }
408:
409: PUBLIC BOOL HTCookie_setCookieMode (HTCookieMode mode)
410: {
411: CookieMode = mode;
412: return YES;
2.2 frystyk 413: }
414:
415: PUBLIC HTCookieMode HTCookie_cookieMode (void)
416: {
417: return CookieMode;
2.1 frystyk 418: }
419:
420: /*
421: ** Callbacks can be used by the application to set and retrieve cookies
422: ** from a persistent cookie jar as libwww doesn't provide a persistent
423: ** storage for this kind of thing and they probably should be secured
424: ** anyway.
425: */
426: PUBLIC BOOL HTCookie_setCallbacks (HTCookieSetCallback * setCookie,
427: void * setCookieContext,
428: HTCookieFindCallback * findCookie,
429: void * findCookieContext)
430: {
431: if (setCookie && findCookie) {
432: HTTRACE(APP_TRACE, "Cookie...... Registering cookie callbacks\n");
433: SetCookie = setCookie;
434: SetCookieContext = setCookieContext;
435:
436: FindCookie = findCookie;
437: FindCookieContext = findCookieContext;
438: return YES;
439: }
440: return NO;
441: }
442:
443: PUBLIC BOOL HTCookie_deleteCallbacks (void)
444: {
445: HTTRACE(APP_TRACE, "Cookie...... Unregistering cookie callbacks\n");
446: SetCookie = NULL;
447: SetCookieContext = NULL;
448: FindCookie = NULL;
449: FindCookieContext = NULL;
450: return YES;
451: }
Webmaster