/* Htbind.c ** FILE SUFFIX BIND MANAGER ** ** (c) COPYRIGHT MIT 1995 ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTBind.c,v 2.26 1998/05/04 19:36:17 frystyk Exp $ ** ** This module sets up the binding between a file Bind and a media ** type, language, encoding etc. In a client application the Binds ** are used in protocols that does not support media types etc., like ** FTP, and in server applications they are used to make the bindings ** between the server and the local file store that the server can ** serve to the rest of the world (well almost). The HTFormat module ** holds this information against the accept headers received in a ** request and uses if for format negotiation. All the binding management ** can all be replace by a database interface. ** ** History: ** Feb 91 Written Tim Berners-Lee CERN/CN ** Apr 91 vms-vms access included using DECnet syntax ** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP. ** Fixed access bug for relative names on VMS. ** Sep 93 (MD) Access to VMS files allows sharing. ** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C ** 22 Feb 94 (MD) Excluded two routines if we are not READING directories ** 18 May 94 (HF) Directory stuff removed and stream handling updated, ** error messages introduced etc. ** 10 Maj 95 HF Spawned off from HTFile in order to make it easier to ** override by a new module. It's now based on anchors ** and hash tables ** Bugs: */ /* Library Includes */ #include "wwwsys.h" #include "WWWUtil.h" #include "HTAnchor.h" #include "HTResponse.h" #include "HTParse.h" #include "HTBind.h" /* Implemented here */ typedef struct _HTBind { char * suffix; HTFormat type; /* Content-Type */ HTEncoding encoding; /* Content-Encoding */ HTEncoding transfer; /* Content-Transfer-Encoding */ HTLanguage language; /* Content-Language */ double quality; } HTBind; #define HASH_SIZE 101 /* Arbitrary prime. Memory/speed tradeoff */ /* Suffix registration */ PRIVATE BOOL HTCaseSen = YES; /* Are suffixes case sensitive */ PRIVATE char *HTDelimiters = NULL; /* Set of suffixes */ PRIVATE HTList **HTBindings = NULL; /* Point to table of lists of bindings */ PRIVATE HTBind no_suffix = { "*", NULL, NULL, NULL, NULL, 0.5 }; PRIVATE HTBind unknown_suffix = { "*.*", NULL, NULL, NULL, NULL, 0.5 }; /* ------------------------------------------------------------------------- */ /* ** Set up the list of suffix bindings. Done by HTLibInit */ PUBLIC BOOL HTBind_init (void) { if (!HTBindings) { if (!(HTBindings = (HTList **) HT_CALLOC(HASH_SIZE, sizeof(HTList *)))) HT_OUTOFMEM("HTBind_init"); } StrAllocCopy(HTDelimiters, DEFAULT_SUFFIXES); no_suffix.type = WWW_UNKNOWN; no_suffix.encoding = WWW_CODING_BINARY; unknown_suffix.type = WWW_UNKNOWN; unknown_suffix.encoding = WWW_CODING_BINARY; return YES; } /* ** Cleans up the memory allocated by file bindings ** Done by HTLibTerminate(). ** Written by Eric Sink, eric@spyglass.com, and Henrik */ PUBLIC BOOL HTBind_deleteAll (void) { int cnt; HTList *cur; if (!HTBindings) return NO; for (cnt=0; cntsuffix); HT_FREE(pres); } } HTList_delete(HTBindings[cnt]); HTBindings[cnt] = NULL; } HT_FREE(HTBindings); HT_FREE(HTDelimiters); return YES; } /* Make suffix bindings case sensitive ** ----------------------------------- */ PUBLIC void HTBind_caseSensitive (BOOL sensitive) { HTCaseSen = sensitive; } /* Get set of suffixes ** ------------------- */ PUBLIC const char *HTBind_delimiters (void) { return HTDelimiters; } /* Change set of suffixes ** ---------------------- */ PUBLIC void HTBind_setDelimiters (const char * new_suffixes) { if (new_suffixes && *new_suffixes) StrAllocCopy(HTDelimiters, new_suffixes); } /* Define the representation associated with a file suffix ** ------------------------------------------------------- ** ** Calling this with suffix set to "*" will set the default ** representation. ** Calling this with suffix set to "*.*" will set the default ** representation for unknown suffix files which contain a "." ** ** If filename suffix is already defined its previous ** definition is overridden (or modified) */ PUBLIC BOOL HTBind_addType (const char * suffix, const char * representation, double value) { return HTBind_add(suffix, representation, NULL, NULL, NULL, value); } PUBLIC BOOL HTBind_addEncoding (const char * suffix, const char * encoding, double value) { return HTBind_add(suffix, NULL, encoding, NULL, NULL, value); } PUBLIC BOOL HTBind_addTransfer (const char * suffix, const char * transfer, double value) { return HTBind_add(suffix, NULL, NULL, transfer, NULL, value); } PUBLIC BOOL HTBind_addLanguage (const char * suffix, const char * language, double value) { return HTBind_add(suffix, NULL, NULL, NULL, language, value); } PUBLIC BOOL HTBind_add (const char * suffix, const char * representation, const char * encoding, const char * transfer, const char * language, double value) { HTBind * suff; if (!suffix) return NO; if (!strcmp(suffix, "*")) suff = &no_suffix; else if (!strcmp(suffix, "*.*")) suff = &unknown_suffix; else { HTList *suflist; int hash=0; const char *ptr=suffix; /* Select list from hash table */ for( ; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char*)ptr)) % HASH_SIZE); if (!HTBindings[hash]) HTBindings[hash] = HTList_new(); suflist = HTBindings[hash]; /* Look for existing binding */ { HTList *cur = suflist; while ((suff = (HTBind *) HTList_nextObject(cur)) != NULL) { if (!strcmp(suff->suffix, suffix)) break; } } /* If not found -- create a new node */ if (!suff) { if ((suff = (HTBind *) HT_CALLOC(1, sizeof(HTBind))) == NULL) HT_OUTOFMEM("HTBind_add"); HTList_addObject(suflist, (void *) suff); StrAllocCopy(suff->suffix, suffix); } } /* Set the appropriate values */ { HTChunk * chunk = HTChunk_new(32); char *ptr; if (representation) { HTChunk_puts(chunk, representation); ptr = HTChunk_data(chunk); for (; *ptr; ptr++) *ptr = TOLOWER(*ptr); suff->type = HTAtom_for(HTChunk_data(chunk)); HTChunk_clear(chunk); } if (encoding) { HTChunk_puts(chunk, encoding); ptr = HTChunk_data(chunk); for (; *ptr; ptr++) *ptr = TOLOWER(*ptr); suff->encoding = HTAtom_for(HTChunk_data(chunk)); HTChunk_clear(chunk); } if (transfer) { HTChunk_puts(chunk, transfer); ptr = HTChunk_data(chunk); for (; *ptr; ptr++) *ptr = TOLOWER(*ptr); suff->transfer = HTAtom_for(HTChunk_data(chunk)); HTChunk_clear(chunk); } if (language) { HTChunk_puts(chunk, language); ptr = HTChunk_data(chunk); for (; *ptr; ptr++) *ptr = TOLOWER(*ptr); suff->language = HTAtom_for(HTChunk_data(chunk)); HTChunk_clear(chunk); } HTChunk_delete(chunk); suff->quality = value; } return YES; } /* Determine a suitable suffix ** --------------------------- ** Use the set of bindings to find a suitable suffix (or index) ** for a certain combination of language, media type and encoding ** given in the anchor. ** ** Returns a pointer to a suitable suffix string that must be freed ** by the caller. If more than one suffix is found they are all ** concatenated using the first delimiter in HTDelimiters. ** If no suffix is found, NULL is returned. */ PUBLIC char * HTBind_getSuffix (HTParentAnchor * anchor) { int cnt; HTList * cur; HTChunk * suffix = HTChunk_new(48); char delimiter = *HTDelimiters; BOOL ct=NO, ce=NO, cl=NO; HTFormat format = HTAnchor_format(anchor); HTList * encoding = HTAnchor_encoding(anchor); HTList * language = HTAnchor_language(anchor); if (anchor) { for (cnt=0; cnttype && pres->type == format)){ HTChunk_putc(suffix, delimiter); HTChunk_puts(suffix, pres->suffix); ct = YES; } else if (!ce && pres->encoding && encoding) { /* @@@ Search list @@@ */ ce = YES; } else if (!cl && pres->language && language) { /* @@@ Search list @@@ */ cl = YES; } } } } } return HTChunk_toCString(suffix); } /* ** Use the set of bindings to find the combination of language, ** media type and encoding of a given object. This information can either be ** stored in the anchor obejct or in the response object depending on which ** function is called. ** ** We comprise here as bindings only can have one language and one encoding. ** If more than one suffix is found they are all searched. The last suffix ** has highest priority, the first one lowest. See also HTBind_getFormat() */ PUBLIC BOOL HTBind_getAnchorBindings (HTParentAnchor * anchor) { BOOL status = NO; double quality=1.0; /* @@@ Should we add this into the anchor? */ if (anchor) { char *addr = HTAnchor_physical(anchor); char *path = HTParse(addr, "", PARSE_PATH+PARSE_PUNCTUATION); char *file; char *end; if ((end = strchr(path, ';')) || (end = strchr(path, '?')) || (end = strchr(path, '#'))) *end = '\0'; if ((file = strrchr(path, '/'))) { HTFormat format = NULL; HTEncoding encoding = NULL; HTEncoding transfer = NULL; HTLanguage language = NULL; if (BIND_TRACE) HTTrace("Anchor...... Get bindings for `%s\'\n", path); status = HTBind_getFormat(file, &format, &encoding, &transfer, &language, &quality); if (status) { HTAnchor_setFormat(anchor, format); HTAnchor_setContentTransferEncoding(anchor, transfer); HTAnchor_addEncoding(anchor, encoding); HTAnchor_addLanguage(anchor, language); } } HT_FREE(path); } return status; } PUBLIC BOOL HTBind_getResponseBindings (HTResponse * response, const char * url) { BOOL status = NO; double quality = 1.0; if (response) { char * path = HTParse(url, "", PARSE_PATH + PARSE_PUNCTUATION); char * file; char * end; if ((end = strchr(path, ';')) || (end = strchr(path, '?')) || (end = strchr(path, '#'))) *end = '\0'; if ((file = strrchr(path, '/'))) { HTFormat format = NULL; HTEncoding encoding = NULL; HTEncoding transfer = NULL; HTLanguage language = NULL; if (BIND_TRACE) HTTrace("Response.... Get Bindings for `%s\'\n", path); status = HTBind_getFormat(file, &format, &encoding, &transfer, &language, &quality); if (status) { HTResponse_setFormat(response, format); HTResponse_setContentTransferEncoding(response, transfer); HTResponse_addEncoding(response, encoding); #if 0 HTResponse_addLanguage(response, language); #endif } } HT_FREE(path); } return status; } /* Determine the content of an file name ** ------------------------------------- ** Use the set of bindings to find the combination of language, ** media type, encoding, and transfer encoding of a given anchor. ** If more than one suffix is found they are all searched. The last suffix ** has highest priority, the first one lowest. See also HTBind_getBindings() ** Either of format, encoding, or language can be NULL ** Returns the format, encoding, and language found */ PUBLIC BOOL HTBind_getFormat (const char * filename, HTFormat * format, HTEncoding * enc, HTEncoding * cte, HTLanguage * lang, double * quality) { int sufcnt=0; char *file=NULL; #ifdef HT_REENTRANT char *lasts; /* For strtok_r */ #endif if (*quality < HT_EPSILON) *quality = 1.0; /* Set to a neutral value */ StrAllocCopy(file, filename); HTUnEscape(file); /* Unescape the file name */ #ifdef HT_REENTRANT if (strtok_r(file, HTDelimiters, &lasts)) { /* Do we have any suffixes? */ #else if (strtok(file, HTDelimiters)) { /* Do we have any suffixes? */ #endif /* HT_REENTRANT */ char *suffix; #ifdef HT_REENTRANT while ((suffix=(char*)strtok_r(NULL, HTDelimiters, &lasts)) != NULL) { #else while ((suffix=strtok(NULL, HTDelimiters)) != NULL) { #endif /* HT_REENTRANT */ HTBind *suff=NULL; int hash=0; char *ptr=suffix; if (BIND_TRACE) HTTrace("Get Binding. Look for '%s\' ", suffix); sufcnt++; /* Select list from hash table */ for( ; *ptr; ptr++) hash = (int)((hash*3+(*(unsigned char*)ptr)) % HASH_SIZE); /* Now search list for entries (case or non case sensitive) */ if (HTBindings[hash]) { HTList *cur = HTBindings[hash]; while ((suff = (HTBind *) HTList_nextObject(cur))) { if ((HTCaseSen && !strcmp(suff->suffix, suffix)) || !strcasecomp(suff->suffix, suffix)) { if (BIND_TRACE) HTTrace("Found!\n"); if (suff->type && format) *format = suff->type; if (suff->encoding && enc) *enc = suff->encoding; if (suff->transfer && cte) *cte = suff->transfer; if (suff->language && lang) *lang = suff->language; if (suff->quality > HT_EPSILON) *quality *= suff->quality; break; } } } if (!suff) { /* We don't have this suffix - use default */ if (BIND_TRACE) HTTrace("Not found - use default for \'*.*\'\n"); if (format) *format = unknown_suffix.type; if (enc) *enc = unknown_suffix.encoding; if (cte) *cte = unknown_suffix.transfer; if (lang) *lang = unknown_suffix.language; *quality = unknown_suffix.quality; } } /* while we still have suffixes */ } if (!sufcnt) { /* No suffix so use default value */ if (BIND_TRACE) HTTrace("Get Binding. No suffix found - using default '%s\'\n", filename); if (format) *format = no_suffix.type; if (enc) *enc = no_suffix.encoding; if (cte) *cte = no_suffix.transfer; if (lang) *lang = no_suffix.language; *quality = no_suffix.quality; } if (BIND_TRACE) HTTrace("Get Binding. Result for '%s\' is: type='%s\', encoding='%s\', cte='%s\', language='%s\' with quality %.2f\n", filename, (format && *format) ? HTAtom_name(*format) : "unknown", (enc && *enc) ? HTAtom_name(*enc) : "unknown", (cte && *cte) ? HTAtom_name(*cte) : "unknown", (lang && *lang) ? HTAtom_name(*lang) : "unknown", *quality); HT_FREE(file); return YES; }