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