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