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