Annotation of libwww/Library/src/HTFile.c, revision 1.49
1.1 timbl 1: /* File Access HTFile.c
2: ** ===========
3: **
4: ** This is unix-specific code in general, with some VMS bits.
1.8 timbl 5: ** These are routines for file access used by browsers.
1.1 timbl 6: **
7: ** History:
8: ** Feb 91 Written Tim Berners-Lee CERN/CN
9: ** Apr 91 vms-vms access included using DECnet syntax
10: ** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
11: ** Fixed access bug for relative names on VMS.
1.28 duns 12: ** Sep 93 (MD) Access to VMS files allows sharing.
13: ** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C
1.1 timbl 14: **
15: ** Bugs:
1.2 timbl 16: ** FTP: Cannot access VMS files from a unix machine.
17: ** How can we know that the
1.1 timbl 18: ** target machine runs VMS?
19: */
20:
1.2 timbl 21: #include "HTFile.h" /* Implemented here */
22:
23:
1.1 timbl 24: #define INFINITY 512 /* file name length @@ FIXME */
1.2 timbl 25: #define MULTI_SUFFIX ".multi" /* Extension for scanning formats */
1.36 luotonen 26: #define MAX_SUFF 15 /* Maximum number of suffixes for a file */
27:
28: #ifdef VMS
29: PRIVATE char * suffix_separators = "_,";
30: #else
1.49 ! luotonen 31: PRIVATE char * suffix_separators = ".,_";
1.36 luotonen 32: #endif
33:
1.1 timbl 34:
35: #include "HTUtils.h"
36:
1.28 duns 37: #ifdef VMS
38: #include "HTVMSUtils.h"
39: #endif /* VMS */
40:
1.1 timbl 41: #include "HTParse.h"
42: #include "tcp.h"
43: #include "HTTCP.h"
44: #ifndef DECNET
45: #include "HTFTP.h"
46: #endif
47: #include "HTAnchor.h"
1.2 timbl 48: #include "HTAtom.h"
49: #include "HTWriter.h"
50: #include "HTFWriter.h"
51: #include "HTInit.h"
1.7 secret 52: #include "HTBTree.h"
1.37 luotonen 53:
1.2 timbl 54: typedef struct _HTSuffix {
55: char * suffix;
1.36 luotonen 56: HTAtom * rep; /* Content-Type */
57: HTAtom * encoding; /* Content-Encoding */
58: HTAtom * language; /* Content-Language */
1.2 timbl 59: float quality;
60: } HTSuffix;
61:
62:
1.36 luotonen 63:
1.12 timbl 64: #ifdef USE_DIRENT /* Set this for Sys V systems */
65: #define STRUCT_DIRENT struct dirent
66: #else
67: #define STRUCT_DIRENT struct direct
68: #endif
1.1 timbl 69:
1.2 timbl 70: #include "HTML.h" /* For directory object building */
71:
1.10 secret 72: #define PUTC(c) (*target->isa->put_character)(target, c)
73: #define PUTS(s) (*target->isa->put_string)(target, s)
74: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
75: #define END(e) (*target->isa->end_element)(target, e)
76: #define FREE_TARGET (*target->isa->free)(target)
1.2 timbl 77: struct _HTStructured {
78: CONST HTStructuredClass * isa;
79: /* ... */
80: };
81:
82:
83: /* Controlling globals
84: **
85: */
86:
87: PUBLIC int HTDirAccess = HT_DIR_OK;
88: PUBLIC int HTDirReadme = HT_DIR_README_TOP;
1.33 timbl 89: PUBLIC BOOL HTTakeBackup = YES;
1.47 luotonen 90: PUBLIC BOOL HTSuffixCaseSense = NO; /* Are suffixes case sensitive */
1.1 timbl 91:
92: PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */
1.23 duns 93: #ifdef VMS
1.28 duns 94: PRIVATE char *HTCacheRoot = "/WWW$SCRATCH"; /* Where to cache things */
1.1 timbl 95: #else
96: PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */
97: #endif
98:
99: /* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */
100:
1.2 timbl 101:
102: /* Suffix registration
103: */
104:
105: PRIVATE HTList * HTSuffixes = 0;
1.36 luotonen 106: PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, NULL, 1.0 };
107: PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, NULL, 1.0};
1.2 timbl 108:
1.11 timbl 109:
1.2 timbl 110: /* Define the representation associated with a file suffix
111: ** -------------------------------------------------------
1.11 timbl 112: **
113: ** Calling this with suffix set to "*" will set the default
114: ** representation.
115: ** Calling this with suffix set to "*.*" will set the default
116: ** representation for unknown suffix files which contain a ".".
1.25 luotonen 117: **
118: ** If filename suffix is already defined its previous
119: ** definition is overridden.
1.2 timbl 120: */
1.36 luotonen 121: PUBLIC void HTAddType ARGS4(CONST char *, suffix,
122: CONST char *, representation,
123: CONST char *, encoding,
124: float, value)
125: {
126: HTSetSuffix(suffix, representation, encoding, NULL, value);
127: }
128:
129:
130: PUBLIC void HTAddEncoding ARGS3(CONST char *, suffix,
131: CONST char *, encoding,
132: float, value)
133: {
134: HTSetSuffix(suffix, NULL, encoding, NULL, value);
135: }
136:
137:
138: PUBLIC void HTAddLanguage ARGS3(CONST char *, suffix,
139: CONST char *, language,
140: float, value)
141: {
142: HTSetSuffix(suffix, NULL, NULL, language, value);
143: }
144:
145:
146: PUBLIC void HTSetSuffix ARGS5(CONST char *, suffix,
147: CONST char *, representation,
148: CONST char *, encoding,
149: CONST char *, language,
150: float, value)
1.2 timbl 151: {
1.11 timbl 152: HTSuffix * suff;
1.25 luotonen 153:
1.19 timbl 154: if (strcmp(suffix, "*")==0) suff = &no_suffix;
155: else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
156: else {
1.25 luotonen 157: HTList *cur = HTSuffixes;
158:
159: while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
160: if (suff->suffix && 0==strcmp(suff->suffix, suffix))
161: break;
162: }
163: if (!suff) { /* Not found -- create a new node */
164: suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
165: if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
1.36 luotonen 166:
1.25 luotonen 167: if (!HTSuffixes) HTSuffixes = HTList_new();
168: HTList_addObject(HTSuffixes, suff);
1.36 luotonen 169:
1.25 luotonen 170: StrAllocCopy(suff->suffix, suffix);
171: }
1.11 timbl 172: }
1.19 timbl 173:
1.36 luotonen 174: if (representation)
175: suff->rep = HTAtom_for(representation);
176: if (language)
177: suff->language = HTAtom_for(language);
178: if (encoding) {
1.38 luotonen 179: char * enc = NULL;
1.19 timbl 180: char * p;
181: StrAllocCopy(enc, encoding);
182: for (p=enc; *p; p++) *p = TOLOWER(*p);
1.38 luotonen 183: suff->encoding = HTAtom_for(enc);
184: free(enc); /* Leak fixed AL 6 Feb 1994 */
1.11 timbl 185: }
1.36 luotonen 186:
1.2 timbl 187: suff->quality = value;
188: }
189:
190:
1.36 luotonen 191: PRIVATE BOOL is_separator ARGS1(char, ch)
192: {
193: if (strchr(suffix_separators, ch)) return YES;
194: else return NO;
195: }
196:
197:
198: /* PRIVATE split_filename()
199: **
200: ** Split the filename to an array of suffixes.
201: ** Return the number of parts placed to the array.
202: ** Array should have MAX_SUFF+1 items.
203: */
204: PRIVATE int split_filename ARGS2(char *, s_str,
205: char **, s_arr)
206: {
207: char * start = s_str;
208: char * end;
209: char save;
210: int i;
211:
212: if (!s_str || !s_arr) return 0;
213:
214: for (i=0; i < MAX_SUFF && *start; i++) {
215: for(end=start+1; *end && !is_separator(*end); end++);
216: save = *end;
217: *end = 0;
1.38 luotonen 218: StrAllocCopy(s_arr[i], start); /* Frees the previous value */
1.36 luotonen 219: *end = save;
220: start = end;
221: }
222: FREE(s_arr[i]); /* Terminating NULL */
223: return i;
224: }
225:
226:
227: /* PRIVATE multi_match()
228: **
229: ** Check if actual filename (split in parts) fulfills
230: ** the requirements.
231: */
232: PRIVATE BOOL multi_match ARGS4(char **, required,
233: int, m,
234: char **, actual,
235: int, n)
236: {
237: int c;
238: int i,j;
239:
240: for(c=0; c<m && c<n && !strcmp(required[c], actual[c]); c++);
241:
242: if (!c) return NO; /* Names differ rigth from start */
243:
244: for(i=c; i<m; i++) {
245: BOOL found = NO;
246: for(j=c; j<n; j++) {
247: if (!strcmp(required[i], actual[j])) {
248: found = YES;
249: break;
250: }
251: }
252: if (!found) return NO;
253: }
254: return YES;
255: }
256:
257:
258: PRIVATE HTContentDescription * content_description ARGS2(char **, actual,
259: int, n)
260: {
261: HTContentDescription * cd;
262: int i;
263:
264: #ifndef NO_INIT
265: if (!HTSuffixes) HTFileInit();
266: #endif
267:
1.47 luotonen 268: if (n < 2) return NULL; /* No suffix */
269:
1.36 luotonen 270: cd = (HTContentDescription*)calloc(1, sizeof(HTContentDescription));
271: if (!cd) outofmem(__FILE__, "HTContentDescription");
272:
273: cd->quality = 1.0;
274:
275: for (i=n-1; i>0; i--) {
276: HTList * cur = HTSuffixes;
277: HTSuffix * suff;
278: BOOL found = NO;
279:
1.47 luotonen 280: CTRACE(stderr, "Searching... for suffix %d: \"%s\"\n", i, actual[i]);
1.36 luotonen 281:
282: while ((suff = (HTSuffix*)HTList_nextObject(cur))) {
1.47 luotonen 283: if ((HTSuffixCaseSense && !strcmp(suff->suffix, actual[i])) ||
284: (!HTSuffixCaseSense && !strcasecomp(suff->suffix, actual[i]))){
1.36 luotonen 285:
286: if (!cd->content_type)
287: cd->content_type = suff->rep;
288: if (!cd->content_encoding)
289: cd->content_encoding = suff->encoding;
290: if (!cd->content_language)
291: cd->content_language = suff->language;
1.47 luotonen 292: if (suff->quality > 0.0000001)
293: cd->quality *= suff->quality;
1.36 luotonen 294:
295: found = YES;
296: break;
297: }
298: }
299: if (!found) {
300: if (i < n-1)
301: break;
302: else {
303: free(cd);
304: return NULL;
305: }
306: }
307: }
308: return cd;
309: }
310:
311:
1.47 luotonen 312: /*
313: ** Get multi-match possibilities for a given file
314: ** ----------------------------------------------
315: ** On entry:
316: ** path absolute path to one file in a directory,
317: ** may end in .multi.
318: ** On exit:
319: ** returns a list of ContentDesription structures
320: ** describing the mathing files.
321: **
322: */
1.37 luotonen 323: PRIVATE HTList * dir_matches ARGS1(char *, path)
1.36 luotonen 324: {
325: static char * required[MAX_SUFF+1];
326: static char * actual[MAX_SUFF+1];
327: int m,n;
328: char * dirname = NULL;
329: char * basename = NULL;
330: int baselen;
331: char * multi = NULL;
332: DIR * dp;
333: STRUCT_DIRENT * dirbuf;
1.37 luotonen 334: HTList * matches = NULL;
1.36 luotonen 335:
1.37 luotonen 336: if (!path) return NULL;
1.36 luotonen 337:
1.37 luotonen 338: StrAllocCopy(dirname, path);
1.36 luotonen 339: basename = (strrchr(dirname, '/'));
340: if (!basename)
1.37 luotonen 341: goto dir_match_failed;
1.36 luotonen 342: *basename++ = 0;
343:
344: multi = strrchr(basename, MULTI_SUFFIX[0]);
345: if (multi && !strcmp(multi, MULTI_SUFFIX))
346: *multi = 0;
347: baselen = strlen(basename);
348:
349: m = split_filename(basename, required);
350:
351: dp = opendir(dirname);
352: if (!dp) {
1.38 luotonen 353: CTRACE(stderr,"Warning..... Can't open directory %s\n",
1.37 luotonen 354: dirname);
355: goto dir_match_failed;
1.36 luotonen 356: }
357:
1.37 luotonen 358: matches = HTList_new();
1.36 luotonen 359: while ((dirbuf = readdir(dp))) {
360: if (!dirbuf->d_ino) continue; /* Not in use */
1.39 luotonen 361: if ((int)(dirbuf->d_namlen) >= baselen) {
1.36 luotonen 362: n = split_filename(dirbuf->d_name, actual);
363: if (multi_match(required, m, actual, n)) {
364: HTContentDescription * cd;
365: cd = content_description(actual, n);
366: if (cd) {
367: if (cd->content_type) {
1.38 luotonen 368: cd->filename = (char*)malloc(strlen(dirname) + 2 +
369: strlen(dirbuf->d_name));
370: if (!cd->filename) outofmem(__FILE__, "dir_matches");
371: sprintf(cd->filename, "%s/%s",
372: dirname, dirbuf->d_name);
1.37 luotonen 373: HTList_addObject(matches, (void*)cd);
1.36 luotonen 374: }
375: else free(cd);
376: }
377: }
378: }
379: }
380: closedir(dp);
381:
1.37 luotonen 382: dir_match_failed:
383: free(dirname);
384: return matches;
385: }
386:
387:
1.47 luotonen 388: /*
389: ** Get the best match for a given file
390: ** -----------------------------------
391: ** On entry:
392: ** req->conversions accepted content-types
393: ** req->encodings accepted content-transfer-encodings
394: ** req->languages accepted content-languages
395: ** path absolute pathname of the filename for
396: ** which the match is desired.
397: ** On exit:
398: ** returns a newly allocated absolute filepath.
399: */
400: PRIVATE char * HTGetBest ARGS2(HTRequest *, req,
401: char *, path)
1.37 luotonen 402: {
403: HTList * matches;
1.38 luotonen 404: HTList * cur;
1.47 luotonen 405: HTContentDescription * cd;
1.38 luotonen 406: HTContentDescription * best = NULL;
1.47 luotonen 407: char * best_path = NULL;
1.37 luotonen 408:
1.47 luotonen 409:
410: if (!path || !*path) return NO;
1.37 luotonen 411:
1.47 luotonen 412: matches = dir_matches(path);
1.38 luotonen 413: if (!matches) {
1.47 luotonen 414: CTRACE(stderr, "No matches.. for \"%s\"\n", path);
1.38 luotonen 415: return NO;
416: }
1.37 luotonen 417:
1.38 luotonen 418: /* BEGIN DEBUG */
419: cur = matches;
1.47 luotonen 420: CTRACE(stderr, "Multi....... Possibilities for \"%s\"\n", path);
1.38 luotonen 421: CTRACE(stderr, "\nCONTENT-TYPE LANGUAGE ENCODING QUALITY FILE\n");
422: while ((cd = (HTContentDescription*)HTList_nextObject(cur))) {
423: CTRACE(stderr, "%s\t%s\t%s\t %.5f %s\n",
424: cd->content_type ?HTAtom_name(cd->content_type) :"-\t",
425: cd->content_language?HTAtom_name(cd->content_language):"-",
426: cd->content_encoding?HTAtom_name(cd->content_encoding):"-",
427: cd->quality,
428: cd->filename ?cd->filename :"-");
429: }
430: CTRACE(stderr, "\n");
431: /* END DEBUG */
432:
433: /*
434: ** Finally get best that is readable
435: */
1.37 luotonen 436: if (HTRank(matches, req->conversions, req->languages, req->encodings)) {
1.38 luotonen 437: cur = matches;
438: while ((best = (HTContentDescription*)HTList_nextObject(cur))) {
439: if (best && best->filename) {
440: if (access(best->filename, R_OK) != -1) {
1.47 luotonen 441: StrAllocCopy(best_path, best->filename);
1.38 luotonen 442: break;
443: }
1.47 luotonen 444: else CTRACE(stderr, "Bad News.... \"%s\" is not readable\n",
1.38 luotonen 445: best->filename);
446: }
1.36 luotonen 447: }
1.37 luotonen 448: } /* Select best */
1.36 luotonen 449:
1.38 luotonen 450: cur = matches;
451: while ((cd = (HTContentDescription*)HTList_nextObject(cur))) {
452: if (cd->filename) free(cd->filename);
453: free(cd);
454: }
1.37 luotonen 455: HTList_delete(matches);
1.38 luotonen 456:
1.47 luotonen 457: return best_path;
1.36 luotonen 458: }
459:
460:
461:
462:
1.47 luotonen 463: /*
464: ** Do multiformat handling
465: ** -----------------------
466: ** On entry:
467: ** req->conversions accepted content-types
468: ** req->encodings accepted content-transfer-encodings
469: ** req->languages accepted content-languages
470: ** path absolute pathname of the filename for
471: ** which the match is desired.
472: ** stat_info pointer to result space.
473: **
474: ** On exit:
475: ** returns a newly allocated absolute filepath of the best
476: ** match, or NULL if no match.
477: ** stat_info will contain inode information as
478: ** returned by stat().
479: */
480: PUBLIC char * HTMulti ARGS3(HTRequest *, req,
481: char *, path,
482: struct stat *, stat_info)
1.36 luotonen 483: {
484: char * multi;
1.47 luotonen 485: char * new_path = NULL;
1.36 luotonen 486: int stat_status = -1;
487:
1.47 luotonen 488: if (!req || !path || !stat_info)
489: return NULL;
1.36 luotonen 490:
491: #ifdef GOT_READ_DIR
1.47 luotonen 492: multi = strrchr(path, MULTI_SUFFIX[0]);
1.36 luotonen 493: if (multi && !strcmp(multi, MULTI_SUFFIX)) {
1.38 luotonen 494: CTRACE(stderr, "Multi....... by %s suffix\n", MULTI_SUFFIX);
1.47 luotonen 495: if (!(new_path = HTGetBest(req, path))) {
1.38 luotonen 496: CTRACE(stderr, "Multi....... failed -- giving up\n");
1.47 luotonen 497: return NULL;
1.36 luotonen 498: }
1.47 luotonen 499: path = new_path;
1.36 luotonen 500: }
501: else {
1.47 luotonen 502: stat_status = stat(path, stat_info);
1.36 luotonen 503: if (stat_status == -1) {
504: CTRACE(stderr,
1.38 luotonen 505: "AutoMulti... because couldn't stat \"%s\" (errno %d)\n",
1.47 luotonen 506: path, errno);
507: if (!(new_path = HTGetBest(req, path))) {
1.38 luotonen 508: CTRACE(stderr, "AutoMulti... failed -- giving up\n");
1.47 luotonen 509: return NULL;
1.36 luotonen 510: }
1.47 luotonen 511: path = new_path;
1.36 luotonen 512: }
513: }
514: #endif /* GOT_READ_DIR */
515:
516: if (stat_status == -1)
1.47 luotonen 517: stat_status = stat(path, stat_info);
1.36 luotonen 518: if (stat_status == -1) {
1.47 luotonen 519: CTRACE(stderr, "Stat fails.. on \"%s\" -- giving up (errno %d)\n",
520: path, errno);
521: return NULL;
1.36 luotonen 522: }
523: else {
1.47 luotonen 524: if (!new_path) {
525: StrAllocCopy(new_path, path);
526: return new_path;
527: }
528: else return path;
1.36 luotonen 529: }
530: }
531:
1.2 timbl 532:
533:
1.1 timbl 534:
1.2 timbl 535:
536:
1.47 luotonen 537:
1.2 timbl 538: /* Send README file
539: **
540: ** If a README file exists, then it is inserted into the document here.
541: */
542:
1.12 timbl 543: #ifdef GOT_READ_DIR
1.2 timbl 544: PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
545: {
546: FILE * fp;
547: char * readme_file_name =
548: malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
549: strcpy(readme_file_name, localname);
550: strcat(readme_file_name, "/");
551: strcat(readme_file_name, HT_DIR_README_FILE);
552:
553: fp = fopen(readme_file_name, "r");
554:
555: if (fp) {
556: HTStructuredClass targetClass;
557:
558: targetClass = *target->isa; /* (Can't init agregate in K&R) */
559: START(HTML_PRE);
560: for(;;){
561: char c = fgetc(fp);
562: if (c == (char)EOF) break;
563: switch (c) {
564: case '&':
565: case '<':
566: case '>':
567: PUTC('&');
568: PUTC('#');
569: PUTC((char)(c / 10));
570: PUTC((char) (c % 10));
571: PUTC(';');
572: break;
1.13 secret 573: /* case '\n':
574: PUTC('\r');
575: Bug removed thanks to joe@athena.mit.edu */
1.2 timbl 576: default:
577: PUTC(c);
578: }
579: }
580: END(HTML_PRE);
581: fclose(fp);
582: }
1.38 luotonen 583: free(readme_file_name); /* Leak fixed AL 6 Feb 1994 */
1.2 timbl 584: }
1.3 timbl 585: #endif
1.2 timbl 586:
587:
1.1 timbl 588: /* Make the cache file name for a W3 document
589: ** ------------------------------------------
590: ** Make up a suitable name for saving the node in
591: **
592: ** E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch
593: ** /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
594: **
595: ** On exit,
596: ** returns a malloc'ed string which must be freed by the caller.
597: */
598: PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
599: {
600: char * access = HTParse(name, "", PARSE_ACCESS);
601: char * host = HTParse(name, "", PARSE_HOST);
602: char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
603:
604: char * result;
605: result = (char *)malloc(
606: strlen(HTCacheRoot)+strlen(access)
607: +strlen(host)+strlen(path)+6+1);
608: if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
609: sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
610: free(path);
611: free(access);
612: free(host);
613: return result;
614: }
615:
1.2 timbl 616:
1.1 timbl 617: /* Open a file for write, creating the path
618: ** ----------------------------------------
619: */
620: #ifdef NOT_IMPLEMENTED
621: PRIVATE int HTCreatePath ARGS1(CONST char *,path)
622: {
623: return -1;
624: }
625: #endif
626:
627: /* Convert filenames between local and WWW formats
628: ** -----------------------------------------------
629: ** Make up a suitable name for saving the node in
630: **
631: ** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch
632: ** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
633: **
634: ** On exit,
635: ** returns a malloc'ed string which must be freed by the caller.
636: */
637: PUBLIC char * HTLocalName ARGS1(CONST char *,name)
638: {
639: char * access = HTParse(name, "", PARSE_ACCESS);
640: char * host = HTParse(name, "", PARSE_HOST);
641: char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
642:
1.6 timbl 643: HTUnEscape(path); /* Interpret % signs */
1.36 luotonen 644:
1.49 ! luotonen 645: if (HTImServer && !*access) {
1.47 luotonen 646: free(access);
647: free(host);
648: CTRACE(stderr, "Local filename is \"%s\"\n", path);
649: return(path);
650: }
651:
1.27 duns 652: if (0==strcmp(access, "file")) { /* local file */
1.1 timbl 653: free(access);
1.9 timbl 654: if ((0==strcasecomp(host, HTHostName())) ||
655: (0==strcasecomp(host, "localhost")) || !*host) {
1.1 timbl 656: free(host);
657: if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
658: return(path);
659: } else {
660: char * result = (char *)malloc(
661: strlen("/Net/")+strlen(host)+strlen(path)+1);
662: if (result == NULL) outofmem(__FILE__, "HTLocalName");
663: sprintf(result, "%s%s%s", "/Net/", host, path);
664: free(host);
665: free(path);
666: if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
667: return result;
668: }
669: } else { /* other access */
670: char * result;
671: CONST char * home = (CONST char*)getenv("HOME");
1.28 duns 672: #ifdef VMS
673: if (!home)
674: home = HTCacheRoot;
675: else
676: home = HTVMS_wwwName(home);
677: #else /* not VMS */
1.1 timbl 678: if (!home) home = "/tmp";
1.28 duns 679: #endif /* not VMS */
1.1 timbl 680: result = (char *)malloc(
681: strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
682: if (result == NULL) outofmem(__FILE__, "HTLocalName");
683: sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
684: free(path);
685: free(access);
686: free(host);
687: return result;
688: }
689: }
690:
691:
692: /* Make a WWW name from a full local path name
693: **
694: ** Bugs:
695: ** At present, only the names of two network root nodes are hand-coded
696: ** in and valid for the NeXT only. This should be configurable in
697: ** the general case.
698: */
699:
700: PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
701: {
702: char * result;
703: #ifdef NeXT
704: if (0==strncmp("/private/Net/", name, 13)) {
705: result = (char *)malloc(7+strlen(name+13)+1);
706: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
707: sprintf(result, "file://%s", name+13);
708: } else
709: #endif
710: if (0==strncmp(HTMountRoot, name, 5)) {
711: result = (char *)malloc(7+strlen(name+5)+1);
712: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
713: sprintf(result, "file://%s", name+5);
714: } else {
715: result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
716: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
717: sprintf(result, "file://%s%s", HTHostName(), name);
718: }
719: if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
720: return result;
721: }
722:
723:
1.2 timbl 724: /* Determine a suitable suffix, given the representation
725: ** -----------------------------------------------------
726: **
727: ** On entry,
728: ** rep is the atomized MIME style representation
729: **
730: ** On exit,
731: ** returns a pointer to a suitable suffix string if one has been
732: ** found, else "".
733: */
734: PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
735: {
736: HTSuffix * suff;
1.35 luotonen 737: HTList * cur;
1.2 timbl 738:
739: #ifndef NO_INIT
740: if (!HTSuffixes) HTFileInit();
741: #endif
1.35 luotonen 742: cur = HTSuffixes;
743: while ((suff = (HTSuffix*)HTList_nextObject(cur))) {
1.2 timbl 744: if (suff->rep == rep) {
745: return suff->suffix; /* OK -- found */
746: }
747: }
748: return ""; /* Dunno */
749: }
750:
751:
1.1 timbl 752: /* Determine file format from file name
753: ** ------------------------------------
754: **
1.19 timbl 755: ** This version will return the representation and also set
756: ** a variable for the encoding.
757: **
758: ** It will handle for example x.txt, x.txt,Z, x.Z
1.2 timbl 759: */
760:
1.47 luotonen 761: PUBLIC HTFormat HTFileFormat ARGS3(CONST char *, filename,
762: HTAtom **, pencoding,
763: HTAtom **, planguage)
764: {
765: char * actual[MAX_SUFF+1];
766: int n;
767: HTContentDescription * cd;
768: HTFormat content_type = NULL;
769:
770: if (!filename) return WWW_BINARY;
771:
772: #ifndef NO_INIT
773: if (!HTSuffixes) HTFileInit();
774: #endif
775:
776: for (n=0; n<MAX_SUFF+1; n++) actual[n] = NULL;
777: n = split_filename((char*)filename, actual);
778: cd = content_description(actual, n);
779: while(n-- > 0) if (actual[n]) free(actual[n]);
780:
781: if (cd) {
782: if (pencoding) *pencoding = cd->content_encoding;
783: if (planguage) *planguage = cd->content_language;
784: content_type = cd->content_type;
785: free(cd);
786: }
787: else {
788: HTSuffix * suff = strchr(filename,'.') ?
789: (unknown_suffix.rep ? &unknown_suffix : &no_suffix) :
790: &no_suffix;
791:
792: if (pencoding) *pencoding = suff->encoding;
793: if (planguage) *planguage = suff->language;
794: content_type = suff->rep;
795: }
796:
797: if (pencoding && !*pencoding)
798: *pencoding = HTAtom_for("binary");
799: return content_type ? content_type : WWW_BINARY;
800: }
1.2 timbl 801:
1.47 luotonen 802: #ifdef OLD_CODE
1.2 timbl 803: HTSuffix * suff;
1.35 luotonen 804: HTList * cur;
1.2 timbl 805: int lf = strlen(filename);
806:
1.30 timbl 807: if (pencoding) *pencoding = NULL;
1.35 luotonen 808: cur = HTSuffixes;
809: while ((suff = (HTSuffix*)HTList_nextObject(cur))) {
810: int ls = strlen(suff->suffix);
1.2 timbl 811: if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
1.30 timbl 812: if (pencoding) *pencoding = suff->encoding;
1.19 timbl 813: if (suff->rep) return suff->rep; /* OK -- found */
1.35 luotonen 814:
815: /* Got encoding, need representation */
816: cur = HTSuffixes;
817: while ((suff = (HTSuffix*)HTList_nextObject(cur))) {
818: int ls2 = strlen(suff->suffix);
819: if ((ls <= lf) &&
820: 0==strncmp(suff->suffix, filename + lf - ls -ls2, ls2)) {
1.19 timbl 821: if (suff->rep) return suff->rep;
822: }
823: }
1.2 timbl 824: }
825: }
1.11 timbl 826:
1.19 timbl 827: /* defaults tree */
1.11 timbl 828:
1.19 timbl 829: suff = strchr(filename, '.') ? /* Unknown suffix */
830: ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
831: : &no_suffix;
832:
833: /* set default encoding unless found with suffix already */
1.30 timbl 834: if (pencoding && !*pencoding) *pencoding = suff->encoding ? suff->encoding
1.19 timbl 835: : HTAtom_for("binary");
836: return suff->rep ? suff->rep : WWW_BINARY;
1.2 timbl 837: }
1.47 luotonen 838: #endif /* OLD_CODE */
839:
1.2 timbl 840:
841:
842: /* Determine value from file name
843: ** ------------------------------
1.1 timbl 844: **
845: */
846:
1.2 timbl 847: PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
848:
1.1 timbl 849: {
1.2 timbl 850: HTSuffix * suff;
1.35 luotonen 851: HTList * cur;
1.2 timbl 852: int lf = strlen(filename);
853:
854: #ifndef NO_INIT
855: if (!HTSuffixes) HTFileInit();
856: #endif
1.35 luotonen 857: cur = HTSuffixes;
858: while ((suff = (HTSuffix*)HTList_nextObject(cur))) {
859: int ls = strlen(suff->suffix);
1.2 timbl 860: if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
861: if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
862: filename, suff->quality);
863: return suff->quality; /* OK -- found */
864: }
865: }
866: return 0.3; /* Dunno! */
1.1 timbl 867: }
868:
869:
870: /* Determine write access to a file
1.2 timbl 871: ** --------------------------------
872: **
873: ** On exit,
874: ** return value YES if file can be accessed and can be written to.
875: **
876: ** Bugs:
877: ** 1. No code for non-unix systems.
878: ** 2. Isn't there a quicker way?
1.1 timbl 879: */
880:
1.23 duns 881: #ifdef VMS
1.1 timbl 882: #define NO_GROUPS
883: #endif
884: #ifdef NO_UNIX_IO
885: #define NO_GROUPS
886: #endif
887: #ifdef PCNFS
888: #define NO_GROUPS
889: #endif
890:
891: PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
892: {
893: #ifdef NO_GROUPS
894: return NO; /* Safe answer till we find the correct algorithm */
895: #else
1.44 frystyk 896: #ifdef NeXT
897: int groups[NGROUPS];
898: #else
899: gid_t groups[NGROUPS];
900: #endif
1.1 timbl 901: uid_t myUid;
902: int ngroups; /* The number of groups */
903: struct stat fileStatus;
904: int i;
905:
906: if (stat(filename, &fileStatus)) /* Get details of filename */
907: return NO; /* Can't even access file! */
908:
909: ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
910: myUid = geteuid(); /* Get my user identifier */
911:
912: if (TRACE) {
913: int i;
1.19 timbl 914: fprintf(stderr,
915: "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
1.5 timbl 916: (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
917: fileStatus.st_gid,
1.1 timbl 918: myUid, ngroups);
1.19 timbl 919: for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
920: fprintf(stderr, ")\n");
1.1 timbl 921: }
922:
923: if (fileStatus.st_mode & 0002) /* I can write anyway? */
924: return YES;
925:
926: if ((fileStatus.st_mode & 0200) /* I can write my own file? */
927: && (fileStatus.st_uid == myUid))
928: return YES;
929:
930: if (fileStatus.st_mode & 0020) /* Group I am in can write? */
931: {
932: for (i=0; i<ngroups; i++) {
933: if (groups[i] == fileStatus.st_gid)
934: return YES;
935: }
936: }
937: if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
938: return NO; /* If no excuse, can't do */
939: #endif
940: }
941:
942:
1.2 timbl 943: /* Make a save stream
944: ** ------------------
945: **
946: ** The stream must be used for writing back the file.
947: ** @@@ no backup done
948: */
1.29 timbl 949: PUBLIC HTStream * HTFileSaveStream ARGS1(HTRequest *, request)
1.2 timbl 950: {
951:
1.29 timbl 952: CONST char * addr = HTAnchor_address((HTAnchor*)request->anchor);
1.33 timbl 953: char * filename = HTLocalName(addr);
954: FILE* fp;
955:
956: /* @ Introduce CVS handling here one day
957: */
958: /* Take a backup before we do anything silly 931205
959: */
960: if (HTTakeBackup) {
961: char * backup_filename = 0;
962: char * p = backup_filename;
963: backup_filename = malloc(strlen(filename)+2);
964: if (!backup_filename) outofmem(__FILE__, "taking backup");
965: strcpy(backup_filename, filename);
1.34 timbl 966: for(p=backup_filename+strlen(backup_filename);; p--) {
1.33 timbl 967: if ((*p=='/') || (p<backup_filename)) {
968: p[1]=','; /* Insert comma after slash */
969: break;
970: }
971: p[1] = p[0]; /* Move up everything to the right of it */
972: }
973:
1.35 luotonen 974: if ((fp=fopen(filename, "r"))) { /* File exists */
1.33 timbl 975: fclose(fp);
976: if (TRACE) printf("File `%s' exists\n", filename);
977: if (remove(backup_filename)) {
978: if (TRACE) printf("Backup file `%s' removed\n",
979: backup_filename);
980: }
981: if (rename(filename, backup_filename)) { /* != 0 => Failure */
982: if (TRACE) printf("Rename `%s' to `%s' FAILED!\n",
983: filename, backup_filename);
984: } else { /* Success */
985: if (TRACE) printf("Renamed `%s' to `%s'\n", filename,
986: backup_filename);
987: }
988: }
989: free(backup_filename);
990: } /* if take backup */
1.2 timbl 991:
1.33 timbl 992: {
993: fp = fopen(filename, "w");
994: if (!fp) return NULL;
1.2 timbl 995:
1.40 frystyk 996: return HTFWriter_new(fp, NO);
1.33 timbl 997: }
1.2 timbl 998: }
999:
1.10 secret 1000: /* Output one directory entry
1001: **
1002: */
1003: PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
1004: CONST char * , tail,
1005: CONST char *, entry)
1006: {
1007: char * relative;
1008: char * escaped = HTEscape(entry, URL_XPALPHAS);
1009:
1010: /* If empty tail, gives absolute ref below */
1.37 luotonen 1011: relative = (char*) malloc(strlen(tail) + strlen(escaped)+2);
1.10 secret 1012: if (relative == NULL) outofmem(__FILE__, "DirRead");
1013: sprintf(relative, "%s/%s", tail, escaped);
1.22 timbl 1014: HTStartAnchor(target, NULL, relative);
1.10 secret 1015: free(escaped);
1016: free(relative);
1017: PUTS(entry);
1018: END(HTML_A);
1019: }
1020:
1021: /* Output parent directory entry
1022: **
1023: ** This gives the TITLE and H1 header, and also a link
1024: ** to the parent directory if appropriate.
1025: */
1026: PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
1027: HTAnchor * , anchor)
1028:
1029: {
1030: char * logical = HTAnchor_address(anchor);
1031: char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
1032: char * current;
1033:
1034: current = strrchr(path, '/'); /* last part or "" */
1035: free(logical);
1036:
1037: {
1038: char * printable = NULL;
1039: StrAllocCopy(printable, (current + 1));
1040: HTUnEscape(printable);
1041: START(HTML_TITLE);
1042: PUTS(*printable ? printable : "Welcome ");
1043: PUTS(" directory");
1044: END(HTML_TITLE);
1045:
1046: START(HTML_H1);
1047: PUTS(*printable ? printable : "Welcome");
1048: END(HTML_H1);
1049: free(printable);
1050: }
1051:
1052: /* Make link back to parent directory
1053: */
1054:
1055: if (current && current[1]) { /* was a slash AND something else too */
1056: char * parent;
1057: char * relative;
1058: *current++ = 0;
1059: parent = strrchr(path, '/'); /* penultimate slash */
1060:
1061: relative = (char*) malloc(strlen(current) + 4);
1062: if (relative == NULL) outofmem(__FILE__, "DirRead");
1063: sprintf(relative, "%s/..", current);
1064: HTStartAnchor(target, "", relative);
1065: free(relative);
1066:
1067: PUTS("Up to ");
1068: if (parent) {
1069: char * printable = NULL;
1070: StrAllocCopy(printable, parent + 1);
1071: HTUnEscape(printable);
1072: PUTS(printable);
1073: free(printable);
1074: } else {
1075: PUTS("/");
1076: }
1077:
1078: END(HTML_A);
1079:
1080: }
1081: free(path);
1082: }
1083:
1.2 timbl 1084:
1.10 secret 1085:
1.1 timbl 1086: /* Load a document
1087: ** ---------------
1088: **
1089: ** On entry,
1090: ** addr must point to the fully qualified hypertext reference.
1091: ** This is the physsical address of the file
1092: **
1093: ** On exit,
1.2 timbl 1094: ** returns <0 Error has occured.
1095: ** HTLOADED OK
1.1 timbl 1096: **
1097: */
1.36 luotonen 1098: PUBLIC int HTLoadFile ARGS1 (HTRequest *, request)
1.32 timbl 1099:
1.1 timbl 1100: {
1.46 luotonen 1101: CONST char * addr;
1.1 timbl 1102: char * filename;
1103: HTFormat format;
1.45 luotonen 1104: static char * nodename = 0;
1.1 timbl 1105: char * newname=0; /* Simplified name of file */
1.47 luotonen 1106: HTAtom * encoding;
1107: HTAtom * language;
1.45 luotonen 1108:
1.46 luotonen 1109: if (!request || !request->anchor)
1110: return HT_INTERNAL;
1111:
1.45 luotonen 1112: FREE(nodename); /* From prev call - Leak fixed AL 6 Feb 1994 */
1.19 timbl 1113:
1.1 timbl 1114: /* Reduce the filename to a basic form (hopefully unique!)
1115: */
1.46 luotonen 1116: addr = HTAnchor_physical(request->anchor);
1.1 timbl 1117: StrAllocCopy(newname, addr);
1118: filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
1119: nodename=HTParse(newname, "", PARSE_HOST);
1120: free(newname);
1121:
1.47 luotonen 1122: format = HTFileFormat(filename, &encoding, &language);
1.16 secret 1123:
1.23 duns 1124: #ifdef VMS
1.1 timbl 1125: /* Assume that the file is in Unix-style syntax if it contains a '/'
1126: after the leading one @@ */
1127: {
1.23 duns 1128: FILE * fp;
1.1 timbl 1129: char * vmsname = strchr(filename + 1, '/') ?
1.28 duns 1130: HTVMS_name(nodename, filename) : filename + 1;
1.23 duns 1131: fp = fopen(vmsname, "r", "shr=put", "shr=upd");
1.1 timbl 1132:
1133: /* If the file wasn't VMS syntax, then perhaps it is ultrix
1134: */
1.23 duns 1135: if (!fp) {
1.1 timbl 1136: char ultrixname[INFINITY];
1137: if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
1138: sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
1.23 duns 1139: fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
1140: if (!fp) {
1.1 timbl 1141: if (TRACE) fprintf(stderr,
1142: "HTFile: Can't open as %s\n", ultrixname);
1143: }
1144: }
1.23 duns 1145: if (fp)
1146: {
1147: if (HTEditable(vmsname)) {
1148: HTAtom * put = HTAtom_for("PUT");
1.29 timbl 1149: HTList * methods = HTAnchor_methods(request->anchor);
1.23 duns 1150: if (HTList_indexOf(methods, put) == (-1)) {
1151: HTList_addObject(methods, put);
1152: }
1153: }
1.29 timbl 1154: HTParseFile(format, request->output_format, request->anchor, fp, request->output_stream);
1.23 duns 1155: fclose(fp);
1156: return HT_LOADED;
1157: } /* If successfull open */
1.1 timbl 1158: }
1159: #else
1160:
1.38 luotonen 1161: FREE(filename);
1162:
1.1 timbl 1163: /* For unix, we try to translate the name into the name of a transparently
1164: ** mounted file.
1165: **
1166: ** Not allowed in secure (HTClienntHost) situations TBL 921019
1167: */
1168: #ifndef NO_UNIX_IO
1.17 timbl 1169: /* Need protection here for telnet server but not httpd server */
1.2 timbl 1170:
1.17 timbl 1171: if (!HTSecure) { /* try local file system */
1.47 luotonen 1172: char * localname = HTLocalName(addr);
1.2 timbl 1173: struct stat dir_info;
1.36 luotonen 1174: char * multi;
1175:
1.1 timbl 1176: #ifdef GOT_READ_DIR
1.2 timbl 1177:
1178: /* Multiformat handling
1179: **
1180: ** If needed, scan directory to find a good file.
1181: ** Bug: we don't stat the file to find the length
1182: */
1.47 luotonen 1183: multi = strrchr(localname, MULTI_SUFFIX[0]);
1.36 luotonen 1184: if (multi && !strcmp(multi, MULTI_SUFFIX)) {
1.47 luotonen 1185: struct stat stat_info;
1186: char * new_path = HTMulti(request, localname, &stat_info);
1187: if (new_path) {
1188: FREE(localname);
1189: localname = new_path;
1190: HTAnchor_setPhysical(request->anchor, localname);
1191: format = HTFileFormat(localname, &encoding, &language);
1.36 luotonen 1192: goto open_file;
1.2 timbl 1193: }
1.36 luotonen 1194: else { /* If not found suitable file */
1.47 luotonen 1195: FREE(localname);
1.36 luotonen 1196: return HTLoadError(request, 403, /* List formats? */
1.2 timbl 1197: "Could not find suitable representation for transmission.");
1198: }
1199: } /* if multi suffix */
1.1 timbl 1200: /*
1201: ** Check to see if the 'localname' is in fact a directory. If it is
1202: ** create a new hypertext object containing a list of files and
1203: ** subdirectories contained in the directory. All of these are links
1204: ** to the directories or files listed.
1.12 timbl 1205: ** NB This assumes the existance of a type 'STRUCT_DIRENT', which will
1.1 timbl 1206: ** hold the directory entry, and a type 'DIR' which is used to point to
1207: ** the current directory being read.
1208: */
1209:
1210:
1.47 luotonen 1211: if (stat(localname,&dir_info) == -1) { /* get file information */
1.1 timbl 1212: /* if can't read file information */
1.47 luotonen 1213: if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
1.1 timbl 1214:
1215: } else { /* Stat was OK */
1216:
1217: if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
1.47 luotonen 1218: /* if localname is a directory */
1.2 timbl 1219:
1220: HTStructured* target; /* HTML object */
1221: HTStructuredClass targetClass;
1222:
1.1 timbl 1223: DIR *dp;
1.12 timbl 1224: STRUCT_DIRENT * dirbuf;
1.2 timbl 1225:
1.6 timbl 1226: char * logical;
1227: char * tail;
1228:
1.2 timbl 1229: BOOL present[HTML_A_ATTRIBUTES];
1230:
1.1 timbl 1231: char * tmpfilename = NULL;
1.2 timbl 1232: struct stat file_info;
1.1 timbl 1233:
1234: if (TRACE)
1.47 luotonen 1235: fprintf(stderr,"%s is a directory\n",localname);
1.1 timbl 1236:
1.2 timbl 1237: /* Check directory access.
1238: ** Selective access means only those directories containing a
1239: ** marker file can be browsed
1240: */
1241: if (HTDirAccess == HT_DIR_FORBID) {
1.47 luotonen 1242: FREE(localname);
1.36 luotonen 1243: return HTLoadError(request, 403,
1.2 timbl 1244: "Directory browsing is not allowed.");
1245: }
1246:
1247:
1248: if (HTDirAccess == HT_DIR_SELECTIVE) {
1249: char * enable_file_name =
1.47 luotonen 1250: malloc(strlen(localname)+ 1 +
1.2 timbl 1251: strlen(HT_DIR_ENABLE_FILE) + 1);
1.47 luotonen 1252: strcpy(enable_file_name, localname);
1.2 timbl 1253: strcat(enable_file_name, "/");
1254: strcat(enable_file_name, HT_DIR_ENABLE_FILE);
1255: if (stat(enable_file_name, &file_info) != 0) {
1.47 luotonen 1256: FREE(localname);
1.36 luotonen 1257: return HTLoadError(request, 403,
1.2 timbl 1258: "Selective access is not enabled for this directory");
1259: }
1260: }
1261:
1262:
1.47 luotonen 1263: dp = opendir(localname);
1.2 timbl 1264: if (!dp) {
1.47 luotonen 1265: FREE(localname);
1.36 luotonen 1266: return HTLoadError(request, 403,
1267: "This directory is not readable.");
1.2 timbl 1268: }
1269:
1270:
1271: /* Directory access is allowed and possible
1272: */
1.29 timbl 1273: logical = HTAnchor_address((HTAnchor*)request->anchor);
1.6 timbl 1274: tail = strrchr(logical, '/') +1; /* last part or "" */
1.36 luotonen 1275:
1276: /*
1277: ** Fix AL 26.1.94: make dir.indexing work also if
1278: ** there is a trailing slash:
1279: */
1280: if (!*tail) tail = ".";
1281:
1.30 timbl 1282: target = HTML_new(request, NULL, WWW_HTML,
1283: request->output_format, request->output_stream);
1.2 timbl 1284: targetClass = *target->isa; /* Copy routine entry points */
1285:
1286: { int i;
1287: for(i=0; i<HTML_A_ATTRIBUTES; i++)
1288: present[i] = (i==HTML_A_HREF);
1289: }
1.1 timbl 1290:
1.29 timbl 1291: HTDirTitles(target, (HTAnchor *)request->anchor);
1.10 secret 1292:
1.2 timbl 1293: if (HTDirReadme == HT_DIR_README_TOP)
1.47 luotonen 1294: do_readme(target, localname);
1.7 secret 1295: {
1.9 timbl 1296: HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
1.2 timbl 1297:
1.21 timbl 1298: while ((dirbuf = readdir(dp))!=0)
1.7 secret 1299: {
1300: HTBTElement * dirname = NULL;
1.2 timbl 1301:
1302: /* while there are directory entries to be read */
1.7 secret 1303: if (dirbuf->d_ino == 0)
1304: /* if the entry is not being used, skip it */
1305: continue;
1.1 timbl 1306:
1.10 secret 1307:
1.2 timbl 1308: /* if the current entry is parent directory */
1.10 secret 1309: if ((*(dirbuf->d_name)=='.') ||
1.7 secret 1310: (*(dirbuf->d_name)==','))
1.2 timbl 1311: continue; /* skip those files whose name begins
1312: with '.' or ',' */
1.10 secret 1313:
1.14 timbl 1314: dirname = (HTBTElement *)malloc(
1315: strlen(dirbuf->d_name) + 2);
1.7 secret 1316: if (dirname == NULL) outofmem(__FILE__,"DirRead");
1.47 luotonen 1317: StrAllocCopy(tmpfilename,localname);
1318: if (strcmp(localname,"/"))
1.7 secret 1319:
1320: /* if filename is not root directory */
1321: StrAllocCat(tmpfilename,"/");
1.10 secret 1322:
1.7 secret 1323:
1324: StrAllocCat(tmpfilename,dirbuf->d_name);
1325: stat(tmpfilename, &file_info);
1326: if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
1327: sprintf((char *)dirname,"D%s",dirbuf->d_name);
1328: else sprintf((char *)dirname,"F%s",dirbuf->d_name);
1329: /* D & F to have first directories, then files */
1330: HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
1.6 timbl 1331: }
1.7 secret 1332:
1333: /* Run through tree printing out in order
1334: */
1335: {
1336: HTBTElement * next_element = HTBTree_next(bt,NULL);
1337: /* pick up the first element of the list */
1.15 secret 1338: char state;
1339: /* I for initial (.. file),
1340: D for directory file,
1341: F for file */
1342:
1343: state = 'I';
1.7 secret 1344:
1345: while (next_element != NULL)
1346: {
1.47 luotonen 1347: StrAllocCopy(tmpfilename,localname);
1348: if (strcmp(localname,"/"))
1.7 secret 1349:
1.2 timbl 1350: /* if filename is not root directory */
1.7 secret 1351: StrAllocCat(tmpfilename,"/");
1352:
1.15 secret 1353: StrAllocCat(tmpfilename,
1354: (char *)HTBTree_object(next_element)+1);
1.7 secret 1355: /* append the current entry's filename to the path */
1356: HTSimplify(tmpfilename);
1357: /* Output the directory entry */
1.15 secret 1358: if (strcmp((char *)
1359: (HTBTree_object(next_element)),"D.."))
1360: {
1361: if (state != *(char *)(HTBTree_object(next_element)))
1.7 secret 1362: {
1.15 secret 1363: if (state == 'D')
1364: END(HTML_DIR);
1365: state = *(char *)
1366: (HTBTree_object(next_element))=='D'?'D':'F';
1.7 secret 1367: START(HTML_H2);
1.15 secret 1368: PUTS(state == 'D'?"Subdirectories:":"Files");
1.7 secret 1369: END(HTML_H2);
1370: START(HTML_DIR);
1371: }
1.15 secret 1372: START(HTML_LI);
1.7 secret 1373: }
1.10 secret 1374: HTDirEntry(target, tail,
1375: (char*)HTBTree_object(next_element) +1);
1.7 secret 1376:
1377: next_element = HTBTree_next(bt,next_element);
1378: /* pick up the next element of the list;
1379: if none, return NULL*/
1380: }
1.15 secret 1381: if (state == 'I')
1382: {
1383: START(HTML_P);
1384: PUTS("Empty Directory");
1385: }
1386: else
1.7 secret 1387: END(HTML_DIR);
1.2 timbl 1388: }
1.16 secret 1389:
1.7 secret 1390: /* end while directory entries left to read */
1391: closedir(dp);
1392: free(logical);
1393: free(tmpfilename);
1394: HTBTreeAndObject_free(bt);
1395:
1396: if (HTDirReadme == HT_DIR_README_BOTTOM)
1.47 luotonen 1397: do_readme(target, localname);
1.7 secret 1398: FREE_TARGET;
1.47 luotonen 1399: FREE(localname);
1.7 secret 1400: return HT_LOADED; /* document loaded */
1.2 timbl 1401: }
1402:
1.47 luotonen 1403: } /* end if localname is directory */
1.1 timbl 1404:
1405: } /* end if file stat worked */
1406:
1407: /* End of directory reading section
1408: */
1409: #endif
1.2 timbl 1410: open_file:
1.16 secret 1411: {
1.47 luotonen 1412: FILE * fp = fopen(localname,"r");
1.24 luotonen 1413:
1.19 timbl 1414: if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
1.47 luotonen 1415: localname, (void*)fp);
1.16 secret 1416: if (fp) { /* Good! */
1.47 luotonen 1417: if (HTEditable(localname)) {
1.16 secret 1418: HTAtom * put = HTAtom_for("PUT");
1.29 timbl 1419: HTList * methods = HTAnchor_methods(request->anchor);
1.16 secret 1420: if (HTList_indexOf(methods, put) == (-1)) {
1421: HTList_addObject(methods, put);
1422: }
1.2 timbl 1423: }
1.47 luotonen 1424: FREE(localname);
1.30 timbl 1425: HTParseFile(format, fp, request);
1.16 secret 1426: fclose(fp);
1427: return HT_LOADED;
1428: } /* If succesfull open */
1.48 frystyk 1429: FREE(localname); /* If error in fopen */
1.16 secret 1430: } /* scope of fp */
1431: } /* local unix file system */
1.1 timbl 1432: #endif
1433: #endif
1.45 luotonen 1434:
1435: try_ftp:
1.1 timbl 1436:
1437: #ifndef DECNET
1438: /* Now, as transparently mounted access has failed, we try FTP.
1439: */
1.16 secret 1440: {
1.38 luotonen 1441: if (nodename && strcmp(nodename, HTHostName())!=0)
1.31 timbl 1442: return HTFTPLoad(request, NULL, addr,
1443: request->anchor, request->output_format, request->output_stream);
1.1 timbl 1444: }
1445: #endif
1446:
1.16 secret 1447: /* All attempts have failed.
1.1 timbl 1448: */
1.16 secret 1449: {
1.2 timbl 1450: if (TRACE)
1.24 luotonen 1451: printf("Can't open `%s', errno=%d\n", addr, errno);
1.36 luotonen 1452: return HTLoadError(request, 403, "Can't access requested file.");
1.2 timbl 1453: }
1.1 timbl 1454:
1.16 secret 1455:
1.1 timbl 1456: }
1457:
1458: /* Protocol descriptors
1459: */
1.29 timbl 1460: GLOBALDEF PUBLIC HTProtocol HTFTP = { "ftp", HTLoadFile, 0 , 0 };
1461: GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile,
1462: HTFileSaveStream, 0 };
Webmaster