Annotation of libwww/Library/src/HTFile.c, revision 1.7
1.1 timbl 1: /* File Access HTFile.c
2: ** ===========
3: **
4: ** This is unix-specific code in general, with some VMS bits.
5: ** These are routines for file access used by WWW browsers.
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.
12: **
13: ** Bugs:
1.2 timbl 14: ** FTP: Cannot access VMS files from a unix machine.
15: ** How can we know that the
1.1 timbl 16: ** target machine runs VMS?
17: */
18:
1.2 timbl 19: #include "HTFile.h" /* Implemented here */
20:
21:
1.1 timbl 22: #define INFINITY 512 /* file name length @@ FIXME */
1.2 timbl 23: #define MULTI_SUFFIX ".multi" /* Extension for scanning formats */
1.1 timbl 24:
25: #include "HTUtils.h"
26:
27: #include "HTParse.h"
28: #include "tcp.h"
29: #include "HTTCP.h"
30: #ifndef DECNET
31: #include "HTFTP.h"
32: #endif
33: #include "HTAnchor.h"
1.2 timbl 34: #include "HTAtom.h"
35: #include "HTWriter.h"
36: #include "HTFWriter.h"
37: #include "HTInit.h"
1.7 ! secret 38: #include "HTBTree.h"
1.2 timbl 39:
40: typedef struct _HTSuffix {
41: char * suffix;
42: HTAtom * rep;
43: float quality;
44: } HTSuffix;
45:
46:
1.1 timbl 47:
48: #ifdef unix /* if this is to compile on a UNIX machine */
1.2 timbl 49: #include "HTML.h" /* For directory object building */
50:
51: #define PUTC(c) (*targetClass.put_character)(target, c)
52: #define PUTS(s) (*targetClass.put_string)(target, s)
53: #define START(e) (*targetClass.start_element)(target, e, 0, 0)
54: #define END(e) (*targetClass.end_element)(target, e)
55: #define END_TARGET (*targetClass.end_document)(target)
56: #define FREE_TARGET (*targetClass.free)(target)
57: struct _HTStructured {
58: CONST HTStructuredClass * isa;
59: /* ... */
60: };
61:
62: #endif /* unix */
63:
64:
65: /* Controlling globals
66: **
67: */
68:
69: PUBLIC int HTDirAccess = HT_DIR_OK;
70: PUBLIC int HTDirReadme = HT_DIR_README_TOP;
1.1 timbl 71:
72: PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */
73: #ifdef vms
74: PRIVATE char *HTCacheRoot = "/WWW$SCRATCH/"; /* Where to cache things */
75: #else
76: PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */
77: #endif
78:
79: /* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */
80:
1.2 timbl 81:
82: /* Suffix registration
83: */
84:
85: PRIVATE HTList * HTSuffixes = 0;
86:
87: /* Define the representation associated with a file suffix
88: ** -------------------------------------------------------
89: */
90: PUBLIC void HTSetSuffix ARGS3(
91: CONST char *, suffix,
92: CONST char *, representation,
93: float, value)
94: {
95:
96: HTSuffix * suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
97: if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
98:
99: if (!HTSuffixes) HTSuffixes = HTList_new();
100:
101: StrAllocCopy(suff->suffix, suffix);
102: suff->rep = HTAtom_for(representation);
103: suff->quality = value;
104: HTList_addObject(HTSuffixes, suff);
105: }
106:
107:
108:
109:
1.1 timbl 110: #ifdef vms
111: /* Convert unix-style name into VMS name
112: ** -------------------------------------
113: **
114: ** Bug: Returns pointer to static -- non-reentrant
115: */
116: PRIVATE char * vms_name(CONST char * nn, CONST char * fn)
117: {
118:
119: /* We try converting the filename into Files-11 syntax. That is, we assume
120: ** first that the file is, like us, on a VMS node. We try remote
121: ** (or local) DECnet access. Files-11, VMS, VAX and DECnet
122: ** are trademarks of Digital Equipment Corporation.
123: ** The node is assumed to be local if the hostname WITHOUT DOMAIN
124: ** matches the local one. @@@
125: */
126: static char vmsname[INFINITY]; /* returned */
127: char * filename = (char*)malloc(strlen(fn)+1);
128: char * nodename = (char*)malloc(strlen(nn)+2+1); /* Copies to hack */
129: char *second; /* 2nd slash */
130: char *last; /* last slash */
131:
132: char * hostname = HTHostName();
133:
134: if (!filename || !nodename) outofmem(__FILE__, "vms_name");
135: strcpy(filename, fn);
136: strcpy(nodename, ""); /* On same node? Yes if node names match */
137: {
138: char *p, *q;
139: for (p=hostname, q=nn; *p && *p!='.' && *q && *q!='.'; p++, q++){
140: if (TOUPPER(*p)!=TOUPPER(*q)) {
141: strcpy(nodename, nn);
142: q = strchr(nodename, '.'); /* Mismatch */
143: if (q) *q=0; /* Chop domain */
144: strcat(nodename, "::"); /* Try decnet anyway */
145: break;
146: }
147: }
148: }
149:
150: second = strchr(filename+1, '/'); /* 2nd slash */
151: last = strrchr(filename, '/'); /* last slash */
152:
153: if (!second) { /* Only one slash */
154: sprintf(vmsname, "%s%s", nodename, filename + 1);
155: } else if(second==last) { /* Exactly two slashes */
156: *second = 0; /* Split filename from disk */
157: sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
158: *second = '/'; /* restore */
159: } else { /* More than two slashes */
160: char * p;
161: *second = 0; /* Split disk from directories */
162: *last = 0; /* Split dir from filename */
163: sprintf(vmsname, "%s%s:[%s]%s",
164: nodename, filename+1, second+1, last+1);
165: *second = *last = '/'; /* restore filename */
166: for (p=strchr(vmsname, '['); *p!=']'; p++)
167: if (*p=='/') *p='.'; /* Convert dir sep. to dots */
168: }
169: free(nodename);
170: free(filename);
171: return vmsname;
172: }
173:
174:
175: #endif /* vms */
176:
1.2 timbl 177:
178:
179: /* Send README file
180: **
181: ** If a README file exists, then it is inserted into the document here.
182: */
183:
1.3 timbl 184: #ifdef unix
1.2 timbl 185: PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
186: {
187: FILE * fp;
188: char * readme_file_name =
189: malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
190: strcpy(readme_file_name, localname);
191: strcat(readme_file_name, "/");
192: strcat(readme_file_name, HT_DIR_README_FILE);
193:
194: fp = fopen(readme_file_name, "r");
195:
196: if (fp) {
197: HTStructuredClass targetClass;
198:
199: targetClass = *target->isa; /* (Can't init agregate in K&R) */
200: START(HTML_PRE);
201: for(;;){
202: char c = fgetc(fp);
203: if (c == (char)EOF) break;
204: switch (c) {
205: case '&':
206: case '<':
207: case '>':
208: PUTC('&');
209: PUTC('#');
210: PUTC((char)(c / 10));
211: PUTC((char) (c % 10));
212: PUTC(';');
213: break;
214: case '\n':
215: PUTC('\r'); /* Fall through */
216: default:
217: PUTC(c);
218: }
219: }
220: END(HTML_PRE);
221: fclose(fp);
222: }
223: }
1.3 timbl 224: #endif
1.2 timbl 225:
226:
1.1 timbl 227: /* Make the cache file name for a W3 document
228: ** ------------------------------------------
229: ** Make up a suitable name for saving the node in
230: **
231: ** E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch
232: ** /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
233: **
234: ** On exit,
235: ** returns a malloc'ed string which must be freed by the caller.
236: */
237: PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
238: {
239: char * access = HTParse(name, "", PARSE_ACCESS);
240: char * host = HTParse(name, "", PARSE_HOST);
241: char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
242:
243: char * result;
244: result = (char *)malloc(
245: strlen(HTCacheRoot)+strlen(access)
246: +strlen(host)+strlen(path)+6+1);
247: if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
248: sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
249: free(path);
250: free(access);
251: free(host);
252: return result;
253: }
254:
1.2 timbl 255:
1.1 timbl 256: /* Open a file for write, creating the path
257: ** ----------------------------------------
258: */
259: #ifdef NOT_IMPLEMENTED
260: PRIVATE int HTCreatePath ARGS1(CONST char *,path)
261: {
262: return -1;
263: }
264: #endif
265:
266: /* Convert filenames between local and WWW formats
267: ** -----------------------------------------------
268: ** Make up a suitable name for saving the node in
269: **
270: ** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch
271: ** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
272: **
273: ** On exit,
274: ** returns a malloc'ed string which must be freed by the caller.
275: */
276: PUBLIC char * HTLocalName ARGS1(CONST char *,name)
277: {
278: char * access = HTParse(name, "", PARSE_ACCESS);
279: char * host = HTParse(name, "", PARSE_HOST);
280: char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
281:
1.6 timbl 282: HTUnEscape(path); /* Interpret % signs */
283:
1.1 timbl 284: if (0==strcmp(access, "file")) {
285: free(access);
286: if ((0==strcmp(host, HTHostName())) || !*host) {
287: free(host);
288: if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
289: return(path);
290: } else {
291: char * result = (char *)malloc(
292: strlen("/Net/")+strlen(host)+strlen(path)+1);
293: if (result == NULL) outofmem(__FILE__, "HTLocalName");
294: sprintf(result, "%s%s%s", "/Net/", host, path);
295: free(host);
296: free(path);
297: if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
298: return result;
299: }
300: } else { /* other access */
301: char * result;
302: CONST char * home = (CONST char*)getenv("HOME");
303: if (!home) home = "/tmp";
304: result = (char *)malloc(
305: strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
306: if (result == NULL) outofmem(__FILE__, "HTLocalName");
307: sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
308: free(path);
309: free(access);
310: free(host);
311: return result;
312: }
313: }
314:
315:
316: /* Make a WWW name from a full local path name
317: **
318: ** Bugs:
319: ** At present, only the names of two network root nodes are hand-coded
320: ** in and valid for the NeXT only. This should be configurable in
321: ** the general case.
322: */
323:
324: PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
325: {
326: char * result;
327: #ifdef NeXT
328: if (0==strncmp("/private/Net/", name, 13)) {
329: result = (char *)malloc(7+strlen(name+13)+1);
330: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
331: sprintf(result, "file://%s", name+13);
332: } else
333: #endif
334: if (0==strncmp(HTMountRoot, name, 5)) {
335: result = (char *)malloc(7+strlen(name+5)+1);
336: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
337: sprintf(result, "file://%s", name+5);
338: } else {
339: result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
340: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
341: sprintf(result, "file://%s%s", HTHostName(), name);
342: }
343: if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
344: return result;
345: }
346:
347:
1.2 timbl 348: /* Determine a suitable suffix, given the representation
349: ** -----------------------------------------------------
350: **
351: ** On entry,
352: ** rep is the atomized MIME style representation
353: **
354: ** On exit,
355: ** returns a pointer to a suitable suffix string if one has been
356: ** found, else "".
357: */
358: PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
359: {
360: HTSuffix * suff;
361: int n;
362: int i;
363:
364: #ifndef NO_INIT
365: if (!HTSuffixes) HTFileInit();
366: #endif
367: n = HTList_count(HTSuffixes);
368: for(i=0; i<n; i++) {
369: suff = HTList_objectAt(HTSuffixes, i);
370: if (suff->rep == rep) {
371: return suff->suffix; /* OK -- found */
372: }
373: }
374: return ""; /* Dunno */
375: }
376:
377:
1.1 timbl 378: /* Determine file format from file name
379: ** ------------------------------------
380: **
1.2 timbl 381: */
382:
383: PUBLIC HTFormat HTFileFormat ARGS1 (CONST char *,filename)
384:
385: {
386: HTSuffix * suff;
387: int n;
388: int i;
389: int lf = strlen(filename);
390:
391: #ifndef NO_INIT
392: if (!HTSuffixes) HTFileInit();
393: #endif
394: n = HTList_count(HTSuffixes);
395: for(i=0; i<n; i++) {
396: int ls;
397: suff = HTList_objectAt(HTSuffixes, i);
398: ls = strlen(suff->suffix);
399: if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
400: return suff->rep; /* OK -- found */
401: }
402: }
1.4 timbl 403: return strchr(filename, '.') ? WWW_BINARY : WWW_PLAINTEXT; /* Dunno */
1.2 timbl 404: }
405:
406:
407: /* Determine value from file name
408: ** ------------------------------
1.1 timbl 409: **
410: */
411:
1.2 timbl 412: PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
413:
1.1 timbl 414: {
1.2 timbl 415: HTSuffix * suff;
416: int n;
417: int i;
418: int lf = strlen(filename);
419:
420: #ifndef NO_INIT
421: if (!HTSuffixes) HTFileInit();
422: #endif
423: n = HTList_count(HTSuffixes);
424: for(i=0; i<n; i++) {
425: int ls;
426: suff = HTList_objectAt(HTSuffixes, i);
427: ls = strlen(suff->suffix);
428: if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
429: if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
430: filename, suff->quality);
431: return suff->quality; /* OK -- found */
432: }
433: }
434: return 0.3; /* Dunno! */
1.1 timbl 435: }
436:
437:
438: /* Determine write access to a file
1.2 timbl 439: ** --------------------------------
440: **
441: ** On exit,
442: ** return value YES if file can be accessed and can be written to.
443: **
444: ** Bugs:
445: ** 1. No code for non-unix systems.
446: ** 2. Isn't there a quicker way?
1.1 timbl 447: */
448:
449: #ifdef vms
450: #define NO_GROUPS
451: #endif
452: #ifdef NO_UNIX_IO
453: #define NO_GROUPS
454: #endif
455: #ifdef PCNFS
456: #define NO_GROUPS
457: #endif
458:
459: PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
460: {
461: #ifdef NO_GROUPS
462: return NO; /* Safe answer till we find the correct algorithm */
463: #else
464: int groups[NGROUPS];
465: uid_t myUid;
466: int ngroups; /* The number of groups */
467: struct stat fileStatus;
468: int i;
469:
470: if (stat(filename, &fileStatus)) /* Get details of filename */
471: return NO; /* Can't even access file! */
472:
473: ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
474: myUid = geteuid(); /* Get my user identifier */
475:
476: if (TRACE) {
477: int i;
478: printf("File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
1.5 timbl 479: (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
480: fileStatus.st_gid,
1.1 timbl 481: myUid, ngroups);
482: for (i=0; i<ngroups; i++) printf(" %d", groups[i]);
483: printf(")\n");
484: }
485:
486: if (fileStatus.st_mode & 0002) /* I can write anyway? */
487: return YES;
488:
489: if ((fileStatus.st_mode & 0200) /* I can write my own file? */
490: && (fileStatus.st_uid == myUid))
491: return YES;
492:
493: if (fileStatus.st_mode & 0020) /* Group I am in can write? */
494: {
495: for (i=0; i<ngroups; i++) {
496: if (groups[i] == fileStatus.st_gid)
497: return YES;
498: }
499: }
500: if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
501: return NO; /* If no excuse, can't do */
502: #endif
503: }
504:
505:
1.2 timbl 506: /* Make a save stream
507: ** ------------------
508: **
509: ** The stream must be used for writing back the file.
510: ** @@@ no backup done
511: */
512: PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
513: {
514:
515: CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
516: char * localname = HTLocalName(addr);
517:
518: FILE* fp = fopen(localname, "w");
519: if (!fp) return NULL;
520:
521: return HTFWriter_new(fp);
522:
523: }
524:
525:
1.1 timbl 526: /* Load a document
527: ** ---------------
528: **
529: ** On entry,
530: ** addr must point to the fully qualified hypertext reference.
531: ** This is the physsical address of the file
532: **
533: ** On exit,
1.2 timbl 534: ** returns <0 Error has occured.
535: ** HTLOADED OK
1.1 timbl 536: **
537: */
1.2 timbl 538: PUBLIC int HTLoadFile ARGS4 (
1.1 timbl 539: CONST char *, addr,
540: HTParentAnchor *, anchor,
1.2 timbl 541: HTFormat, format_out,
542: HTStream *, sink
1.1 timbl 543: )
544: {
545: char * filename;
546: HTFormat format;
547: int fd = -1; /* Unix file descriptor number = INVALID */
548: char * nodename = 0;
549: char * newname=0; /* Simplified name of file */
550:
551: /* Reduce the filename to a basic form (hopefully unique!)
552: */
553: StrAllocCopy(newname, addr);
554: filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
555: nodename=HTParse(newname, "", PARSE_HOST);
556: free(newname);
557:
558: format = HTFileFormat(filename);
559:
560:
561: #ifdef vms
562: /* Assume that the file is in Unix-style syntax if it contains a '/'
563: after the leading one @@ */
564: {
565: char * vmsname = strchr(filename + 1, '/') ?
566: vms_name(nodename, filename) : filename + 1;
567: fd = open(vmsname, O_RDONLY, 0);
568:
569: /* If the file wasn't VMS syntax, then perhaps it is ultrix
570: */
571: if (fd<0) {
572: char ultrixname[INFINITY];
573: if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
574: sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
575: fd = open(ultrixname, O_RDONLY, 0);
576: if (fd<0) {
577: if (TRACE) fprintf(stderr,
578: "HTFile: Can't open as %s\n", ultrixname);
579: }
580: }
581: }
582: #else
583:
1.6 timbl 584: free(filename);
585:
1.1 timbl 586: /* For unix, we try to translate the name into the name of a transparently
587: ** mounted file.
588: **
589: ** Not allowed in secure (HTClienntHost) situations TBL 921019
590: */
591: #ifndef NO_UNIX_IO
1.2 timbl 592: /* @@@@ Need protection here for telnet server but not httpd server */
593:
594: /* if (!HTClientHost) */ { /* try local unix file system */
1.1 timbl 595: char * localname = HTLocalName(addr);
1.2 timbl 596: struct stat dir_info;
1.1 timbl 597:
598: #ifdef GOT_READ_DIR
1.2 timbl 599:
600: /* Multiformat handling
601: **
602: ** If needed, scan directory to find a good file.
603: ** Bug: we don't stat the file to find the length
604: */
605: if ( (strlen(localname) > strlen(MULTI_SUFFIX))
606: && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
607: MULTI_SUFFIX))) {
608: DIR *dp;
609: struct direct * dirbuf;
610:
611: float best = NO_VALUE_FOUND; /* So far best is bad */
612: HTFormat best_rep = NULL; /* Set when rep found */
613: struct direct best_dirbuf; /* Best dir entry so far */
614:
615: char * base = strrchr(localname, '/');
616: int baselen;
617:
618: if (!base || base == localname) goto forget_multi;
619: *base++ = 0; /* Just got directory name */
620: baselen = strlen(base)- strlen(MULTI_SUFFIX);
621: base[baselen] = 0; /* Chop off suffix */
622:
623: dp = opendir(localname);
624: if (!dp) {
625: forget_multi:
626: free(localname);
627: return HTLoadError(sink, 500,
628: "Multiformat: directory scan failed.");
629: }
630:
631: while (dirbuf = readdir(dp)) {
632: /* while there are directory entries to be read */
633: if (dirbuf->d_ino == 0) continue;
634: /* if the entry is not being used, skip it */
635:
636: if (dirbuf->d_namlen > baselen && /* Match? */
637: !strncmp(dirbuf->d_name, base, baselen)) {
638: HTFormat rep = HTFileFormat(dirbuf->d_name);
639: float value = HTStackValue(rep, format_out,
640: HTFileValue(dirbuf->d_name),
641: 0.0 /* @@@@@@ */);
642: if (value != NO_VALUE_FOUND) {
643: if (TRACE) fprintf(stderr,
644: "HTFile: value of presenting %s is %f\n",
645: HTAtom_name(rep), value);
646: if (value > best) {
647: best_rep = rep;
648: best = value;
649: best_dirbuf = *dirbuf;
650: }
651: } /* if best so far */
652: } /* if match */
653:
654: } /* end while directory entries left to read */
655: closedir(dp);
656:
657: if (best_rep) {
658: format = best_rep;
659: base[-1] = '/'; /* Restore directory name */
660: base[0] = 0;
661: StrAllocCat(localname, best_dirbuf.d_name);
662: goto open_file;
663:
664: } else { /* If not found suitable file */
665: free(localname);
666: return HTLoadError(sink, 403, /* List formats? */
667: "Could not find suitable representation for transmission.");
668: }
669: /*NOTREACHED*/
670: } /* if multi suffix */
1.1 timbl 671: /*
672: ** Check to see if the 'localname' is in fact a directory. If it is
673: ** create a new hypertext object containing a list of files and
674: ** subdirectories contained in the directory. All of these are links
675: ** to the directories or files listed.
676: ** NB This assumes the existance of a type 'struct direct', which will
677: ** hold the directory entry, and a type 'DIR' which is used to point to
678: ** the current directory being read.
679: */
680:
681:
682: if (stat(localname,&dir_info) == -1) { /* get file information */
683: /* if can't read file information */
684: if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
685:
686: } else { /* Stat was OK */
687:
688:
689: if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
690: /* if localname is a directory */
1.2 timbl 691:
692: HTStructured* target; /* HTML object */
693: HTStructuredClass targetClass;
694:
1.1 timbl 695: DIR *dp;
696: struct direct * dirbuf;
1.2 timbl 697:
1.6 timbl 698: char * logical;
699: char * tail;
700:
1.2 timbl 701: BOOL present[HTML_A_ATTRIBUTES];
702: char * value[HTML_A_ATTRIBUTES];
703:
1.1 timbl 704: char * tmpfilename = NULL;
1.2 timbl 705: struct stat file_info;
1.1 timbl 706:
707: if (TRACE)
708: fprintf(stderr,"%s is a directory\n",localname);
709:
1.2 timbl 710: /* Check directory access.
711: ** Selective access means only those directories containing a
712: ** marker file can be browsed
713: */
714: if (HTDirAccess == HT_DIR_FORBID) {
1.6 timbl 715: free(localname);
1.2 timbl 716: return HTLoadError(sink, 403,
717: "Directory browsing is not allowed.");
718: }
719:
720:
721: if (HTDirAccess == HT_DIR_SELECTIVE) {
722: char * enable_file_name =
723: malloc(strlen(localname)+ 1 +
724: strlen(HT_DIR_ENABLE_FILE) + 1);
725: strcpy(enable_file_name, localname);
726: strcat(enable_file_name, "/");
727: strcat(enable_file_name, HT_DIR_ENABLE_FILE);
728: if (stat(enable_file_name, &file_info) != 0) {
729: free(localname);
730: return HTLoadError(sink, 403,
731: "Selective access is not enabled for this directory");
732: }
733: }
734:
735:
736: dp = opendir(localname);
737: if (!dp) {
738: free(localname);
739: return HTLoadError(sink, 403, "This directory is not readable.");
740: }
741:
742:
743: /* Directory access is allowed and possible
744: */
1.6 timbl 745: logical = HTAnchor_address((HTAnchor*)anchor);
746: tail = strrchr(logical, '/') +1; /* last part or "" */
747:
1.4 timbl 748: target = HTML_new(anchor, format_out, sink);
1.2 timbl 749: targetClass = *target->isa; /* Copy routine entry points */
750:
751: { int i;
752: for(i=0; i<HTML_A_ATTRIBUTES; i++)
753: present[i] = (i==HTML_A_HREF);
754: }
1.1 timbl 755:
1.6 timbl 756: {
757: char * printable = NULL;
758: StrAllocCopy(printable, tail);
759: HTUnEscape(printable);
760: START(HTML_TITLE);
761: PUTS(*printable ? printable : "Welcome ");
762: PUTS(" directory");
763: END(HTML_TITLE);
764:
765: START(HTML_H1);
766: PUTS(*printable ? printable : "Welcome");
767: END(HTML_H1);
768: free(printable);
769: }
770:
1.2 timbl 771: if (HTDirReadme == HT_DIR_README_TOP)
772: do_readme(target, localname);
1.7 ! secret 773: {
! 774: HTBTree * bt = HTBTree_new((HTComparer)strcasecmp);
1.2 timbl 775:
1.7 ! secret 776: while (dirbuf = readdir(dp))
! 777: {
! 778: HTBTElement * dirname = NULL;
1.2 timbl 779:
780: /* while there are directory entries to be read */
1.7 ! secret 781: if (dirbuf->d_ino == 0)
! 782: /* if the entry is not being used, skip it */
! 783: continue;
1.1 timbl 784:
1.7 ! secret 785: if (!strcmp(dirbuf->d_name,"."))
! 786: continue; /* skip the entry for this directory */
1.2 timbl 787:
1.7 ! secret 788: if (strcmp(dirbuf->d_name,"..") != 0)
! 789: {
1.2 timbl 790: /* if the current entry is parent directory */
1.7 ! secret 791: if ((*(dirbuf->d_name)=='.') ||
! 792: (*(dirbuf->d_name)==','))
1.2 timbl 793: continue; /* skip those files whose name begins
794: with '.' or ',' */
1.7 ! secret 795: }
! 796: else
! 797: {
! 798: if (!*tail) continue; /* No up from top level */
! 799: }
! 800: dirname = malloc(strlen(dirbuf->d_name) + 2);
! 801: if (dirname == NULL) outofmem(__FILE__,"DirRead");
! 802: StrAllocCopy(tmpfilename,localname);
! 803: if (strcmp(localname,"/"))
! 804:
! 805: /* if filename is not root directory */
! 806: StrAllocCat(tmpfilename,"/");
! 807: else
! 808: if (!strcmp(dirbuf->d_name,".."))
! 809: continue;
! 810: /* if root directory and current entry is parent
! 811: directory, skip the current entry */
! 812:
! 813: StrAllocCat(tmpfilename,dirbuf->d_name);
! 814: stat(tmpfilename, &file_info);
! 815: if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
! 816: sprintf((char *)dirname,"D%s",dirbuf->d_name);
! 817: else sprintf((char *)dirname,"F%s",dirbuf->d_name);
! 818: /* D & F to have first directories, then files */
! 819: HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
1.6 timbl 820: }
1.7 ! secret 821:
! 822: /* Run through tree printing out in order
! 823: */
! 824: {
! 825: HTBTElement * next_element = HTBTree_next(bt,NULL);
! 826: /* pick up the first element of the list */
! 827: BOOL up_directory, first_of_directories,
! 828: list_of_directories_begun,
! 829: list_of_files_begun, first_of_files;
! 830:
! 831: up_directory = YES;
! 832: first_of_directories = YES;
! 833: first_of_files = YES;
! 834: list_of_directories_begun = NO;
! 835: list_of_files_begun = NO;
! 836: while (next_element != NULL)
! 837: {
! 838: StrAllocCopy(tmpfilename,localname);
! 839: if (strcmp(localname,"/"))
! 840:
1.2 timbl 841: /* if filename is not root directory */
1.7 ! secret 842: StrAllocCat(tmpfilename,"/");
! 843: else
! 844: if (!strcmp((char *)
! 845: (1 + HTBTree_object(next_element)),".."))
! 846: continue;
! 847: /* if root directory and current entry is parent
1.2 timbl 848: directory, skip the current entry */
1.7 ! secret 849:
! 850: StrAllocCat(tmpfilename,(char *)
! 851: (1 + HTBTree_object(next_element)));
! 852: /* append the current entry's filename to the path */
! 853: HTSimplify(tmpfilename);
! 854: /* Output the directory entry */
! 855: if ( *(char *)(HTBTree_object(next_element))=='D')
! 856: {
! 857: if (up_directory && !strcmp((char *)
! 858: (HTBTree_object(next_element)),"D.."))
! 859: up_directory = NO;
! 860: else
! 861: {
! 862: if (first_of_directories)
! 863: {
! 864: up_directory = NO;
! 865: /* to avoid problems with root directory */
! 866: first_of_directories = NO;
! 867: START(HTML_H2);
! 868: PUTS("Subdirectories:");
! 869: END(HTML_H2);
! 870: START(HTML_DIR);
! 871: list_of_directories_begun = YES;
! 872: }
! 873: START(HTML_LI);
! 874: }
! 875: }
! 876: if ( *(char *)(HTBTree_object(next_element))=='F')
! 877: {
! 878: if (first_of_files && list_of_directories_begun)
! 879: END(HTML_DIR);
! 880: if (first_of_files)
! 881: {
! 882: first_of_files = NO;
! 883: START(HTML_H2);
! 884: PUTS("Files:");
! 885: END(HTML_H2);
! 886: START(HTML_DIR);
! 887: list_of_files_begun = YES;
! 888: }
! 889: START(HTML_LI);
! 890: }
! 891: {
! 892: char * relative;
! 893: char * escaped = HTEscape((char *)(1 +
! 894: HTBTree_object(next_element)),
! 895: URL_XPALPHAS);
! 896: /* If empty tail, gives absolute ref below */
! 897: relative = (char*) malloc(
! 898: strlen(tail) + strlen(escaped)+2);
! 899: if (relative == NULL) outofmem(__FILE__, "DirRead");
! 900: sprintf(relative, "%s/%s", tail, escaped);
! 901: value[HTML_A_HREF] = relative;
! 902: (*targetClass.start_element)(
1.2 timbl 903: target,HTML_A, present, value);
1.7 ! secret 904: free(escaped);
! 905: free(relative);
! 906: }
! 907:
! 908: if (strcmp((char *)
! 909: (1 + HTBTree_object(next_element)),".."))
! 910: {
! 911:
! 912: /* if the current entry is not the parent
! 913: directory then use the file name */
! 914: PUTS((char *)(1 + HTBTree_object(next_element)));
! 915:
! 916: }
! 917: else
! 918: {
! 919: /* use name of parent directory */
! 920: char * endbit = strrchr(tmpfilename, '/');
! 921: PUTS("Up to ");
! 922: PUTS(*endbit ? endbit+1:tmpfilename);
! 923: }
! 924: END(HTML_A);
! 925: next_element = HTBTree_next(bt,next_element);
! 926: /* pick up the next element of the list;
! 927: if none, return NULL*/
! 928: }
! 929: if (list_of_files_begun)
! 930: END(HTML_DIR);
1.2 timbl 931: }
1.7 ! secret 932: /* end while directory entries left to read */
! 933: closedir(dp);
! 934: free(logical);
! 935: free(tmpfilename);
! 936: HTBTreeAndObject_free(bt);
! 937:
! 938: if (HTDirReadme == HT_DIR_README_BOTTOM)
! 939: do_readme(target, localname);
! 940: END_TARGET;
! 941: FREE_TARGET;
! 942: free(localname);
! 943: return HT_LOADED; /* document loaded */
1.2 timbl 944: }
945:
1.1 timbl 946: } /* end if localname is directory */
947:
948: } /* end if file stat worked */
949:
950: /* End of directory reading section
951: */
952: #endif
1.2 timbl 953: open_file:
1.1 timbl 954: fd = open(localname, O_RDONLY, 0);
955: if(TRACE) printf ("HTAccess: Opening `%s' gives %d\n",
956: localname, fd);
1.2 timbl 957: if (fd>=0) { /* Good! */
958: if (HTEditable(localname)) {
959: HTAtom * put = HTAtom_for("PUT");
960: HTList * methods = HTAnchor_methods(anchor);
961: if (HTList_indexOf(methods, put) == (-1)) {
962: HTList_addObject(methods, put);
963: }
964: }
1.1 timbl 965: free(localname);
1.2 timbl 966: }
1.1 timbl 967: } /* local unix file system */
968: #endif
969: #endif
970:
971: #ifndef DECNET
972: /* Now, as transparently mounted access has failed, we try FTP.
973: */
1.2 timbl 974: if (fd<0) {
1.1 timbl 975: if (strcmp(nodename, HTHostName())!=0) {
1.2 timbl 976: fd = HTFTPLoad(addr, anchor, format_out, sink);
977: if (fd<0) return HTInetStatus("FTP file load");
978: return fd;
979: }
1.1 timbl 980: }
981: #endif
982:
983: /* All attempts have failed if fd<0.
984: */
1.2 timbl 985: if (fd<0) {
986: if (TRACE)
1.6 timbl 987: printf("Can't open `%s', errno=%d\n", addr, errno);
1.2 timbl 988: return HTLoadError(sink, 403, "Can't access requested file.");
989: }
1.1 timbl 990:
1.2 timbl 991: HTParseSocket(format, format_out, anchor, fd, sink);
992: NETCLOSE(fd);
993: return HT_LOADED;
1.1 timbl 994:
995: }
996:
997: /* Protocol descriptors
998: */
999: PUBLIC HTProtocol HTFTP = { "ftp", HTLoadFile, 0 };
1.2 timbl 1000: PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };
Webmaster