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