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