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