/* ** URL DATA TREE STRUCTURE ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTUTree.c,v 2.4 1998/05/04 19:37:37 frystyk Exp $ ** ** AUTHORS: ** HFN Henrik Frystyk Nielsen ** ** An infobase is a tree structure where we can store all the information ** we know about a remote server. Typically, each remote server will ** have its own infobase which gets richer as we get to know more about ** the remote web site. ** ** This module maintains an URL information database ** which can contain information stored by filters. This can for ** example be used to store challenges received from remote server. ** A infobase has the advantage that it can be searched using URLs _or_ ** using realms. The letter is most useful to "guess" information ** about a remote URL that we haven't seen before */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "HTUTree.h" /* Implemented here */ #define TREE_TIMEOUT 43200L /* Default tree timeout is 12 h */ #define HASH_SIZE 101 /* Arbitrary prime */ struct _HTUTree { /* Server URL info base */ char * name; char * host; int port; HTList * templates; /* List of templates for this tres */ HTList * realms; /* List of realms for this tree */ time_t created; /* Creation time of this object */ HTUTree_gc * gc; /* Contect garbage collector */ }; struct _HTURealm { /* Realm specifics */ char * realm; void * context; HTUTemplate * tm_ptr; }; struct _HTUTemplate { /* Hierarchical information */ char * tmplate; HTURealm * rm_ptr; }; PRIVATE HTList ** InfoTable = NULL; /* List of information bases */ PRIVATE time_t UTreeTimeout = TREE_TIMEOUT; /* ------------------------------------------------------------------------- */ /* ** Create a new realm ** Returns new object or NULL if error */ PRIVATE HTURealm * HTUTree_newRealm (HTUTree * tree, const char * realm, void * context) { if (tree) { HTURealm * me; if ((me = (HTURealm *) HT_CALLOC(1, sizeof(HTURealm))) == NULL) HT_OUTOFMEM("HTURealm_new"); if (realm) StrAllocCopy(me->realm, realm); me->context = context; HTList_addObject(tree->realms, (void *) me); return me; } return NULL; } /* ** Delete a realm. We call the scheme gc callback to free the opaque ** context object. */ PRIVATE BOOL HTUTree_deleteRealm (HTUTree * tree, HTURealm * me) { if (tree && me) { if (tree->gc && me->context) (*tree->gc)(me->context); HTList_removeObject(tree->realms, (void *) me); HT_FREE(me->realm); HT_FREE(me); return YES; } return NO; } /* ** Find a realm */ PRIVATE HTURealm * HTUTree_findRealm (HTUTree * tree, const char * realm) { if (tree && tree->realms && realm) { HTList * cur = tree->realms; HTURealm * pres; while ((pres = (HTURealm *) HTList_nextObject(cur))) { if (!strcmp(pres->realm, realm)) { if (CORE_TRACE) HTTrace("URL Node.... Realm `%s\' found\n", realm); return pres; } } } return NULL; } /* ** Create a new template and add to URL tree ** Returns new object or NULL if error */ PRIVATE HTUTemplate * HTUTree_newTemplate (HTUTree * tree,const char * tmplate) { if (tree && tmplate) { HTUTemplate * me; if ((me = (HTUTemplate *) HT_CALLOC(1, sizeof(HTUTemplate))) == NULL) HT_OUTOFMEM("HTUTemplate_new"); StrAllocCopy(me->tmplate, tmplate); HTList_addObject(tree->templates, (void *) me); return me; } return NULL; } /* ** Delete a template */ PRIVATE BOOL HTUTree_deleteTemplate (HTUTree * tree, HTUTemplate * me) { if (tree && me) { HTList_removeObject(tree->templates, (void *) me); HT_FREE(me->tmplate); HT_FREE(me); return YES; } return NO; } /* ** Find a template */ PRIVATE HTUTemplate * HTUTree_findTemplate (HTUTree * tree, const char * path) { if (tree && tree->templates && path) { HTUTemplate * pres; HTList * cur = tree->templates; while ((pres = (HTUTemplate *) HTList_nextObject(cur))) { if (HTStrMatch(pres->tmplate, path)) { if (CORE_TRACE) HTTrace("URL Node.... Found template `%s\' for for `%s\'\n", pres->tmplate, path); return pres; } } } return NULL; } /* ** Search a URL Tree for a matching template or realm ** Return the opaque context object found or NULL if none ** Please regard this as a first simple attempt - it can be done ** much more efficient! */ PUBLIC void * HTUTree_findNode (HTUTree * tree, const char * realm, const char * path) { HTURealm * rm = HTUTree_findRealm(tree, realm); if (rm) return rm->context; else { HTUTemplate * tm = HTUTree_findTemplate(tree, path); if (tm) return tm->rm_ptr ? tm->rm_ptr->context : NULL; } if (CORE_TRACE) HTTrace("URL Node.... Not found\n"); return NULL; } /* ** Add a new node (a template and a realm) to the tree */ PUBLIC BOOL HTUTree_addNode (HTUTree * tree, const char * realm, const char * path, void * context) { if (tree) { if (realm && path) { /* If both a path and a realm */ HTUTemplate * new_template = HTUTree_newTemplate(tree, path); HTURealm * new_realm = HTUTree_newRealm(tree, realm, context); new_realm->tm_ptr = new_template; new_template->rm_ptr = new_realm; return YES; } else if (realm) { /* If only a realm */ HTUTree_newRealm(tree, realm, context); return YES; } if (CORE_TRACE) HTTrace("URL Node.... At least realm must be present\n"); } return NO; } /* ** Replace node (insert a new context at the same node) */ PUBLIC BOOL HTUTree_replaceNode (HTUTree * tree, const char * realm, const char * path, void * context) { HTURealm * rm = HTUTree_findRealm(tree, realm); if (!rm) { HTUTemplate * tm = HTUTree_findTemplate(tree, path); if (tm) rm = tm->rm_ptr; } if (rm) { if (tree->gc && rm->context) (*tree->gc)(rm->context); rm->context = context; return YES; } if (CORE_TRACE) HTTrace("URL Node.... Not found\n"); return NO; } /* ** Remove a node (a template and a realm) from the tree */ PUBLIC BOOL HTUTree_deleteNode (HTUTree * tree, const char * realm, const char * path) { if (tree) { HTURealm * rm = HTUTree_findRealm(tree, realm); HTUTemplate * tm = rm ? rm->tm_ptr : HTUTree_findTemplate(tree, path); if (!rm) rm = tm ? tm->rm_ptr : NULL; HTUTree_deleteRealm(tree, rm); HTUTree_deleteTemplate(tree, tm); return YES; } return NO; } PRIVATE BOOL delete_tree (HTUTree * tree) { if (tree) { HTList * cur; /* Free all templates */ if ((cur = tree->templates)) { HTUTemplate * pres; while ((pres = (HTUTemplate *) HTList_lastObject(cur))) HTUTree_deleteTemplate(tree, pres); HTList_delete(tree->templates); } /* Free all nodes */ if ((cur = tree->realms)) { HTURealm * pres; while ((pres = (HTURealm *) HTList_lastObject(cur))) HTUTree_deleteRealm(tree, pres); HTList_delete(tree->realms); } HT_FREE(tree->name); HT_FREE(tree->host); HT_FREE(tree); return YES; } return NO; } /* ** Find an existing URL Tree ** Returns tree or NULL if error */ PRIVATE HTUTree * find_tree (const char * name, const char * host, int port, HTList ** hashlist) { HTUTree * pres = NULL; *hashlist = NULL; if (!name || !host) { if (CORE_TRACE) HTTrace("URL Tree.... Bad argument\n"); return NULL; } /* Find a hash for this host */ { int hash = 0; const char * ptr; for (ptr=host; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE); if (!InfoTable) { if ((InfoTable = (HTList **) HT_CALLOC(HASH_SIZE, sizeof(HTList *))) == NULL) HT_OUTOFMEM("HTUTree_find"); } if (!InfoTable[hash]) InfoTable[hash] = *hashlist = HTList_new(); else *hashlist = InfoTable[hash]; } /* Search the existing list to see if we already have this entry */ { HTList * cur = *hashlist; while ((pres = (HTUTree *) HTList_nextObject(cur))) { if (!strcmp(pres->name, name) && !strcmp(pres->host, host) && pres->port==port) { if (time(NULL) > pres->created + UTreeTimeout) { if (CORE_TRACE) HTTrace("URL Tree.... Collecting URL Tree %p\n", pres); HTList_removeObject(*hashlist, pres); delete_tree(pres); pres = NULL; } return pres; } } } return NULL; } /* ** Create a new URL Tree (or return an aready existing one) ** Returns new object (or the one found) or NULL if error */ PUBLIC HTUTree * HTUTree_new (const char * name, const char * host, int port, HTUTree_gc * gc) { if (name && host) { HTList * hashlist = NULL; HTUTree * pres = find_tree(name, host, port, &hashlist); /* If not found (or gc'ed) then create a new URL tree */ if (!pres) { if ((pres = (HTUTree *) HT_CALLOC(1, sizeof(HTUTree))) == NULL) HT_OUTOFMEM("HTUTree_new"); StrAllocCopy(pres->name, name); StrAllocCopy(pres->host, host); pres->port = (port > 0 ? port : 80); pres->templates = HTList_new(); pres->realms = HTList_new(); pres->created = time(NULL); pres->gc = gc; /* Add the new URL tree to the hash table */ HTList_addObject(hashlist, (void *) pres); if (CORE_TRACE)HTTrace("URL Tree.... Created %p with name `%s\'\n", pres, pres->name); } else { if (CORE_TRACE) HTTrace("URL Tree.... Found %p with name `%s\'\n", pres, pres->name); } return pres; } else { if (CORE_TRACE) HTTrace("URL Tree.... Bad argument\n"); return NULL; } } /* ** Find a URL tree */ PUBLIC HTUTree * HTUTree_find (const char * name, const char * host, int port) { if (name && host) { HTList * hashlist = NULL; HTUTree * pres = find_tree(name, host, port, &hashlist); if (CORE_TRACE) HTTrace("URL Tree.... did %sfind `%s\'\n", pres ? "" : "NOT ", name); return pres; } else { if (CORE_TRACE) HTTrace("URL Tree.... Bad augument\n"); } return NULL; } /* ** Delete a complete server tree and everything within it. */ PUBLIC BOOL HTUTree_delete (const char * name, const char * host, int port) { if (name && host) { HTList * hashlist = NULL; HTUTree * pres = find_tree(name, host, port, &hashlist); if (pres) { HTList_removeObject(hashlist, pres); delete_tree(pres); if (CORE_TRACE) HTTrace("URL Tree.... deleted %p\n", pres); return YES; } } return NO; } /* ** Delete all URL Trees */ PUBLIC BOOL HTUTree_deleteAll (void) { if (InfoTable) { int cnt; HTList * cur; for (cnt=0; cnt