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