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