HTMulti.c
Upload User: zlh9724
Upload Date: 2007-01-04
Package Size: 1991k
Code Size: 10k
Category:

Browser Client

Development Platform:

Unix_Linux

  1. /*       HTMulti.c
  2. ** MULTIFORMAT HANDLING
  3. **
  4. ** (c) COPYRIGHT MIT 1995.
  5. ** Please first read the full copyright statement in the file COPYRIGH.
  6. **
  7. ** History:
  8. ** March 94  AL Separated from HTFile.c because
  9. ** multiformat handling would be a mess in VMS.
  10. */
  11. /* Library include files */
  12. #include "tcp.h"
  13. #include "HTUtils.h"
  14. #include "HTString.h"
  15. #include "HTMulti.h"
  16. #include "HTFile.h"
  17. #include "HTBind.h"
  18. #include "HTList.h"
  19. #include "HTReqMan.h"
  20. PRIVATE HTList * welcome_names = NULL; /* Welcome.html, index.html etc. */
  21. /* PUBLIC HTSplitFilename()
  22. **
  23. ** Split the filename to an array of suffixes.
  24. ** Return the number of parts placed to the array.
  25. ** Array should have MAX_SUFF+1 items.
  26. */
  27. PRIVATE int HTSplitFilename (char * s_str, char ** s_arr)
  28. {
  29.     CONST char *delimiters = HTBind_delimiters();
  30.     char * start = s_str;
  31.     char * end;
  32.     char save;
  33.     int i;
  34.     if (!s_str || !s_arr) return 0;
  35.     for (i=0; i < MAX_SUFF && *start; i++) {
  36. for(end=start+1; *end && !strchr(delimiters, *end); end++);
  37. save = *end;
  38. *end = 0;
  39. StrAllocCopy(s_arr[i], start); /* Frees the previous value */
  40. *end = save;
  41. start = end;
  42.     }
  43.     HT_FREE(s_arr[i]);       /* Terminating NULL */
  44.     return i;
  45. }
  46. /*
  47. ** Set default file name for welcome page on each directory.
  48. */
  49. PUBLIC void HTAddWelcome (char * name)
  50. {
  51.     if (name) {
  52. char * mycopy = NULL;
  53. StrAllocCopy(mycopy,name);
  54. if (!welcome_names)
  55.     welcome_names = HTList_new();
  56. HTList_addObject(welcome_names, (void*)mycopy);
  57.     }
  58. }
  59. #ifdef GOT_READ_DIR
  60. /* PRIVATE multi_match()
  61. **
  62. ** Check if actual filename (split in parts) fulfills
  63. ** the requirements.
  64. */
  65. PRIVATE BOOL multi_match (char ** required, int m, char ** actual, int n)
  66. {
  67.     int c;
  68.     int i,j;
  69. #ifdef VMS
  70.     for(c=0;  c<m && c<n && !strcasecomp(required[c], actual[c]);  c++);
  71. #else /* not VMS */
  72.     for(c=0;  c<m && c<n && !strcmp(required[c], actual[c]);  c++);
  73. #endif /* not VMS */
  74.     if (!c) return NO; /* Names differ rigth from start */
  75.     for(i=c; i<m; i++) {
  76. BOOL found = NO;
  77. for(j=c; j<n; j++) {
  78. #ifdef VMS
  79.     if (!strcasecomp(required[i], actual[j])) {
  80. #else /* not VMS */
  81.     if (!strcmp(required[i], actual[j])) {
  82. #endif /* not VMS */
  83. found = YES;
  84. break;
  85.     }
  86. }
  87. if (!found) return NO;
  88.     }
  89.     return YES;
  90. }
  91. /*
  92. ** Get multi-match possibilities for a given file
  93. ** ----------------------------------------------
  94. ** On entry:
  95. ** path absolute path to one file in a directory,
  96. ** may end in .multi.
  97. ** On exit:
  98. ** returns a list of ContentDesription structures
  99. ** describing the mathing files.
  100. **
  101. */
  102. PRIVATE HTList * dir_matches (char * path)
  103. {
  104.     static char * required[MAX_SUFF+1];
  105.     static char * actual[MAX_SUFF+1];
  106.     int m,n;
  107.     char * dirname = NULL;
  108.     char * basename = NULL;
  109.     int baselen;
  110.     char * multi = NULL;
  111.     DIR * dp;
  112.     STRUCT_DIRENT * dirbuf;
  113.     HTList * matches = NULL;
  114. #ifdef HT_REENTRANT
  115.     STRUCT_DIRENT result;     /* For readdir_r */
  116. #endif
  117.     if (!path) return NULL;
  118.     StrAllocCopy(dirname, path);
  119.     basename = (strrchr(dirname, '/'));
  120.     if (!basename)
  121. goto dir_match_failed;
  122.     *basename++ = 0;
  123.     multi = strrchr(basename, MULTI_SUFFIX[0]);
  124.     if (multi && !strcasecomp(multi, MULTI_SUFFIX))
  125. *multi = 0;
  126.     baselen = strlen(basename);
  127.     m = HTSplitFilename(basename, required);
  128.     dp = opendir(dirname);
  129.     if (!dp) {
  130. if (PROT_TRACE)
  131.     TTYPrint(TDEST,"Warning..... Can't open directory %sn", dirname);
  132. goto dir_match_failed;
  133.     }
  134.     matches = HTList_new();
  135. #ifdef HT_REENTRANT
  136. while ((dirbuf = (STRUCT_DIRENT *) readdir_r(dp, &result))) {
  137. #else
  138. while ((dirbuf = readdir(dp))) {
  139. #endif /* HT_REENTRANT */
  140. #ifndef __QNX__ /* doesn't return unused directory slots */
  141. if (!dirbuf->d_ino) continue; /* Not in use */
  142. #endif
  143. if (!strcmp(dirbuf->d_name,".") ||
  144.     !strcmp(dirbuf->d_name,"..") ||
  145.     !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
  146.     continue;
  147. /* Use of direct->namlen is only valid in BSD'ish system */
  148. /* Thanks to chip@chinacat.unicom.com (Chip Rosenthal) */
  149. /* if ((int)(dirbuf->d_namlen) >= baselen) { */
  150. if ((int) strlen(dirbuf->d_name) >= baselen) {
  151.     n = HTSplitFilename(dirbuf->d_name, actual);
  152.     if (multi_match(required, m, actual, n)) {
  153. HTContentDescription * cd;
  154. cd = HTBind_getDescription(dirbuf->d_name);
  155. if (cd) {
  156.     if (cd->content_type) {
  157. if ((cd->filename = (char *) HT_MALLOC(strlen(dirname) + 2 + strlen(dirbuf->d_name))) == NULL)
  158.     HT_OUTOFMEM("dir_matches");
  159. sprintf(cd->filename, "%s/%s",
  160. dirname, dirbuf->d_name);
  161. HTList_addObject(matches, (void*)cd);
  162.     }
  163.     else HT_FREE(cd);
  164. }
  165.     }
  166. }
  167.     }
  168.     closedir(dp);
  169.   dir_match_failed:
  170.     HT_FREE(dirname);
  171.     return matches;
  172. }
  173. /*
  174. ** Get the best match for a given file
  175. ** -----------------------------------
  176. ** On entry:
  177. ** req->conversions  accepted content-types
  178. ** req->encodings   accepted content-transfer-encodings
  179. ** req->languages   accepted content-languages
  180. ** path   absolute pathname of the filename for
  181. **   which the match is desired.
  182. ** On exit:
  183. ** returns a newly allocated absolute filepath.
  184. */
  185. PRIVATE char * HTGetBest (HTRequest * req, char * path)
  186. {
  187.     HTList * matches;
  188.     HTList * cur;
  189.     HTContentDescription * cd;
  190.     HTContentDescription * best = NULL;
  191.     char * best_path = NULL;
  192.     if (!path || !*path) return NULL;
  193.     matches = dir_matches(path);
  194.     if (!matches) {
  195. if (PROT_TRACE)
  196.     TTYPrint(TDEST, "No matches.. for "%s"n", path);
  197. return NULL;
  198.     }
  199.     /* BEGIN DEBUG */
  200.     cur = matches;
  201.     if (PROT_TRACE)
  202. TTYPrint(TDEST, "Multi....... Possibilities for "%s"n", path);
  203.     if (PROT_TRACE)
  204. TTYPrint(TDEST, "nCONTENT-TYPE  LANGUAGE  ENCODING  QUALITY  FILEn");
  205.     while ((cd = (HTContentDescription*)HTList_nextObject(cur))) {
  206. if (PROT_TRACE)
  207.    TTYPrint(TDEST, "%st%st%st  %.5f  %sn",
  208.    cd->content_type    ?HTAtom_name(cd->content_type)  :"-t",
  209.    cd->content_language?HTAtom_name(cd->content_language):"-",
  210.    cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
  211.    cd->quality,
  212.    cd->filename        ?cd->filename                     :"-");
  213.     }
  214.     if (PROT_TRACE) TTYPrint(TDEST, "n");
  215.     /* END DEBUG */
  216.     /*
  217.     ** Finally get best that is readable
  218.     */
  219.     if (HTRank(matches, req->conversions, req->languages, req->encodings)) {
  220. cur = matches;
  221. while ((best = (HTContentDescription*)HTList_nextObject(cur))) {
  222.     if (best && best->filename) {
  223. if (access(best->filename, R_OK) != -1) {
  224.     StrAllocCopy(best_path, best->filename);
  225.     break;
  226. } else if (PROT_TRACE)
  227.     TTYPrint(TDEST, "Bad News.... "%s" is not readablen",
  228.     best->filename);
  229.     }
  230. }
  231.     } /* Select best */
  232.     cur = matches;
  233.     while ((cd = (HTContentDescription*)HTList_nextObject(cur))) {
  234. if (cd->filename) HT_FREE(cd->filename);
  235. HT_FREE(cd);
  236.     }
  237.     HTList_delete(matches);
  238.     return best_path;
  239. }
  240. PRIVATE int welcome_value (char * name)
  241. {
  242.     HTList * cur = welcome_names;
  243.     char * welcome;
  244.     int v = 0;
  245.     while ((welcome = (char*)HTList_nextObject(cur))) {
  246. v++;
  247. if (!strcmp(welcome,name)) return v;
  248.     }
  249.     return 0;
  250. }
  251. PRIVATE char * get_best_welcome (char * path)
  252. {
  253.     char * best_welcome = NULL;
  254.     int best_value = 0;
  255.     DIR * dp;
  256.     STRUCT_DIRENT * dirbuf;
  257.     char * last = strrchr(path, '/');
  258.     if (!welcome_names) {
  259. HTAddWelcome("Welcome.html");
  260. HTAddWelcome("welcome.html");
  261. #if 0
  262. HTAddWelcome("Index.html");
  263. #endif
  264. HTAddWelcome("index.html");
  265.     }
  266.     if (last && last!=path) *last = 0;
  267.     dp = opendir(path);
  268.     if (last && last!=path) *last='/';
  269.     if (!dp) {
  270. if (PROT_TRACE)
  271.     TTYPrint(TDEST, "Warning..... Can't open directory %sn",path);
  272. return NULL;
  273.     }
  274.     while ((dirbuf = readdir(dp))) {
  275. if (
  276. #ifndef __QNX__ /* doesn't return unused directory slots */
  277.     !dirbuf->d_ino ||
  278. #endif
  279.     !strcmp(dirbuf->d_name,".") ||
  280.     !strcmp(dirbuf->d_name,"..") ||
  281.     !strcmp(dirbuf->d_name, DEFAULT_DIR_FILE))
  282.     continue;
  283. else {
  284.     int v = welcome_value(dirbuf->d_name);
  285.     if (v > best_value) {
  286. best_value = v;
  287. StrAllocCopy(best_welcome, dirbuf->d_name);
  288.     }
  289. }
  290.     }
  291.     closedir(dp);
  292.     if (best_welcome) {
  293. char * welcome;
  294. if ((welcome = (char *) HT_MALLOC(strlen(path) + strlen(best_welcome)+2)) == NULL)
  295.     HT_OUTOFMEM("get_best_welcome");
  296. sprintf(welcome, "%s%s%s", path, last ? "" : "/", best_welcome);
  297. HT_FREE(best_welcome);
  298. if (PROT_TRACE)
  299.     TTYPrint(TDEST,"Welcome..... "%s"n",welcome);
  300. return welcome;
  301.     }
  302.     return NULL;
  303. }
  304. #endif /* GOT_READ_DIR */
  305. /*
  306. ** Do multiformat handling
  307. ** -----------------------
  308. ** On entry:
  309. ** req->conversions  accepted content-types
  310. ** req->encodings   accepted content-transfer-encodings
  311. ** req->languages   accepted content-languages
  312. ** path   absolute pathname of the filename for
  313. **   which the match is desired.
  314. ** stat_info   pointer to result space.
  315. **
  316. ** On exit:
  317. ** returns a newly allocated absolute filepath of the best
  318. ** match, or NULL if no match.
  319. ** stat_info   will contain inode information as
  320. **   returned by stat().
  321. */
  322. PUBLIC char * HTMulti (HTRequest * req,
  323.        char * path,
  324.        struct stat * stat_info)
  325. {
  326.     char * new_path = NULL;
  327.     int stat_status = -1;
  328.     if (!req || !path || !*path || !stat_info)
  329. return NULL;
  330. #ifdef GOT_READ_DIR
  331.     if (*(path+strlen(path)-1) == '/') { /* Find welcome page */
  332. new_path = get_best_welcome(path);
  333. if (new_path) path = new_path;
  334.     }
  335.     else{
  336. char * multi = strrchr(path, MULTI_SUFFIX[0]);
  337. if (multi && !strcasecomp(multi, MULTI_SUFFIX)) {
  338.     if (PROT_TRACE)
  339. TTYPrint(TDEST, "Multi....... by %s suffixn", MULTI_SUFFIX);
  340.     if (!(new_path = HTGetBest(req, path))) {
  341. if (PROT_TRACE)
  342.     TTYPrint(TDEST, "Multi....... failed -- giving upn");
  343. return NULL;
  344.     }
  345.     path = new_path;
  346. }
  347. else {
  348.     stat_status = HT_STAT(path, stat_info);
  349.     if (stat_status == -1) {
  350. if (PROT_TRACE)
  351.     TTYPrint(TDEST,
  352.     "AutoMulti... can't stat "%s"(errno %d)n",
  353.     path, errno);
  354. if (!(new_path = HTGetBest(req, path))) {
  355.     if (PROT_TRACE)
  356. TTYPrint(TDEST, "AutoMulti... failed -- giving upn");
  357.     return NULL;
  358. }
  359. path = new_path;
  360.     }
  361. }
  362.     }
  363. #endif /* GOT_READ_DIR */
  364.     if (stat_status == -1)
  365. stat_status = HT_STAT(path, stat_info);
  366.     if (stat_status == -1) {
  367. if (PROT_TRACE)
  368.     TTYPrint(TDEST, "Stat fails.. on "%s" -- giving up (errno %d)n",
  369.     path, errno);
  370. return NULL;
  371.     }
  372.     else {
  373. if (!new_path) {
  374.     StrAllocCopy(new_path, path);
  375.     return new_path;
  376. }
  377. else return path;
  378.     }
  379. }