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.
6 **	@(#) $Id$
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? */
54 PRIVATE HTCookieMode CookieMode = HT_COOKIE_PROMPT | HT_COOKIE_ACCEPT | HT_COOKIE_SEND;
55 
56 /* ------------------------------------------------------------------------- */
57 
HTCookie_new(void)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 
HTCookie_delete(HTCookie * me)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 
HTCookie_setName(HTCookie * me,const char * name)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 
HTCookie_name(HTCookie * me)88 PUBLIC char * HTCookie_name (HTCookie * me)
89 {
90     return me ? me->name : NULL;
91 }
92 
HTCookie_setValue(HTCookie * me,const char * value)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 
HTCookie_value(HTCookie * me)102 PUBLIC char * HTCookie_value (HTCookie * me)
103 {
104     return me ? me->value : NULL;
105 }
106 
HTCookie_setDomain(HTCookie * me,const char * domain)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 
HTCookie_domain(HTCookie * me)116 PUBLIC char * HTCookie_domain (HTCookie * me)
117 {
118     return me ? me->domain : NULL;
119 }
120 
HTCookie_setPath(HTCookie * me,const char * path)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 
HTCookie_path(HTCookie * me)130 PUBLIC char * HTCookie_path (HTCookie * me)
131 {
132     return me ? me->path : NULL;
133 }
134 
HTCookie_setExpiration(HTCookie * me,time_t expiration)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 
HTCookie_expiration(HTCookie * me)144 PUBLIC time_t HTCookie_expiration (HTCookie * me)
145 {
146     return me ? me->expiration : -1;
147 }
148 
HTCookie_setSecure(HTCookie * me,BOOL secure)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 
HTCookie_isSecure(HTCookie * me)158 PUBLIC BOOL HTCookie_isSecure (HTCookie * me)
159 {
160     return me ? me->secure : NO;
161 }
162 
163 /* ------------------------------------------------------------------------- */
164 
HTCookieHolder_addCookie(HTRequest * request,HTCookie * cookie)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 
HTCookieHolder_find(HTRequest * request)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 
HTCookieHolder_delete(HTCookieHolder * me)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)))
217 		HTCookie_delete(cookie);
218 	    HTList_delete(me->cookies);
219 	}
220 	HTList_removeObject(cookie_holder, me);
221 	HT_FREE(me);
222 	return YES;
223     }
224     return NO;
225 }
226 
HTCookieHolder_deleteAll(void)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 ** Added By Jesse Morgan <jesse@jesterpm.net> on 2006-05-22
246 ** Splits a KEY=VALUE pair into a KEY and VALUE
247 */
HTCookie_splitPair(char * pair,char ** key,char ** value)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 /*
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 */
HTCookie_parseSetCookie(HTRequest * request,HTResponse * response,char * token,char * value)268 PRIVATE int HTCookie_parseSetCookie (HTRequest * request, HTResponse * response,
269 				     char * token, char * value)
270 
271 {
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 
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))) {
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 
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 */
HTCookie_beforeFilter(HTRequest * request,void * param,int mode)323 PRIVATE int HTCookie_beforeFilter (HTRequest * request, void * param, int mode)
324 {
325     if ((CookieMode & HT_COOKIE_SEND) && FindCookie) {
326 	HTAssocList * cookies = (*FindCookie)(request, FindCookieContext);
327 	if (cookies) {
328 	    HTChunk * cookie_header = HTChunk_new(64);
329 	    HTAssocList * cur = cookies;
330 	    HTAssoc * pres;
331 	    BOOL first=YES;
332 	    while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
333 		if (!first) HTChunk_putc(cookie_header, ';');
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 */
HTCookie_afterFilter(HTRequest * request,HTResponse * response,void * param,int status)353 PRIVATE int HTCookie_afterFilter (HTRequest * request, HTResponse * response,
354 				  void * param, int status)
355 {
356     if ((CookieMode & HT_COOKIE_ACCEPT) && SetCookie) {
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 
363 		/* Should we check to see if hosts match? */
364 		if (CookieMode & (HT_COOKIE_SAME_HOST|HT_COOKIE_SAME_DOMAIN)) {
365 		    char * cookie_host = HTCookie_domain(pres);
366 		    if (cookie_host) {
367 			int res;
368 			char * addr = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
369 			char * host = HTParse(addr, "", PARSE_HOST);
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) {
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 		}
395 
396 		/* Call the application with our new cookie */
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 */
HTCookie_init(void)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 
HTCookie_terminate(void)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();
440 
441     baking_cookies = NO;
442     return YES;
443 }
444 
HTCookie_setCookieMode(HTCookieMode mode)445 PUBLIC BOOL HTCookie_setCookieMode (HTCookieMode mode)
446 {
447     CookieMode = mode;
448     return YES;
449 }
450 
HTCookie_cookieMode(void)451 PUBLIC HTCookieMode HTCookie_cookieMode (void)
452 {
453     return CookieMode;
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 */
HTCookie_setCallbacks(HTCookieSetCallback * setCookie,void * setCookieContext,HTCookieFindCallback * findCookie,void * findCookieContext)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 
HTCookie_deleteCallbacks(void)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 }
488