Annotation of libwww/Library/src/HTAAUtil.c, revision 2.19

2.9       frystyk     1: /*                                                                  HTAAUtil.c
                      2: **     COMMON PARTS OF ACCESS AUTHORIZATION MODULE
                      3: **     FOR BOTH SERVER AND BROWSER
                      4: **
2.13      frystyk     5: **     (c) COPYRIGHT MIT 1995.
2.9       frystyk     6: **     Please first read the full copyright statement in the file COPYRIGH.
2.1       luotonen    7: **
2.19    ! frystyk     8: **     The authentication information is stored in a list of authentication
        !             9: **     data bases, each uniquely identified by a hostname and a port number.
        !            10: **     Each data base contains a set of templates which can be used to predict
        !            11: **     what information to use in a hierarchical tree. All authentication
        !            12: **     dependent information is stored as opaque data in a anode. Normally
        !            13: **     a server application would only keep one auth base but if it wants
        !            14: **     different protection setup as a function of different interfaces then
        !            15: **     it can have one auth base representing each interface. For example a
        !            16: **     server with interfaces "www.foo.com" and "internal.foo.com" can have
        !            17: **     different protection setups for each interface.
2.1       luotonen   18: **
                     19: ** AUTHORS:
                     20: **     AL      Ari Luotonen    luotonen@dxcern.cern.ch
2.3       duns       21: **     MD      Mark Donszelmann    duns@vxdeop.cern.ch
2.19    ! frystyk    22: **     HFN     Henrik Frystyk
2.1       luotonen   23: **
                     24: ** HISTORY:
2.4       luotonen   25: **      8 Nov 93  MD   (VMS only) Added case insensitive comparison
                     26: **                     in HTAA_templateCaseMatch
2.1       luotonen   27: */
                     28: 
2.11      frystyk    29: /* Library include files */
                     30: #include "tcp.h"
2.1       luotonen   31: #include "HTUtils.h"
2.11      frystyk    32: #include "HTString.h"
2.19    ! frystyk    33: #include "HTParse.h"
        !            34: #include "HTReqMan.h"
        !            35: #include "HTAssoc.h"
        !            36: #include "HTAAUtil.h"                                   /* Implemented here */
        !            37: 
        !            38: typedef struct _HTAuthScheme {
        !            39:     char *             scheme;
        !            40:     HTAuthParCallback *        parser;
        !            41:     HTAuthGenCallback *        generator;
        !            42:     HTAuthGcCallback * gc;
        !            43: } HTAuthScheme;
        !            44: 
        !            45: PRIVATE HTList * HTSchemes;    /* List of registered authentication schemes */
        !            46: 
        !            47: typedef struct _HTABase {                /* Server Authentication info base */
        !            48:     char *     host;
        !            49:     int                port;
        !            50:     HTList *   templates;                /* List of templates for this base */
        !            51:     HTList *   nodes;                        /* List of nodes for this base */
        !            52: } HTABase;
        !            53: 
        !            54: typedef struct _HTANode {                /* Authentication scheme specifics */
        !            55:     char *     realm;
        !            56:     char *     scheme;
        !            57:     void *     data;
        !            58: } HTANode;
        !            59: 
        !            60: typedef struct _HTATemplate {                   /* Hierarchical information */
        !            61:     char *     tmplate;
        !            62:     HTANode *  node;
        !            63: } HTATemplate;
        !            64: 
        !            65: PRIVATE HTList * AuthBases = NULL;           /* Current authentication base */
        !            66: 
        !            67: /* ------------------------------------------------------------------------- */
        !            68: /*                          AUTHENTICATION SCHEMES                          */
        !            69: /* ------------------------------------------------------------------------- */
        !            70: 
        !            71: /*     HTAuthCall_add
        !            72: **     --------------
        !            73: **     Register a callback functions that is to be called when we want to
        !            74: **     parse challenges and to generate credentials - or the other way round.
        !            75: **     If you are a server then you want to do the latter and if you are a 
        !            76: **     client then you want to do the former.
        !            77: */
        !            78: PUBLIC BOOL HTAuthCall_add (CONST char *       scheme,
        !            79:                            HTAuthParCallback * parser,
        !            80:                            HTAuthGenCallback * generator,
        !            81:                            HTAuthGcCallback *  gc)
        !            82: {
        !            83:     if (scheme && parser && generator && gc) {
        !            84:        HTAuthScheme * me;
        !            85:        if (AUTH_TRACE)
        !            86:            HTTrace("Auth add.... %s with parser %p and generator %p\n",
        !            87:                    scheme, (void *) parser, (void *) generator);
        !            88:        if ((me = (HTAuthScheme *) HT_CALLOC(1, sizeof(HTAuthScheme))) == NULL)
        !            89:            HT_OUTOFMEM("HTAuthCall_add");
        !            90:        StrAllocCopy(me->scheme, scheme);
        !            91:        me->parser = parser;
        !            92:        me->generator = generator;
        !            93:        me->gc = gc;
        !            94:        if (!HTSchemes) HTSchemes = HTList_new();       
        !            95:        return HTList_addObject(HTSchemes, (void *) me);
        !            96:     }
        !            97:     return NO;
        !            98: }
2.1       luotonen   99: 
2.19    ! frystyk   100: /*     HTAuthCall_delete
        !           101: **     -------------------
        !           102: **     Unregister a authentication scheme from the list
        !           103: **     Return YES if OK, else NO
        !           104: */
        !           105: PUBLIC BOOL HTAuthCall_delete (CONST char * scheme)
        !           106: {
        !           107:     HTList * cur = HTSchemes;
        !           108:     if (scheme && cur) {
        !           109:        HTAuthScheme * pres;
        !           110:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
        !           111:            if (!strcmp(scheme, pres->scheme)) {
        !           112:                HTList_removeObject(HTSchemes, (void *) pres);
        !           113:                HT_FREE(pres->scheme);
        !           114:                HT_FREE(pres);
        !           115:                return YES;
        !           116:            }
        !           117:        }
        !           118:     }
        !           119:     return NO;
        !           120: }
2.1       luotonen  121: 
2.19    ! frystyk   122: /*     HTAuthCall_deleteAll
        !           123: **     ----------------------
        !           124: **     Unregisters all call back functions
        !           125: **     Returns YES if OK, else NO
2.1       luotonen  126: */
2.19    ! frystyk   127: PUBLIC BOOL HTAuthCall_deleteAll (void)
2.1       luotonen  128: {
2.19    ! frystyk   129:     HTList * cur = HTSchemes;
        !           130:     if (AUTH_TRACE) HTTrace("Auth delete. all schemes\n");
        !           131:     if (cur) {
        !           132:        HTAuthScheme * pres;
        !           133:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
        !           134:            HT_FREE(pres->scheme);
        !           135:            HT_FREE(pres);
        !           136:        }
        !           137:        HTList_delete(HTSchemes);
        !           138:        HTSchemes = NULL;
        !           139:        return YES;
        !           140:     }
        !           141:     return NO;
        !           142: }
2.1       luotonen  143: 
2.19    ! frystyk   144: /* ------------------------------------------------------------------------- */
        !           145: /*                    AUTHENTICATION INFORMATION DATA BASE                  */
        !           146: /* ------------------------------------------------------------------------- */
        !           147: 
        !           148: /*     This module maintains an authentication information database
        !           149: **     which contains informations for generate either credentials or
        !           150: **     challenges. The database is symmetric for both server and client
        !           151: **     applications and the implementation can be changed independent of the 
        !           152: **     API so if you fell like using a fancy database toolkit then feel free
        !           153: **     to go right ahead :-)
        !           154: */
        !           155: 
        !           156: /*
        !           157: **     Create a new anode
        !           158: **     Returns new object or NULL if error
        !           159: */
        !           160: PRIVATE HTANode * HTANode_new (HTABase * base, CONST char * realm,
        !           161:                               CONST char * scheme, void * data)
        !           162: {
        !           163:     if (base && realm && scheme) {
        !           164:        HTANode * me;
        !           165:        if ((me = (HTANode *) HT_CALLOC(1, sizeof(HTANode))) == NULL)
        !           166:            HT_OUTOFMEM("HTANode_new");
        !           167:        StrAllocCopy(me->realm, realm);
        !           168:        StrAllocCopy(me->scheme, scheme);
        !           169:        me->data = data;
        !           170:        HTList_addObject(base->nodes, (void *) me);
        !           171:        return me;
        !           172:     }
        !           173:     return NULL;
        !           174: }
2.1       luotonen  175: 
2.19    ! frystyk   176: /*
        !           177: **     Delete a node. We call the scheme gc callback to handle the opaque
        !           178: **     data object.
        !           179: */
        !           180: PRIVATE BOOL HTANode_delete (HTABase * base, HTANode * me)
        !           181: {
        !           182:     if (base && me) {
        !           183:        HTAuth_cleanup(me->scheme, me->data);
        !           184:        HT_FREE(me->realm);
        !           185:        HT_FREE(me->scheme);
        !           186:        HTList_removeObject(base->nodes, (void *) me);
        !           187:        HT_FREE(me);
        !           188:        return YES;
2.1       luotonen  189:     }
2.19    ! frystyk   190:     return NO;
2.1       luotonen  191: }
                    192: 
2.19    ! frystyk   193: /*
        !           194: **     Search an authentication base for a matching anode.
        !           195: **     Return the anode object found or NULL if none
        !           196: */
        !           197: PRIVATE HTANode * HTANode_find (HTABase * base, CONST char * realm)
        !           198: {    
        !           199:     if (base && base->nodes && realm) {
        !           200:        HTList * cur = base->nodes;
        !           201:        HTANode * pres;
        !           202:        while ((pres = (HTANode *) HTList_nextObject(cur))) {
        !           203:            if (!strcmp(pres->realm, realm)) return pres;
        !           204:        }
        !           205:     }
        !           206:     return NULL;
        !           207: }
2.1       luotonen  208: 
2.19    ! frystyk   209: /*
        !           210: **     Create a new template and add to authentication base
        !           211: **     Returns new object or NULL if error
        !           212: */
        !           213: PRIVATE HTATemplate * HTATemplate_new (HTABase * base, char * tmplate,
        !           214:                                       HTANode * node)
        !           215: {
        !           216:     if (base && tmplate && node) {
        !           217:        HTATemplate * me;
        !           218:        if ((me = (HTATemplate *) HT_CALLOC(1, sizeof(HTATemplate))) == NULL)
        !           219:            HT_OUTOFMEM("HTATemplate_new");
        !           220:        me->node = node;
        !           221:        me->tmplate = tmplate;
        !           222:        HTList_addObject(base->templates, (void *) me);
        !           223:        return me;
2.1       luotonen  224:     }
2.19    ! frystyk   225:     return NULL;
2.1       luotonen  226: }
                    227: 
2.19    ! frystyk   228: /*
        !           229: **     Delete a template
        !           230: */
        !           231: PRIVATE BOOL HTATemplate_delete (HTABase * base, HTATemplate * me)
        !           232: {
        !           233:     if (base && me) {
        !           234:        HT_FREE(me->tmplate);
        !           235:        HTList_removeObject(base->templates, (void *) me);
        !           236:        HT_FREE(me);
        !           237:        return YES;
        !           238:     }
        !           239:     return NO;
        !           240: }
2.1       luotonen  241: 
2.19    ! frystyk   242: /*
        !           243: **     Search an authentication base for a matching template.
        !           244: **     Return the template object found or NULL if none
        !           245: */
        !           246: PRIVATE HTATemplate * HTATemplate_find (HTABase * base, CONST char *docname)
        !           247: {
        !           248:     if (base && base->templates && docname) {
        !           249:        HTList * cur = base->templates;
        !           250:        HTATemplate * pres;
        !           251:        while ((pres = (HTATemplate *) HTList_nextObject(cur))) {
        !           252:            if (HTStrMatch(pres->tmplate, docname)) {
        !           253:                if (AUTH_TRACE)
        !           254:                    HTTrace("Template.... `%s' matched `%s'\n",
        !           255:                            docname, pres->tmplate);
        !           256:                return pres;
        !           257:            }
        !           258:        }
        !           259:     }
        !           260:     return NULL;
        !           261: }
2.1       luotonen  262: 
2.19    ! frystyk   263: /*
        !           264: **     Search an authentication base for a HTATemplate object in order to
        !           265: **     update it with a new HTANode. We do this whenever we replace a anode
        !           266: **     Return YES if OK, else NO
        !           267: */
        !           268: PRIVATE BOOL HTATemplate_update (HTABase * base, HTANode * old, HTANode *me)
        !           269: {
        !           270:     if (base && old && me) {
        !           271:        HTList * cur = base->templates;
        !           272:        HTATemplate * pres;
        !           273:        while ((pres = (HTATemplate *) HTList_nextObject(cur))) {
        !           274:            if (pres->node == old) {
        !           275:                pres->node = me;
        !           276:                if (AUTH_TRACE) HTTrace("Template.... updating %p\n", pres);
        !           277:                return YES;
        !           278:            }
        !           279:        }
        !           280:     }
        !           281:     return NO;
        !           282: }
2.1       luotonen  283: 
2.19    ! frystyk   284: /*
        !           285: **     Create a new authentication base
        !           286: **     Returns new object or NULL if error
        !           287: */
        !           288: PRIVATE HTABase * HTABase_new (CONST char * host, int port)
        !           289: {
        !           290:     if (host) {
        !           291:        HTABase * me;
        !           292:        if ((me = (HTABase *) HT_CALLOC(1, sizeof(HTABase))) == NULL)
        !           293:            HT_OUTOFMEM("HTABase_new");
        !           294:        StrAllocCopy(me->host, host);
        !           295:        me->port = (port > 0 ? port : 80);
        !           296:        me->templates = HTList_new();
        !           297:        me->nodes = HTList_new();
        !           298:        HTList_addObject(AuthBases, (void *) me);
        !           299:        if (AUTH_TRACE) HTTrace("Auth Base... %p created\n", me);
        !           300:        return me;
        !           301:     }
        !           302:     return NULL;
2.1       luotonen  303: }
                    304: 
2.19    ! frystyk   305: /*
        !           306: **     Delete a complete server tree and everything within it.
        !           307: */
        !           308: PRIVATE BOOL HTABase_delete (HTABase * base)
        !           309: {
        !           310:     if (base) {
        !           311:        HTList * cur;
        !           312: 
        !           313:        /* Free all templates */
        !           314:        if ((cur = base->templates)) {
        !           315:            HTATemplate * pres;
        !           316:            while ((pres = (HTATemplate *) HTList_lastObject(cur)))
        !           317:                HTATemplate_delete(base, pres);
        !           318:            HTList_delete(base->templates);
        !           319:        }
2.1       luotonen  320: 
2.19    ! frystyk   321:        /* Free all nodes */
        !           322:        if ((cur = base->nodes)) {
        !           323:            HTANode * pres;
        !           324:            while ((pres = (HTANode *) HTList_lastObject(cur)))
        !           325:                HTANode_delete(base, pres);
        !           326:            HTList_delete(base->nodes);     
        !           327:        }
2.1       luotonen  328: 
2.19    ! frystyk   329:        HT_FREE(base->host);
        !           330:        HT_FREE(base);
        !           331:        return YES;
        !           332:     }
        !           333:     return NO;
        !           334: }
2.1       luotonen  335: 
                    336: /*
2.19    ! frystyk   337: **     Find a authentication base. Return NULL if not found
2.1       luotonen  338: */
2.19    ! frystyk   339: PRIVATE HTABase * HTABase_find (CONST char * host, int port)
        !           340: {
        !           341:     HTList * cur = AuthBases;
        !           342:     if (port <= 0) port = 80;
        !           343:     if (host && cur) {
        !           344:        HTABase * pres;
        !           345:        while ((pres = (HTABase *) HTList_nextObject(cur))) {
        !           346:            if (pres->port==port && !strcmp(pres->host, host)) return pres;
        !           347:        }
        !           348:     }
        !           349:     return NULL;
        !           350: }
2.1       luotonen  351: 
2.19    ! frystyk   352: /* ------------------------------------------------------------------------- */
        !           353: /*                HANDLING THE AUTHENTICATION INFORMATION BASE              */
        !           354: /* ------------------------------------------------------------------------- */
        !           355: 
        !           356: /*     HTAuthInfo_add
        !           357: **     --------------
        !           358: **     Add an access authentication information node to the database. If
        !           359: **     the entry is already found then it is replaced with the new one.
        !           360: **     The template must follow normal URI syntax but can include a wildcard
        !           361: **     Return YES if added (or replaced), else NO
2.1       luotonen  362: */
2.19    ! frystyk   363: PUBLIC BOOL HTAuthInfo_add (CONST char * scheme, char * url,
        !           364:                            char * realm, void * data)
        !           365: {
        !           366:     HTABase * base;
        !           367:     HTANode * anode;
        !           368:     if (!scheme || !url || !realm || !data) return NO;
        !           369:     if (AUTH_TRACE) HTTrace("Auth base... adding info for `%s'\n", url);
        !           370:     if (!AuthBases) AuthBases = HTList_new();
        !           371: 
        !           372:     /* Find an existing authentication base or create new */
        !           373:     {
        !           374:        char * host = HTParse(url, "", PARSE_HOST);
        !           375:        char * colon = strchr(host, ':');
        !           376:        int port = 80;
        !           377:        if (colon ) {
        !           378:            *(colon++) = '\0';                       /* Chop off port number */
        !           379:            port = atoi(colon);
        !           380:        }
        !           381:        if ((base = HTABase_find(host, port)) == NULL)
        !           382:            base = HTABase_new(host, port);
        !           383:        HT_FREE(host);
        !           384:        if (!base) return NO;                  /* Couldn't create a new base */
        !           385:     }
2.1       luotonen  386: 
2.19    ! frystyk   387:     /*
        !           388:     ** Find a matching anode or create a new one. If we find an existing one
        !           389:     ** we also update the template pointing to the anode. Otherwise we create
        !           390:     ** a new template as well.
        !           391:     */
        !           392:     {
        !           393:        HTANode * old = HTANode_find(base, realm);
        !           394:        anode = HTANode_new(base, realm, scheme, data);
        !           395:        if (old) {
        !           396:            HTATemplate_update(base, old, anode);
        !           397:            HTANode_delete(base, old);
        !           398:        } else {
        !           399:            char * docname = HTParse(url, "", PARSE_PATH);
        !           400:            HTATemplate_new(base, docname, anode);
        !           401:        }
        !           402:     }
        !           403:     return anode ? YES : NO;
        !           404: }
        !           405: 
        !           406: /*     HTAuthInfo_deleteAll
        !           407: **     --------------------
        !           408: **     Remove the Database
        !           409: */
        !           410: PUBLIC BOOL HTAuthInfo_deleteAll (void)
        !           411: {
        !           412:     if (AuthBases) {
        !           413:        HTList * cur = AuthBases;
        !           414:        HTABase * pres;
        !           415:        while ((pres = (HTABase *) HTList_nextObject(cur)))
        !           416:            HTABase_delete(pres);
        !           417:        HTList_delete(AuthBases);
        !           418:        AuthBases = NULL;
        !           419:        return YES;
        !           420:     }
        !           421:     return NO;
        !           422: }
2.1       luotonen  423: 
2.19    ! frystyk   424: /*     AuthInfo_find
        !           425: **     -------------
        !           426: **     Seaches the set of authentication information bases for a match
        !           427: **     In order to find an anode we do the following:
        !           428: **
        !           429: **             1) Find the right auth base
        !           430: **             2) See if there is a realm match
        !           431: **             3) See if there is a template match for URL
2.1       luotonen  432: **
2.19    ! frystyk   433: **     Return the node found else NULL which means that we don't have any
        !           434: **     authentication information to hook on to this request or response
        !           435: */
        !           436: PRIVATE HTANode * HTAuthInfo_find (char * url, char * realm)
        !           437: {
        !           438:     HTABase * base;
        !           439:     HTATemplate * tmplate;
        !           440:     HTANode * anode;
        !           441:     if (AUTH_TRACE)
        !           442:        HTTrace("Auth base... looking for info on `%s'\n", url);
        !           443: 
        !           444:     /* Find an existing authentication base */
        !           445:     {
        !           446:        char * host = HTParse(url, "", PARSE_HOST);
        !           447:        char * colon = strchr(host, ':');
        !           448:        int port = 80;
        !           449:        if (colon ) {
        !           450:            *(colon++) = '\0';                       /* Chop off port number */
        !           451:            port = atoi(colon);
        !           452:        }       
        !           453:        base = HTABase_find(host, port);
        !           454:        HT_FREE(host);
        !           455:        if (base == NULL) return NULL;                     /* Base not found */
        !           456:     }
        !           457: 
        !           458:     /* Do we have a realm to look for? */
        !           459:     if (realm) {
        !           460:        if ((anode = HTANode_find(base, realm)) != NULL) {
        !           461:            if (AUTH_TRACE)
        !           462:                HTTrace("Auth info... found matching realm `%s\'\n", realm);
        !           463:            return anode;
        !           464:        }
        !           465:     }
        !           466: 
        !           467:     /* If no realm or realm not found then look for template */
        !           468:     {
        !           469:        char * docname = HTParse(url, "", PARSE_PATH);
        !           470:        if ((tmplate = HTATemplate_find(base, docname)) != NULL)
        !           471:            anode = tmplate->node;
        !           472:        HT_FREE(docname);
        !           473:        return anode;
        !           474:     }
        !           475:     return NULL;                                                /* No match */
        !           476: }
        !           477: 
        !           478: /* ------------------------------------------------------------------------- */
        !           479: /*                PARSE AND GENERATE CHELLENGES AND CREDENTIALS             */
        !           480: /* ------------------------------------------------------------------------- */
        !           481: 
        !           482: /*     HTAuth_parse
        !           483: **     ------------
        !           484: **     This function looks for a authentication scheme that matches what we
        !           485: **     have in the request object and calls the parser callback function.
        !           486: **     Case is not significant.
        !           487: **     Return YES or whatever callback returns
        !           488: */
        !           489: PUBLIC BOOL HTAuth_parse (HTRequest * request)
        !           490: {
        !           491:     HTList * cur = HTSchemes;
        !           492:     if (request && request->scheme && request->challenge && cur) {
        !           493:        HTAuthScheme * pres;
        !           494:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
        !           495:            if (!strcasecomp(request->scheme, pres->scheme)) {
        !           496:                if (AUTH_TRACE)
        !           497:                    HTTrace("Auth Calling Parser %p\n", pres->parser);
        !           498:                return (*(pres->parser))(request, pres->scheme);
2.1       luotonen  499:            }
2.19    ! frystyk   500:        }
        !           501:     }
        !           502:     if (AUTH_TRACE)HTTrace("Auth Parse.. No challenge or credentials found\n");
        !           503:     return YES;
        !           504: }
        !           505: 
        !           506: /*     HTAuth_generate
        !           507: **     ---------------
        !           508: **     This function looks for a any authentication scheme that protects 
        !           509: **     this resource and calls the generator callback in order to make a
        !           510: **     challenge or setup credentials depending on whether we are a server
        !           511: **     or a client.
        !           512: **     Return YES or whatever callback returns
        !           513: */
        !           514: PUBLIC BOOL HTAuth_generate (HTRequest * request)
        !           515: {
        !           516:     HTList * cur = HTSchemes;
        !           517:     if (request && cur) {
        !           518:        char * url = HTAnchor_physical(request->anchor);
        !           519:        HTANode * node = HTAuthInfo_find(url, request->realm);
        !           520:        if (node && node->data) {
        !           521:            HTAuthScheme * pres;
        !           522:            while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
        !           523:                if (!strcasecomp(node->scheme, pres->scheme)) {
        !           524:                    if (AUTH_TRACE)
        !           525:                        HTTrace("Auth Calling Generator %p\n",pres->generator);
        !           526:                    return (*(pres->generator))(request, node->scheme,
        !           527:                                                node->realm, node->data);
        !           528:                }
2.1       luotonen  529:            }
                    530:        }
2.19    ! frystyk   531:     }
        !           532:     if (AUTH_TRACE)HTTrace("Auth Gen.... No challenge or credentials found\n");
        !           533:     return YES;
2.1       luotonen  534: }
2.6       frystyk   535: 
2.19    ! frystyk   536: /*     HTAuth_cleanup
        !           537: **     --------------
        !           538: **     This function looks for a authentication scheme that matches what we
        !           539: **     have in the request object and calls the cleanup callback function.
        !           540: **     Case is not significant. If the scheme is not registered then
        !           541: **     Return YES if callback found else NO
        !           542: */
        !           543: PUBLIC BOOL HTAuth_cleanup (CONST char * scheme, void * data)
        !           544: {
        !           545:     HTList * cur = HTSchemes;
        !           546:     if (scheme && cur && data) {
        !           547:        HTAuthScheme * pres;
        !           548:        while ((pres = (HTAuthScheme *) HTList_nextObject(cur))) {
        !           549:            if (!strcasecomp(scheme, pres->scheme)) {
        !           550:                if (AUTH_TRACE) HTTrace("Auth Calling gc %p\n", pres->gc);
        !           551:                (*(pres->gc))(scheme, data);
        !           552:                return YES;
        !           553:            }
        !           554:        }
        !           555:     }
        !           556:     return NO;
        !           557: }
2.1       luotonen  558: 

Webmaster