/* ** Matches MIME constructions for content-types and others like ** them, for example "text/html", "text/plain". It can also match ** wild cards like "text/<star>" and "<star>/<star>. We use <star> ** instead of * in order note to make C like comments :-) */ PUBLIC BOOL HTMIMEMatch (HTAtom * tmplate, HTAtom * actual) { const char *t, *a; char *st, *sa; BOOL match = NO; if (tmplate && actual && (t = HTAtom_name(tmplate))) { if (!strcmp(t, "*")) return YES; if (strchr(t, '*') && (a = HTAtom_name(actual)) && (st = strchr(t, '/')) && (sa = strchr(a,'/'))) { *sa = 0; *st = 0; if ((*(st-1)=='*' && (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) || (*(st+1)=='*' && !strcasecomp(t,a))) match = YES; *sa = '/'; *st = '/'; } } return match; }
/* ** Returns the icon corresponding to content_type or content_encoding. ** If no match is found then use "unknown icon" */ PUBLIC HTIconNode * HTIcon_find (HTFileMode mode, HTFormat content_type, HTEncoding content_encoding) { if (!icon_unknown) icon_unknown = icon_blank; if (mode == HT_IS_FILE) { const char * ct = content_type ? HTAtom_name(content_type) : NULL; const char * ce = content_encoding ? HTAtom_name(content_encoding) : NULL; HTList * cur = icons; HTIconNode * node; while ((node = (HTIconNode*)HTList_nextObject(cur))) { char * slash = strchr(node->type_templ,'/'); if ((ct && slash && match(node->type_templ,ct)) || (ce && !slash && HTStrMatch(node->type_templ,ce))) { return node; } } } else if (mode == HT_IS_DIR) { return icon_dir ? icon_dir : icon_unknown; } else if (mode == HT_IS_BLANK) { return icon_blank ? icon_blank : icon_unknown; } else if (mode == HT_IS_PARENT) { return icon_parent ? icon_parent : icon_unknown; } return icon_unknown; }
/* * Added by [email protected] (94/04/08) */ PRIVATE BOOL lang_match (HTAtom * tmplate, HTAtom * actual) { const char *t, *a; char *st, *sa; BOOL match = NO; if (tmplate && actual && (t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) { st = strchr(t, '_'); sa = strchr(a, '_'); if ((st != NULL) && (sa != NULL)) { if (!strcasecomp(t, a)) match = YES; else match = NO; } else { if (st != NULL) *st = 0; if (sa != NULL) *sa = 0; if (!strcasecomp(t, a)) match = YES; else match = NO; if (st != NULL) *st = '_'; if (sa != NULL) *sa = '_'; } } return match; }
/* Parse a file given format and file pointer ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** */ PUBLIC int HTParseFile ARGS6( HTFormat, format_in, HTFormat, format_out, HTParentAnchor *, anchor, FILE *, fp, HTStream*, sink, int, compressed) { HTStream * stream; HTStreamClass targetClass; stream = HTStreamStack(format_in, format_out, compressed, sink , anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, can't convert from %s to %s.", HTAtom_name(format_in), HTAtom_name(format_out)); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTFormat(in HTParseFile): %s\n", buffer); #endif return HTLoadError(sink, 501, buffer); } targetClass = *(stream->isa); /* Copy pointers to procedures */ HTFileCopy(fp, stream); (*targetClass.end_document)(stream); (*targetClass.free)(stream); return HT_LOADED; }
/* Find the cost of a filter stack ** ------------------------------- ** ** Must return the cost of the same stack which StreamStack would set up. ** ** On entry, ** length The size of the data to be converted */ PUBLIC double HTStackValue (HTList * theseConversions, HTFormat rep_in, HTFormat rep_out, double initial_value, long int length) { int which_list; HTList* conversion[2]; HTTRACE(CORE_TRACE, "StackValue.. Evaluating stream stack for %s worth %.3f to %s\n" _ HTAtom_name(rep_in) _ initial_value _ HTAtom_name(rep_out)); if (rep_out == WWW_SOURCE || rep_out == rep_in) return 0.0; conversion[0] = theseConversions; conversion[1] = HTConversions; for(which_list = 0; which_list<2; which_list++) if (conversion[which_list]) { HTList * cur = conversion[which_list]; HTPresentation * pres; while ((pres = (HTPresentation*)HTList_nextObject(cur))) { if (pres->rep == rep_in && (pres->rep_out == rep_out || HTMIMEMatch(pres->rep_out, rep_out))) { double value = initial_value * pres->quality; if (HTMaxSecs != 0.0) value = value - (length*pres->secs_per_byte + pres->secs) /HTMaxSecs; return value; } } } return NO_VALUE_FOUND; /* Really bad */ }
/* Parse a document in memory given format and memory block pointer ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** State of memory and target stream on entry: ** HTChunk* (chunk) assumed valid, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** -501 Stream stack failed (cannot present or convert). ** HT_LOADED All data sent. ** ** State of memory and target stream on return: ** always chunk unchanged; target freed, aborted, or NULL. */ PUBLIC int HTParseMem ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, HTChunk *, chunk, HTStream *, sink) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(rep_in, format_out, sink, anchor); if (!stream) { char *buffer = 0; HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O, HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE((tfp, "HTFormat(in HTParseMem): %s\n", buffer)); rv = HTLoadError(sink, 501, buffer); FREE(buffer); return rv; } /* Push the data down the stream */ targetClass = *(stream->isa); rv = HTMemCopy(chunk, stream); (*targetClass._free)(stream); return HT_LOADED; }
/* HTGetIcon() ** returns the icon corresponding to content_type or content_encoding. */ PUBLIC HTIconNode * HTGetIcon ARGS3(mode_t, mode, HTFormat, content_type, HTFormat, content_encoding) { if (!icon_unknown) icon_unknown = icon_blank; if ((mode & S_IFMT) == S_IFREG) { char * ct = content_type ? HTAtom_name(content_type) : NULL; char * ce = content_encoding ? HTAtom_name(content_encoding) : NULL; HTList * cur = icons; HTIconNode * node; while ((node = (HTIconNode*)HTList_nextObject(cur))) { char * slash = strchr(node->type_templ,'/'); if ((ct && slash && match(node->type_templ,ct)) || (ce && !slash && HTAA_templateMatch(node->type_templ,ce))) { return node; } } } else if ((mode & S_IFMT) == S_IFDIR) { return icon_dir ? icon_dir : icon_unknown; } else if ((mode & S_IFMT) == S_IFLNK) { return icon_dir ? icon_dir : icon_unknown; /* @@ */ } return icon_unknown; }
/* Create a new coder and insert it into stream chain ** -------------------------------------------------- ** Creating the content decoding stack is not based on quality factors as ** we don't have the freedom as with content types. Specify whether you ** you want encoding or decoding using the BOOL "encode" flag. */ PUBLIC HTStream * HTContentCodingStack (HTEncoding encoding, HTStream * target, HTRequest * request, void * param, BOOL encode) { HTList * coders[2]; HTStream * top = target; HTCoding * pres = NULL; HTCoding * best_match = NULL; double best_quality = -1e30; /* Pretty bad! */ int cnt; if (!encoding || !request) { HTTRACE(CORE_TRACE, "Codings... Nothing applied...\n"); return target ? target : HTErrorStream(); } coders[0] = HTRequest_encoding(request); coders[1] = HTContentCoders; HTTRACE(CORE_TRACE, "C-E......... Looking for `%s\'\n" _ HTAtom_name(encoding)); for (cnt=0; cnt < 2; cnt++) { HTList * cur = coders[cnt]; while ((pres = (HTCoding *) HTList_nextObject(cur))) { if ((pres->encoding == encoding || HTMIMEMatch(pres->encoding, encoding)) && pres->quality > best_quality) { best_match = pres; best_quality = pres->quality; } } } if (best_match) { HTTRACE(CORE_TRACE, "C-E......... Found `%s\'\n" _ HTAtom_name(best_match->encoding)); if (encode) { if (best_match->encoder) top = (*best_match->encoder)(request, param, encoding, top); } else { if (best_match->decoder) top = (*best_match->decoder)(request, param, encoding, top); } } else if (!HTFormat_isUnityContent(encoding)) { /* ** If this is not a unity coding and we didn't find any coders ** that could handle it then put in a local file save stream ** instead of the stream that we got. */ if (encode) { HTTRACE(CORE_TRACE, "C-E......... NOT FOUND - can't encode stream!\n"); } else { HTTRACE(CORE_TRACE, "C-E......... NOT FOUND - error!\n"); top = HTBlackHole(); } } return top; }
PRIVATE BOOL HTRank (HTRequest * request, HTArray * variants) { HTContentDescription * cd; void ** data; if (!variants) { HTTRACE(PROT_TRACE, "Ranking..... No variants\n"); return NO; } /* ** Walk through the list of local and global preferences and find the ** overall q factor for each variant */ cd = (HTContentDescription *) HTArray_firstObject(variants, data); while (cd) { double ctq_local = type_value(cd->content_type, HTRequest_conversion(request)); double ctq_global = type_value(cd->content_type, HTFormat_conversion()); double clq_local = lang_value(cd->content_language, HTRequest_language(request)); double clq_global = lang_value(cd->content_language, HTFormat_language()); double ceq_local = encoding_value(cd->content_encoding, HTRequest_encoding(request)); double ceq_global = encoding_value(cd->content_encoding, HTFormat_contentCoding()); HTTRACE(PROT_TRACE, "Qualities... Content type: %.3f, Content language: %.3f, Content encoding: %.3f\n" _ HTMAX(ctq_local, ctq_global) _ HTMAX(clq_local, clq_global) _ HTMAX(ceq_local, ceq_global)); cd->quality *= (HTMAX(ctq_local, ctq_global) * HTMAX(clq_local, clq_global) * HTMAX(ceq_local, ceq_global)); cd = (HTContentDescription *) HTArray_nextObject(variants, data); } /* Sort the array of all our accepted preferences */ HTArray_sort(variants, VariantSort); /* Write out the result */ #ifdef HTDEBUG if (PROT_TRACE) { int cnt = 1; cd = (HTContentDescription *) HTArray_firstObject(variants, data); HTTRACE(PROT_TRACE, "Ranking.....\n"); HTTRACE(PROT_TRACE, "RANK QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n"); while (cd) { HTTRACE(PROT_TRACE, "%d. %.4f %-20.20s %-8.8s %-10.10s %s\n" _ cnt++ _ cd->quality _ cd->content_type ? HTAtom_name(cd->content_type) : "-" _ cd->content_language?HTAtom_name(cd->content_language):"-" _ cd->content_encoding?HTAtom_name(cd->content_encoding):"-" _ cd->filename ? cd->filename :"-"); cd = (HTContentDescription *) HTArray_nextObject(variants, data); } } #endif /* HTDEBUG */ return YES; }
PRIVATE BOOL better_match (HTFormat f, HTFormat g) { const char *p, *q; if (f && g && (p = HTAtom_name(f)) && (q = HTAtom_name(g))) { int i,j; for(i=0 ; *p; p++) if (*p == '*') i++; for(j=0 ; *q; q++) if (*q == '*') j++; if (i < j) return YES; } return NO; }
/* Parse a file given format and file pointer ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to \n for unix ** when the format is textual. ** ** State of file and target stream on entry: ** FILE* (fp) assumed open, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** -501 Stream stack failed (cannot present or convert). ** -1 Download cancelled. ** HT_NO_DATA Error before any data read. ** HT_PARTIAL_CONTENT Interruption or error after some data read. ** HT_LOADED Normal end of file indication on reading. ** ** State of file and target stream on return: ** always fp still open; target freed, aborted, or NULL. */ PUBLIC int HTParseFile ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, FILE *, fp, HTStream*, sink) { HTStream * stream; HTStreamClass targetClass; int rv; #ifdef SH_EX /* 1998/01/04 (Sun) 16:04:09 */ if (fp == NULL) return HT_LOADED; #endif stream = HTStreamStack(rep_in, format_out, sink, anchor); if (!stream) { char *buffer = 0; if (LYCancelDownload) { LYCancelDownload = FALSE; return -1; } HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O, HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE((tfp, "HTFormat(in HTParseFile): %s\n", buffer)); rv = HTLoadError(sink, 501, buffer); FREE(buffer); return rv; } /* Push the data down the stream ** ** @@ Bug: This decision ought to be made based on "encoding" ** rather than on content-type. @@@ When we handle encoding. ** The current method smells anyway. */ targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTFileCopy(fp, stream); if (rv == -1 || rv == HT_INTERRUPTED) { (*targetClass._abort)(stream, NULL); } else { (*targetClass._free)(stream); } if (rv == -1) return HT_NO_DATA; else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) return HT_PARTIAL_CONTENT; else return HT_LOADED; }
/* Parse a socket given format and file number ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** */ PUBLIC int HTParseSocket ARGS6( HTFormat, format_in, HTFormat, format_out, HTParentAnchor *, anchor, int, file_number, HTStream*, sink, int, compressed) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(format_in, format_out, compressed, sink, anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, can't convert from %s to %s.", HTAtom_name(format_in), HTAtom_name(format_out)); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTFormat: %s\n", buffer); #endif return HTLoadError(sink, 501, buffer); } targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTCopy(file_number, stream, 0); if (rv == -1) { /* handle_interrupt should have been done in HTCopy */ /* (*targetClass.handle_interrupt)(stream); */ return HT_INTERRUPTED; } (*targetClass.end_document)(stream); /* New thing: we force close the data socket here, so that if an external viewer gets forked off in the free method below, the connection doesn't remain upon until the child exits -- which it does if we don't do this. */ NETCLOSE (file_number); (*targetClass.free)(stream); return HT_LOADED; }
/* Determine file format from file name -- string version ** ------------------------------------------------------ */ PUBLIC char *HTFileMimeType ARGS2 ( WWW_CONST char *, filename, WWW_CONST char *, default_type) { HTAtom *pencoding; HTFormat format; int compressed; format = HTFileFormat (filename, &pencoding, HTAtom_for (default_type), &compressed); if (HTAtom_name (format)) return HTAtom_name (format); else return default_type; }
/* Link me Anchor to another given one ** ------------------------------------- */ PUBLIC BOOL HTLink_add (HTAnchor * source, HTAnchor * destination, HTLinkType type, HTMethod method) { if (source && destination) { HTTRACE(ANCH_TRACE, "Link create. from anchor %p to %p with type %s, method %s\n" _ (void *) source _ (void *) destination _ type ? HTAtom_name(type) : "NONE" _ method != METHOD_INVALID ? HTMethod_name(method) : "NONE"); if (!source->mainLink.dest) { source->mainLink.dest = destination; source->mainLink.type = type; source->mainLink.method = method; } else { HTLink * newLink = HTLink_new(); newLink->dest = destination; newLink->type = type; newLink->method = method; if (!source->links) source->links = HTList_new(); HTList_addObject (source->links, newLink); } if (!destination->parent->sources) destination->parent->sources = HTList_new(); HTList_addObject (destination->parent->sources, source); return YES; } else HTTRACE(ANCH_TRACE, "Link........ Bad argument\n"); return NO; }
/* Create a filter stack ** --------------------- ** ** If a wildcard match is made, a temporary HTPresentation ** structure is made to hold the destination format while the ** new stack is generated. This is just to pass the out format to ** MIME so far. Storing the format of a stream in the stream might ** be a lot neater. ** */ PUBLIC HTStream * HTStreamStack ARGS4( HTFormat, rep_in, HTFormat, rep_out, HTStream*, sink, HTParentAnchor*, anchor) { HTPresentation temp; HTPresentation *match; HTStream *result; CTRACE((tfp, "HTFormat: Constructing stream stack for %s to %s\n", HTAtom_name(rep_in), HTAtom_name(rep_out))); /* don't return on WWW_SOURCE some people might like * to make use of the source!!!! LJM */ #if 0 if (rep_out == WWW_SOURCE || rep_out == rep_in) return sink; /* LJM */ #endif if (rep_out == rep_in) { result = sink; } else if ((match = HTFindPresentation(rep_in, rep_out, &temp))) { if (match == &temp) { CTRACE((tfp, "StreamStack: Using %s\n", HTAtom_name(temp.rep_out))); } else { CTRACE((tfp, "StreamStack: found exact match: %s\n", HTAtom_name(match->rep))); } result = (*match->converter)(match, anchor, sink); } else { result = NULL; } if (TRACE) { if (result && result->isa && result->isa->name) { CTRACE((tfp, "StreamStack: Returning \"%s\"\n", result->isa->name)); } else if (result) { CTRACE((tfp, "StreamStack: Returning *unknown* stream!\n")); } else { CTRACE((tfp, "StreamStack: Returning NULL!\n")); CTRACE_FLUSH(tfp); /* a crash may be imminent... - kw */ } } return result; }
/* Create a new transfer coder and insert it into stream chain ** ----------------------------------------------------------- ** Creating the content decoding stack is not based on quality factors as ** we don't have the freedom as with content types. Specify whether you ** you want encoding or decoding using the BOOL "encode" flag. */ PUBLIC HTStream * HTContentTransferCodingStack (HTEncoding encoding, HTStream * target, HTRequest * request, void * param, BOOL encode) { HTList * coders[2]; HTStream * top = target; HTCoding * pres = NULL; int cnt; if (!encoding || !request) { HTTRACE(CORE_TRACE, "C-T-E..... Nothing applied...\n"); return target ? target : HTErrorStream(); } /* ** We use the same encoders/decoders as for Transfer-Encodings */ coders[0] = HTRequest_transfer(request); coders[1] = HTTransferCoders; HTTRACE(CORE_TRACE, "C-T-E....... Looking for %s\n" _ HTAtom_name(encoding)); for (cnt=0; cnt < 2; cnt++) { HTList * cur = coders[cnt]; while ((pres = (HTCoding *) HTList_nextObject(cur))) { if (pres->encoding == encoding) { HTTRACE(CORE_TRACE, "C-T-E....... Found...\n"); if (encode) { if (pres->encoder) top = (*pres->encoder)(request, param, encoding, top); break; } else if (pres->decoder) { top = (*pres->decoder)(request, param, encoding, top); break; } } } } /* ** If this is not a unity coding and we didn't find any coders ** that could handle it then put in a local file save stream ** instead of the stream that we got. */ if (!HTFormat_isUnityTransfer(encoding) && target==top) { if (encode) { HTTRACE(CORE_TRACE, "C-T-E....... NOT FOUND - removing encoding!\n"); HTAnchor_setContentTransferEncoding(HTRequest_anchor(request), NULL); } else { HTTRACE(CORE_TRACE, "C-T-E....... NOT FOUND - error!\n"); top = HTBlackHole(); } } return top; }
/* Find the cost of a filter stack ** ------------------------------- ** ** Must return the cost of the same stack which StreamStack would set up. ** ** On entry, ** length The size of the data to be converted */ PUBLIC float HTStackValue ARGS4( HTFormat, format_in, HTFormat, rep_out, float, initial_value, long int, length) { HTAtom * wildcard = HTAtom_for("*"); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n", HTAtom_name(format_in), initial_value, HTAtom_name(rep_out)); #endif if (rep_out == WWW_SOURCE || rep_out == format_in) return 0.0; if (!HTPresentations) HTFormatInit(); /* set up the list */ { int n = HTList_count(HTPresentations); int i; HTPresentation * pres; for(i=0; i<n; i++) { pres = HTList_objectAt(HTPresentations, i); if (pres->rep == format_in && ( pres->rep_out == rep_out || pres->rep_out == wildcard)) { float value = initial_value * pres->quality; if (HTMaxSecs != 0.0) value = value - (length*pres->secs_per_byte + pres->secs) /HTMaxSecs; return value; } } } return -1e30; /* Really bad */ }
/* Find the cost of a filter stack ** ------------------------------- ** ** Must return the cost of the same stack which StreamStack would set up. ** ** On entry, ** length The size of the data to be converted */ PUBLIC float HTStackValue ARGS4( HTFormat, rep_in, HTFormat, rep_out, float, initial_value, long int, length) { HTAtom * wildcard = WWW_WILDCARD_REP_OUT; CTRACE((tfp, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n", HTAtom_name(rep_in), initial_value, HTAtom_name(rep_out))); if (rep_out == WWW_SOURCE || rep_out == rep_in) return 0.0; /* don't do anymore do it in the Lynx code at startup LJM */ /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */ { int n = HTList_count(HTPresentations); int i; HTPresentation * pres; for (i = 0; i < n; i++) { pres = (HTPresentation *)HTList_objectAt(HTPresentations, i); if (pres->rep == rep_in && (pres->rep_out == rep_out || pres->rep_out == wildcard)) { float value = initial_value * pres->quality; if (HTMaxSecs != 0.0) value = value - (length*pres->secs_per_byte + pres->secs) /HTMaxSecs; return value; } } } return (float) -1e30; /* Really bad */ }
/* Parse a socket given format and file number ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -501 Stream stack failed (cannot present or convert). ** -2 Unexpected disconnect before any data received. ** -1 Stream stack failed (cannot present or convert), or ** Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -501 socket still open, target stream NULL. ** -2 socket still open, target freed. ** -1 socket still open, target stream aborted or NULL. ** otherwise socket closed, target stream freed. */ PUBLIC int HTParseSocket ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, int, file_number, HTStream*, sink) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(rep_in, format_out, sink, anchor); if (!stream) { char *buffer = 0; if (LYCancelDownload) { LYCancelDownload = FALSE; return -1; } HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O, HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE((tfp, "HTFormat: %s\n", buffer)); rv = HTLoadError(sink, 501, buffer); /* returns -501 */ FREE(buffer); } else { /* ** Push the data, don't worry about CRLF we can strip them later. */ targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTCopy(anchor, file_number, NULL, stream); if (rv != -1 && rv != HT_INTERRUPTED) (*targetClass._free)(stream); } return rv; /* Originally: full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */ }
PRIVATE double encoding_value (HTAtom * encoding, HTList * accepted) { if (!encoding) return (1.0); if (accepted) { HTList * cur = accepted; HTAcceptNode * node; HTAcceptNode * wild = NULL; const char * e = HTAtom_name(encoding); if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary")) return (1.0); while ((node = (HTAcceptNode*)HTList_nextObject(cur))) { if (node->atom == encoding) return node->quality; else if (HTMIMEMatch(node->atom, encoding)) wild = node; } if (wild) return wild->quality; else return (0.0); /* Nothing matched */ } return (1.0); /* We accept all encodings */ }
/* ** request_terminater : global filter to delete the requests ** Funtion's type : HTNetAfter */ PRIVATE int terminate_handler (HTRequest * request, HTResponse * response, void * param, int status) { my_headers(request); if (response) { HTPrint ("\tStatus:%d\n\tContent-length:%d\n\tIs Cachable:%c\n\tis Cached:%c\n\tReason: %s\n",\ status, HTResponse_length(response),\ (HTResponse_isCachable(response))?'Y':'N',\ (HTResponse_isCached(response,YES))?'Y':'N', \ (HTResponse_reason(response))?HTResponse_reason(response):"NULL"); HTPrint ("\tFormat : %s \n",(char *)HTAtom_name(HTResponse_format(response))); } else HTPrint ("\tResponse NULL\n"); /* Terminate libwww */ HTProfile_delete(); exit(0); }
PRIVATE int HTGuess_flush (HTStream * me) { if (!me->transparent) { HTResponse * response = me->response; /* ** First we look for magic tokens and evaluate the contents of the buffer ** that we are investigating. */ if (me->cnt) { HTTRACE(STREAM_TRACE, "GUESSING.... Result of content analysis: Text=%d%% Newlines=%d%% Ctrl=%d%% High=%d%%\n" _ (int)(100*me->text_cnt/me->cnt + 0.5) _ (int)(100*me->lf_cnt /me->cnt + 0.5) _ (int)(100*me->ctrl_cnt/me->cnt + 0.5) _ (int)(100*me->high_cnt/me->cnt + 0.5)); } if (!me->ctrl_cnt || me->text_cnt + me->lf_cnt >= 16 * (me->ctrl_cnt + me->high_cnt)) { char *ptr; /* some kind of text */ *me->write_ptr = 0; /* terminate buffer */ if (me->high_cnt > 0) HTResponse_setContentTransferEncoding(response, WWW_CODING_8BIT); else HTResponse_setContentTransferEncoding(response, WWW_CODING_7BIT); if (is_html(me->buffer)) HTResponse_setFormat(response, HTAtom_for("text/html")); else if (!strncmp(me->buffer, "%!", 2)) HTResponse_setFormat(response, HTAtom_for("application/postscript")); else if (strstr(me->buffer, "#define") && strstr(me->buffer, "_width") && strstr(me->buffer, "_bits")) HTResponse_setFormat(response, HTAtom_for("image/x-xbitmap")); else if ((ptr = strstr(me->buffer, "converted with BinHex"))!=NULL) HTResponse_setContentTransferEncoding(response, WWW_CODING_MACBINHEX); else if (!strncmp(me->buffer, "begin ", 6)) HTResponse_setContentTransferEncoding(response, WWW_CODING_BASE64); else HTResponse_setFormat(response, WWW_PLAINTEXT); } else { if (!strncmp(me->buffer, "GIF", 3)) HTResponse_setFormat(response, WWW_GIF); else if (!strncmp(me->buffer, "\377\330\377\340", 4)) HTResponse_setFormat(response, WWW_JPEG); else if (!strcmp(me->buffer, "MM")) /* MM followed by a zero */ HTResponse_setFormat(response, WWW_TIFF); else if (!strncmp(me->buffer, "\211PNG\r\n\032\n", 8)) HTResponse_setFormat(response, WWW_PNG); else if (!strncmp(me->buffer, ".snd", 4)) HTResponse_setFormat(response, WWW_AUDIO); else if (!strncmp(me->buffer, "\037\235", 2)) HTResponse_addEncoding(response, WWW_CODING_COMPRESS); else if (!strncmp(me->buffer, "\037\213", 2)) HTResponse_addEncoding(response, WWW_CODING_GZIP); else HTResponse_setFormat(response, WWW_BINARY); } /* ** If we couldn't find any magic tokens then we try and look at the suffix ** of the URL file name and use our own bindings to see if that gives any ** results. */ if (HTResponse_format(response) == WWW_UNKNOWN) { HTParentAnchor * anchor = HTRequest_anchor(me->request); char * addr = HTAnchor_physical(anchor); HTTRACE(STREAM_TRACE, "GUESSING.... Hmm - trying local bindings\n"); HTBind_getResponseBindings (response, addr); } /* ** If nothing worked then give up and say binary... */ if (HTResponse_format(response) == WWW_UNKNOWN) { HTTRACE(STREAM_TRACE, "GUESSING.... That's it - I'm giving up!\n"); HTResponse_setFormat(response, WWW_BINARY); } HTTRACE(STREAM_TRACE, "Guessed..... Content-Type `%s\'\n" _ HTAtom_name(HTResponse_format(response))); /* ** Set up the new stream stack with the type we figured out */ if ((me->target = HTStreamStack(HTResponse_format(response), me->output_format, me->output_stream, me->request, NO)) == NULL) { HTTRACE(STREAM_TRACE, "HTGuess..... Can't convert media type\n"); me->target = HTErrorStream(); } me->transparent = YES; return PUT_BLOCK(me->buffer, me->cnt); } return HT_OK; }
/* Create a filter stack ** --------------------- ** ** If a wildcard match is made, a temporary HTPresentation ** structure is made to hold the destination format while the ** new stack is generated. This is just to pass the out format to ** MIME so far. Storing the format of a stream in the stream might ** be a lot neater. */ PUBLIC HTStream * HTStreamStack ARGS5( HTFormat, format_in, HTFormat, rep_out, int, compressed, HTStream*, sink, HTParentAnchor*, anchor) { HTAtom * wildcard = HTAtom_for("*"); HTPresentation temp; /* Inherit force_dump_to_file from mo-www.c. */ extern int force_dump_to_file; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "[HTStreamStack] Constructing stream stack for %s to %s\n", HTAtom_name(format_in), HTAtom_name(rep_out)); #endif #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, " Compressed is %d\n", compressed); #endif if (rep_out == WWW_SOURCE || rep_out == format_in) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "[HTStreamStack] rep_out == WWW_SOURCE | rep_out == format_in; returning sink\n"); #endif return sink; } if (!HTPresentations) HTFormatInit(); /* set up the list */ if (force_dump_to_file && format_in != WWW_MIME) { return HTSaveAndExecute (NULL, anchor, sink, format_in, compressed); } { int n = HTList_count(HTPresentations); int i; HTPresentation * pres; for(i=0; i<n; i++) { pres = HTList_objectAt(HTPresentations, i); #ifndef DISABLE_TRACE if (www2Trace) { fprintf (stderr, "HTFormat: looking at pres '%s'\n", HTAtom_name (pres->rep)); if (pres->command) fprintf (stderr, "HTFormat: pres->command is '%s'\n", pres->command); else fprintf (stderr, "HTFormat: pres->command doesn't exist\n"); } #endif if (pres->rep == format_in || partial_wildcard_matches (pres->rep, format_in)) { if (pres->command && strstr (pres->command, "mosaic-internal-present")) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "[HTStreamStack] HEY HEY HEY caught internal-present\n"); #endif return HTPlainPresent (pres, anchor, sink, format_in, compressed); } if (pres->rep_out == rep_out) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "[HTStreamStack] pres->rep_out == rep_out\n"); #endif return (*pres->converter)(pres, anchor, sink, format_in, compressed); } if (pres->rep_out == wildcard) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "[HTStreamStack] pres->rep_out == wildcard\n"); #endif temp = *pres;/* make temp conversion to needed fmt */ temp.rep_out = rep_out; /* yuk */ return (*pres->converter)(&temp, anchor, sink, format_in, compressed); } } } } #ifndef DISABLE_TRACE if (www2Trace) { fprintf (stderr, "[HTStreamStack] Returning NULL at bottom.\n"); } #endif return NULL; }
static int partial_wildcard_matches (HTFormat r1, HTFormat r2) { /* r1 is the presentation format we're currently looking at out of the list we understand. r2 is the one we need to get to. */ char *s1, *s2, *subtype1 = NULL, *subtype2 = NULL; int i; s1 = HTAtom_name (r1); s2 = HTAtom_name (r2); if (!s1 || !s2) return 0; s1 = strdup (s1); s2 = strdup (s2); for (i = 0; i < strlen (s1); i++) if (s1[i] == '/') { s1[i] = '\0'; subtype1 = &(s1[i+1]); /* Now s1 contains the main type and subtype1 contains the subtype. */ goto done1; } done1: if (!subtype1) goto nope; /* Bail if we don't have a wildcard possibility. */ if (subtype1[0] != '*') goto nope; for (i = 0; i < strlen (s2); i++) if (s2[i] == '/') { s2[i] = '\0'; subtype2 = &(s2[i+1]); /* Now s2 contains the main type and subtype2 contains the subtype. */ goto done2; } done2: if (!subtype2) goto nope; /* Bail if s1 and s2 aren't the same and s1[0] isn't '*'. */ if (strcmp (s1, s2) && s1[0] != '*') goto nope; /* OK, so now either we have the same main types or we have a wildcard type for s1. We also know that we have a wildcard possibility in s1. Therefore, at this point, we have a match. */ free (s1); free (s2); return 1; nope: free (s1); free (s2); return 0; }
/* 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 (!HTBindings) HTBind_init(); 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; unsigned char * p; HTTRACE(BIND_TRACE, "Get Binding. Look for '%s\' " _ suffix); sufcnt++; /* Select list from hash table */ for (p=suffix, hash=0; *p; p++) { hash = (hash * 3 + TOLOWER(*p)) % HT_L_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)) { HTTRACE(BIND_TRACE, "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 */ HTTRACE(BIND_TRACE, "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 */ HTTRACE(BIND_TRACE, "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; } HTTRACE(BIND_TRACE, "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; }
PUBLIC BOOL HTAnchor_setLevel (HTParentAnchor * me, HTLevel level) { return HTAnchor_addFormatParam(me, "level", HTAtom_name(level)); }
PUBLIC BOOL HTAnchor_setCharset (HTParentAnchor * me, HTCharset charset) { return HTAnchor_addFormatParam(me, "charset", HTAtom_name(charset)); }
PUBLIC BOOL HTResponse_setCharset (HTResponse * me, HTCharset charset) { return HTResponse_addFormatParam(me, "charset", HTAtom_name(charset)); }
PRIVATE int pumpData (HTStream * me) { HTRequest * request = me->request; HTResponse * response = me->response; HTFormat format = HTResponse_format(response); HTList * te = HTResponse_transfer(response); HTList * ce = HTResponse_encoding(response); long length = HTResponse_length(response); HTStream * BlackHole = HTBlackHole(); BOOL savestream = NO; me->transparent = YES; /* Pump rest of data right through */ /* ** Cache the metainformation in the anchor object by copying ** it from the response object. This we do regardless if ** we have a persistent cache or not as the memory cache will ** use it as well. If we are updating a cache entry using ** byte ranges then we already have the metainformation and ** hence we can ignore the new one as it'd better be the same. */ if (!(me->mode & HT_MIME_PARTIAL) && HTResponse_isCachable(me->response) != HT_NO_CACHE) HTAnchor_update(HTRequest_anchor(request), me->response); /* ** If we asked only to read the header or footer or we used a HEAD ** method then we stop here as we don't expect any body part. */ if (me->mode & (HT_MIME_HEADER | HT_MIME_FOOTER) || HTRequest_method(request) == METHOD_HEAD) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); return HT_LOADED; } /* ** If we are paring a 1xx response then return HT_CONTINUE */ if (me->mode & HT_MIME_CONT) return HT_CONTINUE; /* ** If we get a 101 Protocol Switch then we are done here ** but not done with the response (which we don't know ** how to go about parsing */ if (me->mode & HT_MIME_UPGRADE) { me->hasBody = YES; return HT_OK; } /* ** If there is no content-length, no transfer encoding and no ** content type then we assume that there is no body part in ** the message and we can return HT_LOADED */ { HTHost * host = HTNet_host(me->net); if (length<0 && te==NULL && HTHost_isPersistent(host) && !HTHost_closeNotification(host)) { if (format != WWW_UNKNOWN) { HTTRACE(STREAM_TRACE, "MIME Parser. BAD - there seems to be a body but no length. This must be an HTTP/1.0 server pretending that it is HTTP/1.1\n"); HTHost_setCloseNotification(host, YES); } else { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); HTTRACE(STREAM_TRACE, "MIME Parser. No body in this message\n"); return HT_LOADED; } } } /* ** Deal with the body */ me->hasBody = YES; /* ** Handle any Content Type */ if (!(me->mode & HT_MIME_PARTIAL) && (format != WWW_UNKNOWN || length > 0 || te)) { HTStream * target; HTTRACE(STREAM_TRACE, "Building.... C-T stack from %s to %s\n" _ HTAtom_name(format) _ HTAtom_name(me->target_format)); if ((target = HTStreamStack(format, me->target_format, me->target, request, YES))==BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** Handle any Content Encodings */ HTTRACE(STREAM_TRACE, "Building.... Content-Decoding stack\n"); if (ce) { HTStream * target = HTContentDecodingStack(ce, me->target, request, NULL); if (target == BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** Can we cache the data object? If so then create a T stream and hook it ** into the stream pipe. We do it before the transfer decoding so that we ** don't have to deal with that when we retrieve the object from cache. ** If we are appending to a cache entry then use a different stream than ** if creating a new entry. */ #ifndef NO_CACHE if (HTCacheMode_enabled()) { if (me->mode & HT_MIME_PARTIAL) { HTStream * append = HTStreamStack(WWW_CACHE_APPEND, me->target_format, me->target, request, NO); if (append) me->target = HTTee(me->target, append, NULL); #if 0 /* @@ JK: change */ if (append) me->target = append; #endif } else if (HTResponse_isCachable(me->response) == HT_CACHE_ALL) { HTStream * cache = HTStreamStack(WWW_CACHE, me->target_format, me->target, request, NO); if (cache) me->target = HTTee(me->target, cache, NULL); } } #endif /* ** Handle any Transfer Encodings */ HTTRACE(STREAM_TRACE, "Building.... Transfer-Decoding stack\n"); if (te) { HTStream * target = HTTransferDecodingStack(te, me->target, request, NULL); if (target == BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** If we for some reason couldn't find a target stream */ if (!me->target) me->target = HTBlackHole(); return HT_OK; }
/* PRIVATE multi_match() ** ** Check if actual filename (split in parts) fulfills ** the requirements. */ PRIVATE BOOL multi_match (char ** required, int m, char ** actual, int n) { int c; int i,j; #ifdef VMS for(c=0; c<m && c<n && !strcasecomp(required[c], actual[c]); c++); #else /* not VMS */ for(c=0; c<m && c<n && !strcmp(required[c], actual[c]); c++); #endif /* not VMS */ if (!c) return NO; /* Names differ rigth from start */ for(i=c; i<m; i++) { BOOL found = NO; for(j=c; j<n; j++) { #ifdef VMS if (!strcasecomp(required[i], actual[j])) { #else /* not VMS */ if (!strcmp(required[i], actual[j])) { #endif /* not VMS */ found = YES; break; } } if (!found) return NO; } return YES; } /* ** Get multi-match possibilities for a given file ** ---------------------------------------------- ** On entry: ** path absolute path to one file in a directory, ** may end in .multi. ** On exit: ** returns a list of ContentDesription structures ** describing the mathing files. ** */ PRIVATE HTArray * dir_matches (char * path) { static char * required[MAX_SUFF+1]; static char * actual[MAX_SUFF+1]; int m,n; char * dirname = NULL; char * basename = NULL; int baselen; char * multi = NULL; DIR * dp; struct dirent * dirbuf; HTArray * matches = NULL; #ifdef HT_REENTRANT struct dirent result; /* For readdir_r */ #endif if (!path) return NULL; StrAllocCopy(dirname, path); basename = (strrchr(dirname, '/')); if (!basename) goto dir_match_failed; *basename++ = 0; multi = strrchr(basename, MULTI_SUFFIX[0]); if (multi && !strcasecomp(multi, MULTI_SUFFIX)) *multi = 0; baselen = strlen(basename); m = HTSplitFilename(basename, required); dp = opendir(dirname); if (!dp) { HTTRACE(PROT_TRACE, "Warning..... Can't open directory %s\n" _ dirname); goto dir_match_failed; } matches = HTArray_new(VARIANTS); #ifdef HAVE_READDIR_R_2 while ((dirbuf = (struct dirent *) readdir_r(dp, &result))) { #elif defined(HAVE_READDIR_R_3) while (readdir_r(dp, &result, &dirbuf) == 0) { #else while ((dirbuf = readdir(dp))) { #endif /* HAVE_READDIR_R_2 */ if (!dirbuf->d_ino) continue; /* Not in use */ if (!strcmp(dirbuf->d_name,".") || !strcmp(dirbuf->d_name,"..") || !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE)) continue; /* Use of direct->namlen is only valid in BSD'ish system */ /* Thanks to [email protected] (Chip Rosenthal) */ /* if ((int)(dirbuf->d_namlen) >= baselen) { */ if ((int) strlen(dirbuf->d_name) >= baselen) { n = HTSplitFilename(dirbuf->d_name, actual); if (multi_match(required, m, actual, n)) { HTContentDescription * cd; if ((cd = (HTContentDescription *) HT_CALLOC(1, sizeof(HTContentDescription))) == NULL) HT_OUTOFMEM("dir_matches"); if (HTBind_getFormat(dirbuf->d_name, &cd->content_type, &cd->content_encoding, &cd->content_transfer, &cd->content_language, &cd->quality)) { if (cd->content_type) { if ((cd->filename = (char *) HT_MALLOC(strlen(dirname) + 2 + strlen(dirbuf->d_name))) == NULL) HT_OUTOFMEM("dir_matches"); sprintf(cd->filename, "%s/%s", dirname, dirbuf->d_name); HTArray_addObject(matches, (void *) cd); } else { HT_FREE(cd); } } else { HT_FREE(cd); } } } } closedir(dp); dir_match_failed: HT_FREE(dirname); return matches; } /* ** Get the best match for a given file ** ----------------------------------- ** On entry: ** req->conversions accepted content-types ** req->encodings accepted content-transfer-encodings ** req->languages accepted content-languages ** path absolute pathname of the filename for ** which the match is desired. ** On exit: ** returns a newly allocated absolute filepath. */ PRIVATE char * HTGetBest (HTRequest * req, char * path) { HTArray * variants = NULL; char * representation = NULL; if (!path || !*path) return NULL; if ((variants = dir_matches(path)) == NULL) { HTTRACE(PROT_TRACE, "No matches.. for \"%s\"\n" _ path); return NULL; } #ifdef HTDEBUG if (PROT_TRACE) { void ** data; HTContentDescription * cd = HTArray_firstObject(variants, data); HTTRACE(PROT_TRACE, "Multi....... Possibilities for \"%s\"\n" _ path); HTTRACE(PROT_TRACE, " QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n"); while (cd) { HTTRACE(PROT_TRACE, " %.4f %-20.20s %-8.8s %-10.10s %s\n" _ cd->quality _ cd->content_type ?HTAtom_name(cd->content_type) :"-\t" _ cd->content_language?HTAtom_name(cd->content_language):"-" _ cd->content_encoding?HTAtom_name(cd->content_encoding):"-" _ cd->filename ?cd->filename :"-"); cd = (HTContentDescription *) HTArray_nextObject(variants, data); } } #endif /* HTDEBUG */ /* ** Finally get the best variant which is readable */ if (HTRank(req, variants)) { void ** data; HTContentDescription * cd = HTArray_firstObject(variants, data); while (cd) { if (cd->filename) { if (access(cd->filename, R_OK) != -1) StrAllocCopy(representation, cd->filename); else HTTRACE(PROT_TRACE, "Multi....... `%s\' is not readable\n" _ cd->filename); } HT_FREE(cd->filename); HT_FREE(cd); cd = (HTContentDescription *) HTArray_nextObject(variants, data); } } HTArray_delete(variants); return representation; } PRIVATE int welcome_value (char * name) { HTList * cur = welcome_names; char * welcome; int v = 0; while ((welcome = (char*)HTList_nextObject(cur))) { v++; if (!strcmp(welcome,name)) return v; } return 0; } PRIVATE char * get_best_welcome (char * path) { char * best_welcome = NULL; int best_value = 0; DIR * dp; struct dirent * dirbuf; char * last = strrchr(path, '/'); if (!welcome_names) { HTAddWelcome("Welcome.html"); HTAddWelcome("welcome.html"); #if 0 HTAddWelcome("Index.html"); #endif HTAddWelcome("index.html"); } if (last && last!=path) *last = 0; dp = opendir(path); if (last && last!=path) *last='/'; if (!dp) { HTTRACE(PROT_TRACE, "Warning..... Can't open directory %s\n" _ path); return NULL; } while ((dirbuf = readdir(dp))) { if (!dirbuf->d_ino || !strcmp(dirbuf->d_name,".") || !strcmp(dirbuf->d_name,"..") || !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE)) continue; else { int v = welcome_value(dirbuf->d_name); if (v > best_value) { best_value = v; StrAllocCopy(best_welcome, dirbuf->d_name); } } } closedir(dp); if (best_welcome) { char * welcome; if ((welcome = (char *) HT_MALLOC(strlen(path) + strlen(best_welcome)+2)) == NULL) HT_OUTOFMEM("get_best_welcome"); sprintf(welcome, "%s%s%s", path, last ? "" : "/", best_welcome); HT_FREE(best_welcome); HTTRACE(PROT_TRACE, "Welcome..... \"%s\"\n" _ welcome); return welcome; } return NULL; }