Annotation of libwww/Library/src/HTMulti.c, revision 2.31
2.29 frystyk 1: /*
2: ** CONTENT NEGOTIATION
2.11 frystyk 3: **
2.15 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.11 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.31 ! frystyk 6: ** @(#) $Id: HTMulti.c,v 2.30 1996/08/08 02:16:51 frystyk Exp $
2.1 luotonen 7: **
8: ** History:
9: ** March 94 AL Separated from HTFile.c because
10: ** multiformat handling would be a mess in VMS.
11: */
12:
2.13 frystyk 13: /* Library include files */
2.26 frystyk 14: #include "sysdep.h"
2.28 frystyk 15: #include "WWWUtil.h"
16: #include "WWWCore.h"
2.2 duns 17: #include "HTMulti.h"
2.20 frystyk 18: #include "HTFile.h"
2.1 luotonen 19:
2.29 frystyk 20: #define MULTI_SUFFIX ".multi"/* Extension for scanning formats */
21: #define MAX_SUFF 15 /* Maximum number of suffixes for a file */
22: #define VARIANTS 4 /* We start with this array size */
23:
24: typedef struct _HTContentDescription {
25: char * filename;
26: HTFormat content_type;
27: HTLanguage content_language;
28: HTEncoding content_encoding;
29: HTEncoding content_transfer;
30: int content_length;
31: double quality;
32: } HTContentDescription;
33:
2.3 luotonen 34: PRIVATE HTList * welcome_names = NULL; /* Welcome.html, index.html etc. */
35:
2.29 frystyk 36: /* ------------------------------------------------------------------------- */
37:
38: /*
39: ** Sort the q values in descending order
40: */
41: PRIVATE int VariantSort (const void * a, const void * b)
42: {
43: HTContentDescription * aa = *(HTContentDescription **) a;
44: HTContentDescription * bb = *(HTContentDescription **) b;
45: return (aa->quality > bb->quality) ? -1 : 1;
46: }
47:
48: /*
49: * Added by takada@seraph.ntt.jp (94/04/08)
50: */
51: PRIVATE BOOL lang_match (HTAtom * tmplate, HTAtom * actual)
52: {
53: const char *t, *a;
54: char *st, *sa;
55: BOOL match = NO;
56:
57: if (tmplate && actual &&
58: (t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) {
59: st = strchr(t, '_');
60: sa = strchr(a, '_');
61: if ((st != NULL) && (sa != NULL)) {
62: if (!strcasecomp(t, a))
63: match = YES;
64: else
65: match = NO;
66: }
67: else {
68: if (st != NULL) *st = 0;
69: if (sa != NULL) *sa = 0;
70: if (!strcasecomp(t, a))
71: match = YES;
72: else
73: match = NO;
74: if (st != NULL) *st = '_';
75: if (sa != NULL) *sa = '_';
76: }
77: }
78: return match;
79: }
80:
81: PRIVATE double type_value (HTAtom * content_type, HTList * accepted)
82: {
83: if (!content_type) return (1.0);
84: if (accepted) {
85: HTList * cur = accepted;
86: HTPresentation * pres;
87: HTPresentation * wild = NULL;
88: while ((pres = (HTPresentation *) HTList_nextObject(cur))) {
89: if (pres->rep == content_type)
90: return pres->quality;
2.30 frystyk 91: else if (HTMIMEMatch(pres->rep, content_type))
2.29 frystyk 92: wild = pres;
93: }
94: if (wild) return wild->quality;
95: else return (0.0); /* Nothing matched */
96: }
97: return (1.0); /* We accept all types */
98: }
99:
100: PRIVATE double lang_value (HTAtom * language, HTList * accepted)
101: {
102: if (!language) return (1.0);
103: if (accepted) {
104: HTList * cur = accepted;
105: HTAcceptNode * node;
106: HTAcceptNode * wild = NULL;
107: while ((node = (HTAcceptNode *) HTList_nextObject(cur))) {
108: if (node->atom == language)
109: return node->quality;
110: /*
111: * patch by takada@seraph.ntt.jp (94/04/08)
112: * the original line was
2.30 frystyk 113: * else if (HTMIMEMatch(node->atom, language)) {
2.29 frystyk 114: * and the new line is
115: */
116: else if (lang_match(node->atom, language))
117: wild = node;
118: }
119: if (wild) return wild->quality;
120: else return (0.0); /* Nothing matched */
121: }
122: return (1.0); /* We accept all languages */
123: }
124:
125: PRIVATE double encoding_value (HTAtom * encoding, HTList * accepted)
126: {
127: if (!encoding) return (1.0);
128: if (accepted) {
129: HTList * cur = accepted;
130: HTAcceptNode * node;
131: HTAcceptNode * wild = NULL;
132: const char * e = HTAtom_name(encoding);
133: if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary"))
134: return (1.0);
135: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
136: if (node->atom == encoding)
137: return node->quality;
2.30 frystyk 138: else if (HTMIMEMatch(node->atom, encoding))
2.29 frystyk 139: wild = node;
140: }
141: if (wild) return wild->quality;
142: else return (0.0); /* Nothing matched */
143: }
144: return (1.0); /* We accept all encodings */
145: }
146:
147: PRIVATE BOOL HTRank (HTRequest * request, HTArray * variants)
148: {
149: HTContentDescription * cd;
150: void ** data;
151: if (!variants) {
152: if (PROT_TRACE) HTTrace("Ranking..... No variants\n");
153: return NO;
154: }
155: /*
156: ** Walk through the list of local and global preferences and find the
157: ** overall q factor for each variant
158: */
159: cd = (HTContentDescription *) HTArray_firstObject(variants, data);
160: while (cd) {
161: double ctq_local = type_value(cd->content_type, HTRequest_conversion(request));
162: double ctq_global = type_value(cd->content_type, HTFormat_conversion());
163: double clq_local = lang_value(cd->content_language, HTRequest_language(request));
164: double clq_global = lang_value(cd->content_language, HTFormat_language());
165: double ceq_local = encoding_value(cd->content_encoding, HTRequest_encoding(request));
166: double ceq_global = encoding_value(cd->content_encoding, HTFormat_contentCoding());
167: if (PROT_TRACE)
168: HTTrace("Qualities... Content type: %.3f, Content language: %.3f, Content encoding: %.3f\n",
169: HTMAX(ctq_local, ctq_global),
170: HTMAX(clq_local, clq_global),
171: HTMAX(ceq_local, ceq_global));
172: cd->quality *= (HTMAX(ctq_local, ctq_global) *
173: HTMAX(clq_local, clq_global) *
174: HTMAX(ceq_local, ceq_global));
175: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
176: }
177:
178: /* Sort the array of all our accepted preferences */
179: HTArray_sort(variants, VariantSort);
180:
181: /* Write out the result */
182: if (PROT_TRACE) {
183: int cnt = 1;
184: cd = (HTContentDescription *) HTArray_firstObject(variants, data);
185: HTTrace("Ranking.....\n");
186: HTTrace("RANK QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
187: while (cd) {
188: HTTrace("%d. %.4f %-20.20s %-8.8s %-10.10s %s\n",
189: cnt++,
190: cd->quality,
191: cd->content_type ? HTAtom_name(cd->content_type) : "-",
192: cd->content_language?HTAtom_name(cd->content_language):"-",
193: cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
194: cd->filename ? cd->filename :"-");
195: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
196: }
197: }
198: return YES;
199: }
2.3 luotonen 200:
2.14 frystyk 201: /* PUBLIC HTSplitFilename()
202: **
203: ** Split the filename to an array of suffixes.
204: ** Return the number of parts placed to the array.
205: ** Array should have MAX_SUFF+1 items.
206: */
2.23 frystyk 207: PRIVATE int HTSplitFilename (char * s_str, char ** s_arr)
2.14 frystyk 208: {
2.26 frystyk 209: const char *delimiters = HTBind_delimiters();
2.14 frystyk 210: char * start = s_str;
211: char * end;
212: char save;
213: int i;
214:
215: if (!s_str || !s_arr) return 0;
216:
217: for (i=0; i < MAX_SUFF && *start; i++) {
218: for(end=start+1; *end && !strchr(delimiters, *end); end++);
219: save = *end;
220: *end = 0;
221: StrAllocCopy(s_arr[i], start); /* Frees the previous value */
222: *end = save;
223: start = end;
224: }
2.24 frystyk 225: HT_FREE(s_arr[i]); /* Terminating NULL */
2.14 frystyk 226: return i;
227: }
228:
229:
2.3 luotonen 230: /*
231: ** Set default file name for welcome page on each directory.
232: */
2.23 frystyk 233: PUBLIC void HTAddWelcome (char * name)
2.3 luotonen 234: {
235: if (name) {
236: char * mycopy = NULL;
237: StrAllocCopy(mycopy,name);
238:
239: if (!welcome_names)
240: welcome_names = HTList_new();
241: HTList_addObject(welcome_names, (void*)mycopy);
242: }
243: }
244:
245:
2.26 frystyk 246: #ifdef HAVE_READDIR
2.1 luotonen 247:
248: /* PRIVATE multi_match()
249: **
250: ** Check if actual filename (split in parts) fulfills
251: ** the requirements.
252: */
2.23 frystyk 253: PRIVATE BOOL multi_match (char ** required, int m, char ** actual, int n)
2.1 luotonen 254: {
255: int c;
256: int i,j;
257:
2.2 duns 258: #ifdef VMS
259: for(c=0; c<m && c<n && !strcasecomp(required[c], actual[c]); c++);
260: #else /* not VMS */
2.1 luotonen 261: for(c=0; c<m && c<n && !strcmp(required[c], actual[c]); c++);
2.2 duns 262: #endif /* not VMS */
2.1 luotonen 263:
264: if (!c) return NO; /* Names differ rigth from start */
265:
266: for(i=c; i<m; i++) {
267: BOOL found = NO;
268: for(j=c; j<n; j++) {
2.2 duns 269: #ifdef VMS
270: if (!strcasecomp(required[i], actual[j])) {
271: #else /* not VMS */
2.1 luotonen 272: if (!strcmp(required[i], actual[j])) {
2.2 duns 273: #endif /* not VMS */
2.1 luotonen 274: found = YES;
275: break;
276: }
277: }
278: if (!found) return NO;
279: }
280: return YES;
281: }
282:
283:
284: /*
285: ** Get multi-match possibilities for a given file
286: ** ----------------------------------------------
287: ** On entry:
288: ** path absolute path to one file in a directory,
289: ** may end in .multi.
290: ** On exit:
291: ** returns a list of ContentDesription structures
292: ** describing the mathing files.
293: **
294: */
2.29 frystyk 295: PRIVATE HTArray * dir_matches (char * path)
2.1 luotonen 296: {
297: static char * required[MAX_SUFF+1];
298: static char * actual[MAX_SUFF+1];
299: int m,n;
300: char * dirname = NULL;
301: char * basename = NULL;
302: int baselen;
303: char * multi = NULL;
304: DIR * dp;
2.26 frystyk 305: struct dirent * dirbuf;
2.29 frystyk 306: HTArray * matches = NULL;
2.16 frystyk 307: #ifdef HT_REENTRANT
2.31 ! frystyk 308: struct dirent result; /* For readdir_r */
2.16 frystyk 309: #endif
2.1 luotonen 310:
311: if (!path) return NULL;
312:
313: StrAllocCopy(dirname, path);
314: basename = (strrchr(dirname, '/'));
315: if (!basename)
316: goto dir_match_failed;
317: *basename++ = 0;
318:
319: multi = strrchr(basename, MULTI_SUFFIX[0]);
2.2 duns 320: if (multi && !strcasecomp(multi, MULTI_SUFFIX))
2.1 luotonen 321: *multi = 0;
322: baselen = strlen(basename);
323:
324: m = HTSplitFilename(basename, required);
325:
326: dp = opendir(dirname);
327: if (!dp) {
2.13 frystyk 328: if (PROT_TRACE)
2.25 eric 329: HTTrace("Warning..... Can't open directory %s\n", dirname);
2.1 luotonen 330: goto dir_match_failed;
331: }
332:
2.29 frystyk 333: matches = HTArray_new(VARIANTS);
2.16 frystyk 334: #ifdef HT_REENTRANT
2.31 ! frystyk 335: while ((dirbuf = (struct dirent *) readdir_r(dp, &result))) {
2.16 frystyk 336: #else
337: while ((dirbuf = readdir(dp))) {
338: #endif /* HT_REENTRANT */
2.1 luotonen 339: if (!dirbuf->d_ino) continue; /* Not in use */
2.3 luotonen 340: if (!strcmp(dirbuf->d_name,".") ||
341: !strcmp(dirbuf->d_name,"..") ||
2.20 frystyk 342: !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
2.3 luotonen 343: continue;
2.7 frystyk 344:
2.13 frystyk 345: /* Use of direct->namlen is only valid in BSD'ish system */
346: /* Thanks to chip@chinacat.unicom.com (Chip Rosenthal) */
347: /* if ((int)(dirbuf->d_namlen) >= baselen) { */
348: if ((int) strlen(dirbuf->d_name) >= baselen) {
2.1 luotonen 349: n = HTSplitFilename(dirbuf->d_name, actual);
350: if (multi_match(required, m, actual, n)) {
351: HTContentDescription * cd;
2.29 frystyk 352: if ((cd = (HTContentDescription *)
353: HT_CALLOC(1, sizeof(HTContentDescription))) == NULL)
354: HT_OUTOFMEM("dir_matches");
355: if (HTBind_getFormat(dirbuf->d_name,
356: &cd->content_type,
357: &cd->content_encoding,
358: &cd->content_transfer,
359: &cd->content_language,
360: &cd->quality)) {
2.1 luotonen 361: if (cd->content_type) {
2.24 frystyk 362: if ((cd->filename = (char *) HT_MALLOC(strlen(dirname) + 2 + strlen(dirbuf->d_name))) == NULL)
363: HT_OUTOFMEM("dir_matches");
2.29 frystyk 364: sprintf(cd->filename, "%s/%s", dirname, dirbuf->d_name);
365: HTArray_addObject(matches, (void *) cd);
366: } else {
367: HT_FREE(cd);
2.1 luotonen 368: }
2.29 frystyk 369: } else {
370: HT_FREE(cd);
2.1 luotonen 371: }
372: }
373: }
374: }
375: closedir(dp);
376:
377: dir_match_failed:
2.24 frystyk 378: HT_FREE(dirname);
2.1 luotonen 379: return matches;
380: }
381:
382:
383: /*
384: ** Get the best match for a given file
385: ** -----------------------------------
386: ** On entry:
387: ** req->conversions accepted content-types
388: ** req->encodings accepted content-transfer-encodings
389: ** req->languages accepted content-languages
390: ** path absolute pathname of the filename for
391: ** which the match is desired.
392: ** On exit:
393: ** returns a newly allocated absolute filepath.
394: */
2.23 frystyk 395: PRIVATE char * HTGetBest (HTRequest * req, char * path)
2.1 luotonen 396: {
2.29 frystyk 397: HTArray * variants = NULL;
398: char * representation = NULL;
2.1 luotonen 399:
2.13 frystyk 400: if (!path || !*path) return NULL;
2.1 luotonen 401:
2.29 frystyk 402: if ((variants = dir_matches(path)) == NULL) {
403: if (PROT_TRACE) HTTrace("No matches.. for \"%s\"\n", path);
2.13 frystyk 404: return NULL;
2.1 luotonen 405: }
406:
2.29 frystyk 407: if (PROT_TRACE) {
408: void ** data;
409: HTContentDescription * cd = HTArray_firstObject(variants, data);
2.25 eric 410: HTTrace("Multi....... Possibilities for \"%s\"\n", path);
2.29 frystyk 411: HTTrace(" QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
412: while (cd) {
413: HTTrace(" %.4f %-20.20s %-8.8s %-10.10s %s\n",
414: cd->quality,
415: cd->content_type ?HTAtom_name(cd->content_type) :"-\t",
416: cd->content_language?HTAtom_name(cd->content_language):"-",
417: cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
418: cd->filename ?cd->filename :"-");
419: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
420: }
2.1 luotonen 421: }
422:
423: /*
2.29 frystyk 424: ** Finally get the best variant which is readable
2.1 luotonen 425: */
2.29 frystyk 426: if (HTRank(req, variants)) {
427: void ** data;
428: HTContentDescription * cd = HTArray_firstObject(variants, data);
429: while (cd) {
430: if (cd->filename) {
431: if (access(cd->filename, R_OK) != -1)
432: StrAllocCopy(representation, cd->filename);
433: else if (PROT_TRACE)
434: HTTrace("Multi....... `%s\' is not readable\n",
435: cd->filename);
2.1 luotonen 436: }
2.29 frystyk 437: HT_FREE(cd->filename);
438: HT_FREE(cd);
439: cd = (HTContentDescription *) HTArray_nextObject(variants, data);
2.1 luotonen 440: }
441: }
2.29 frystyk 442: HTArray_delete(variants);
443: return representation;
2.1 luotonen 444: }
445:
2.3 luotonen 446:
447:
2.23 frystyk 448: PRIVATE int welcome_value (char * name)
2.3 luotonen 449: {
450: HTList * cur = welcome_names;
451: char * welcome;
452: int v = 0;
453:
454: while ((welcome = (char*)HTList_nextObject(cur))) {
455: v++;
456: if (!strcmp(welcome,name)) return v;
457: }
458: return 0;
459: }
460:
461:
462:
2.23 frystyk 463: PRIVATE char * get_best_welcome (char * path)
2.3 luotonen 464: {
465: char * best_welcome = NULL;
466: int best_value = 0;
467: DIR * dp;
2.26 frystyk 468: struct dirent * dirbuf;
2.3 luotonen 469: char * last = strrchr(path, '/');
470:
471: if (!welcome_names) {
472: HTAddWelcome("Welcome.html");
473: HTAddWelcome("welcome.html");
2.5 luotonen 474: #if 0
2.3 luotonen 475: HTAddWelcome("Index.html");
2.5 luotonen 476: #endif
2.3 luotonen 477: HTAddWelcome("index.html");
478: }
479:
2.5 luotonen 480: if (last && last!=path) *last = 0;
2.3 luotonen 481: dp = opendir(path);
2.5 luotonen 482: if (last && last!=path) *last='/';
2.3 luotonen 483: if (!dp) {
2.13 frystyk 484: if (PROT_TRACE)
2.25 eric 485: HTTrace("Warning..... Can't open directory %s\n",path);
2.3 luotonen 486: return NULL;
487: }
488: while ((dirbuf = readdir(dp))) {
2.13 frystyk 489: if (!dirbuf->d_ino ||
2.3 luotonen 490: !strcmp(dirbuf->d_name,".") ||
491: !strcmp(dirbuf->d_name,"..") ||
2.20 frystyk 492: !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
2.3 luotonen 493: continue;
494: else {
495: int v = welcome_value(dirbuf->d_name);
496: if (v > best_value) {
497: best_value = v;
498: StrAllocCopy(best_welcome, dirbuf->d_name);
499: }
500: }
501: }
502: closedir(dp);
503:
504: if (best_welcome) {
2.24 frystyk 505: char * welcome;
506: if ((welcome = (char *) HT_MALLOC(strlen(path) + strlen(best_welcome)+2)) == NULL)
507: HT_OUTOFMEM("get_best_welcome");
2.4 luotonen 508: sprintf(welcome, "%s%s%s", path, last ? "" : "/", best_welcome);
2.24 frystyk 509: HT_FREE(best_welcome);
2.13 frystyk 510: if (PROT_TRACE)
2.25 eric 511: HTTrace("Welcome..... \"%s\"\n",welcome);
2.3 luotonen 512: return welcome;
513: }
514: return NULL;
515: }
516:
2.26 frystyk 517: #endif /* HAVE_READDIR */
2.1 luotonen 518:
519:
520: /*
521: ** Do multiformat handling
522: ** -----------------------
523: ** On entry:
524: ** req->conversions accepted content-types
525: ** req->encodings accepted content-transfer-encodings
526: ** req->languages accepted content-languages
527: ** path absolute pathname of the filename for
528: ** which the match is desired.
529: ** stat_info pointer to result space.
530: **
531: ** On exit:
532: ** returns a newly allocated absolute filepath of the best
533: ** match, or NULL if no match.
534: ** stat_info will contain inode information as
535: ** returned by stat().
536: */
2.23 frystyk 537: PUBLIC char * HTMulti (HTRequest * req,
538: char * path,
539: struct stat * stat_info)
2.1 luotonen 540: {
541: char * new_path = NULL;
542: int stat_status = -1;
543:
2.3 luotonen 544: if (!req || !path || !*path || !stat_info)
2.1 luotonen 545: return NULL;
546:
2.26 frystyk 547: #ifdef HAVE_READDIR
2.19 frystyk 548: if (*(path+strlen(path)-1) == '/') { /* Find welcome page */
2.3 luotonen 549: new_path = get_best_welcome(path);
550: if (new_path) path = new_path;
2.29 frystyk 551: } else{
2.3 luotonen 552: char * multi = strrchr(path, MULTI_SUFFIX[0]);
553: if (multi && !strcasecomp(multi, MULTI_SUFFIX)) {
2.13 frystyk 554: if (PROT_TRACE)
2.25 eric 555: HTTrace("Multi....... by %s suffix\n", MULTI_SUFFIX);
2.1 luotonen 556: if (!(new_path = HTGetBest(req, path))) {
2.13 frystyk 557: if (PROT_TRACE)
2.25 eric 558: HTTrace("Multi....... failed -- giving up\n");
2.1 luotonen 559: return NULL;
560: }
561: path = new_path;
2.29 frystyk 562: } else {
2.18 frystyk 563: stat_status = HT_STAT(path, stat_info);
2.3 luotonen 564: if (stat_status == -1) {
2.13 frystyk 565: if (PROT_TRACE)
2.29 frystyk 566: HTTrace("AutoMulti... can't stat \"%s\"(errno %d)\n",
2.13 frystyk 567: path, errno);
2.3 luotonen 568: if (!(new_path = HTGetBest(req, path))) {
2.13 frystyk 569: if (PROT_TRACE)
2.25 eric 570: HTTrace("AutoMulti... failed -- giving up\n");
2.3 luotonen 571: return NULL;
572: }
573: path = new_path;
574: }
2.1 luotonen 575: }
576: }
2.26 frystyk 577: #endif /* HAVE_READDIR */
2.1 luotonen 578:
579: if (stat_status == -1)
2.18 frystyk 580: stat_status = HT_STAT(path, stat_info);
2.1 luotonen 581: if (stat_status == -1) {
2.13 frystyk 582: if (PROT_TRACE)
2.25 eric 583: HTTrace("Stat fails.. on \"%s\" -- giving up (errno %d)\n",
2.13 frystyk 584: path, errno);
2.1 luotonen 585: return NULL;
2.29 frystyk 586: } else {
2.1 luotonen 587: if (!new_path) {
588: StrAllocCopy(new_path, path);
589: return new_path;
590: }
591: else return path;
592: }
593: }
594:
595:
Webmaster