Annotation of libwww/Library/src/HTCookie.c, revision 2.2

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.2     ! frystyk     6: **     @(#) $Id: HTCookie.c,v 2.1 1999/03/24 16:28:16 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(&param_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.1       frystyk   304:                if (!first) HTChunk_putc(cookie_header, ',');
                    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();
                    404:     return YES;
                    405: }
                    406: 
                    407: PUBLIC BOOL HTCookie_setCookieMode (HTCookieMode mode)
                    408: {
                    409:     CookieMode = mode;
                    410:     return YES;
2.2     ! frystyk   411: }
        !           412: 
        !           413: PUBLIC HTCookieMode HTCookie_cookieMode (void)
        !           414: {
        !           415:     return CookieMode;
2.1       frystyk   416: }
                    417: 
                    418: /*
                    419: **  Callbacks can be used by the application to set and retrieve cookies
                    420: **  from a persistent cookie jar as libwww doesn't provide a persistent
                    421: **  storage for this kind of thing and they probably should be secured
                    422: **  anyway.
                    423: */
                    424: PUBLIC BOOL HTCookie_setCallbacks (HTCookieSetCallback *       setCookie,
                    425:                                   void *                       setCookieContext,
                    426:                                   HTCookieFindCallback *       findCookie,
                    427:                                   void *                       findCookieContext)
                    428: {
                    429:     if (setCookie && findCookie) {
                    430:        HTTRACE(APP_TRACE, "Cookie...... Registering cookie callbacks\n");
                    431:        SetCookie = setCookie;
                    432:        SetCookieContext = setCookieContext;
                    433: 
                    434:        FindCookie = findCookie;
                    435:        FindCookieContext = findCookieContext;
                    436:        return YES;
                    437:     }
                    438:     return NO;
                    439: }
                    440: 
                    441: PUBLIC BOOL HTCookie_deleteCallbacks (void)
                    442: {
                    443:     HTTRACE(APP_TRACE, "Cookie...... Unregistering cookie callbacks\n");
                    444:     SetCookie = NULL;
                    445:     SetCookieContext = NULL;
                    446:     FindCookie = NULL;
                    447:     FindCookieContext = NULL;
                    448:     return YES;
                    449: }

Webmaster