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