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