Annotation of libwww/Library/src/HTFile.c, revision 1.89
1.72 frystyk 1: /* HTFile.c
2: ** FILE ACCESS
3: **
1.79 frystyk 4: ** (c) COPYRIGHT MIT 1995.
1.72 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.1 timbl 6: **
7: ** This is unix-specific code in general, with some VMS bits.
1.8 timbl 8: ** These are routines for file access used by browsers.
1.1 timbl 9: **
10: ** History:
11: ** Feb 91 Written Tim Berners-Lee CERN/CN
12: ** Apr 91 vms-vms access included using DECnet syntax
13: ** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
14: ** Fixed access bug for relative names on VMS.
1.28 duns 15: ** Sep 93 (MD) Access to VMS files allows sharing.
16: ** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C
1.52 duns 17: ** 22 Feb 94 (MD) Excluded two routines if we are not READING directories
1.61 frystyk 18: ** 18 May 94 (HF) Directory stuff removed and stream handling updated,
19: ** error messages introduced etc.
1.1 timbl 20: **
21: ** Bugs:
1.2 timbl 22: ** FTP: Cannot access VMS files from a unix machine.
23: ** How can we know that the
1.1 timbl 24: ** target machine runs VMS?
25: */
26:
1.67 frystyk 27: /* Library Includes */
1.75 frystyk 28: #include "tcp.h"
1.67 frystyk 29: #include "HTUtils.h"
1.75 frystyk 30: #include "HTString.h"
1.1 timbl 31: #include "HTParse.h"
32: #include "HTTCP.h"
1.86 frystyk 33: #include "HTMIME.h"
1.1 timbl 34: #include "HTAnchor.h"
1.2 timbl 35: #include "HTAtom.h"
36: #include "HTWriter.h"
1.75 frystyk 37: #include "HTFWrite.h"
1.52 duns 38: #include "HTFormat.h"
1.54 luotonen 39: #include "HTMulti.h"
1.78 frystyk 40: #include "HTDirBrw.h"
41: #include "HTBind.h"
1.80 frystyk 42: #include "HTSocket.h"
43: #include "HTThread.h"
1.61 frystyk 44: #include "HTError.h"
1.67 frystyk 45: #include "HTFile.h" /* Implemented here */
46:
1.80 frystyk 47: /* This is the local definition of HTRequest->net_info */
48: typedef enum _FileState {
1.85 frystyk 49: FS_FILE_RETRY = -4,
50: FS_FILE_ERROR = -3,
1.86 frystyk 51: FS_FILE_NO_DATA = -2,
52: FS_FILE_GOT_DATA = -1,
1.85 frystyk 53: FS_FILE_BEGIN = 0,
1.86 frystyk 54: FS_FILE_DO_CN,
1.85 frystyk 55: FS_FILE_NEED_OPEN_FILE,
56: FS_FILE_NEED_TARGET,
57: FS_FILE_NEED_BODY,
58: FS_FILE_PARSE_DIR,
59: FS_FILE_TRY_FTP
1.80 frystyk 60: } FileState;
61:
1.77 frystyk 62: typedef struct _file_info {
63: SOCKFD sockfd; /* Socket descripter */
64: SockA sock_addr; /* SockA is defined in tcp.h */
65: HTInputSocket * isoc; /* Input buffer */
66: SocAction action; /* Result of the select call */
67: HTStream * target; /* Target stream */
68: int addressCount; /* Attempts if multi-homed host */
69: time_t connecttime; /* Used on multihomed hosts */
1.89 ! frystyk 70: long bytes_read; /* Bytes read from network */
1.77 frystyk 71: struct _HTRequest * request; /* Link back to request structure */
1.80 frystyk 72:
73: FileState state; /* Current state of the connection */
74: char * localname; /* Local representation of file name */
75: FILE * fp; /* If we can't use sockets on local files */
1.77 frystyk 76: } file_info;
1.2 timbl 77:
1.80 frystyk 78: /* Local definition */
79: struct _HTStream {
80: CONST HTStreamClass * isa;
81: HTStream * target;
82: };
83:
1.77 frystyk 84: /* Controlling globals */
1.33 timbl 85: PUBLIC BOOL HTTakeBackup = YES;
1.1 timbl 86:
87: PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */
1.2 timbl 88:
1.61 frystyk 89: /* ------------------------------------------------------------------------- */
1.11 timbl 90:
1.80 frystyk 91: /* Convert file URLs into a local representation
92: ** ---------------------------------------------
93: ** The URL has already been translated through the rules in get_physical
94: ** in HTAccess.c and all we need to do now is to map the path to a local
95: ** representation, for example if must translate '/' to the ones that
96: ** turn the wrong way ;-)
97: **
98: ** On Entry
99: ** The full URL that's is going to be translated
100: ** On Exit,
101: ** -1 Error
102: ** 0 URL is not pointing to the local file system
103: ** 1 A local name is returned in `local' which must be freed
104: ** by caller
1.1 timbl 105: */
1.80 frystyk 106: PRIVATE int HTLocalName ARGS2(CONST char *, url, char **, local)
1.1 timbl 107: {
1.80 frystyk 108: if (url) {
109: char * access = HTParse(url, "", PARSE_ACCESS);
110: char * host = HTParse(url, "", PARSE_HOST);
111: char * path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
112: CONST char *myhost;
113:
114: /* Find out if this is a reference to the local file system */
115: if ((*access && strcmp(access, "file")) ||
116: (*host && strcasecomp(host, "localhost") &&
117: (myhost=HTGetHostName()) && strcmp(host, myhost))) {
118: if (PROT_TRACE)
119: fprintf(TDEST, "LocalName... Not on local file system\n");
120: free(access);
121: free(host);
122: free(path);
123: return 0;
124: } else {
125: /*
126: ** Do whatever translation is required here in order to fit your
127: ** platform _before_ the path is unescaped.
128: */
1.65 duns 129: #if VMS
1.80 frystyk 130: HTVMS_checkDecnet(path);
131: #endif
1.77 frystyk 132: #ifdef _WINDOWS
133: /* an absolute pathname with logical drive */
134: if (*path == '/' && path[2] == ':')
135: /* NB. need memmove because overlaps */
1.80 frystyk 136: memmove( path, path+1, strlen(path) + 1);
1.77 frystyk 137: #endif
1.80 frystyk 138:
139: HTUnEscape(path); /* Take out the escaped characters */
140: if (PROT_TRACE)
141: fprintf(TDEST, "Node........ `%s' means path `%s'\n",url,path);
142: free(access);
1.1 timbl 143: free(host);
1.80 frystyk 144: *local = path;
145: return 1;
1.1 timbl 146: }
147: }
1.80 frystyk 148: return -1;
1.1 timbl 149: }
150:
151:
152: /* Make a WWW name from a full local path name
153: **
154: ** Bugs:
155: ** At present, only the names of two network root nodes are hand-coded
156: ** in and valid for the NeXT only. This should be configurable in
157: ** the general case.
158: */
159:
160: PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
161: {
162: char * result;
163: #ifdef NeXT
164: if (0==strncmp("/private/Net/", name, 13)) {
165: result = (char *)malloc(7+strlen(name+13)+1);
166: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
167: sprintf(result, "file://%s", name+13);
168: } else
169: #endif
170: if (0==strncmp(HTMountRoot, name, 5)) {
171: result = (char *)malloc(7+strlen(name+5)+1);
172: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
173: sprintf(result, "file://%s", name+5);
174: } else {
1.63 frystyk 175: result = (char *)malloc(7+strlen(HTGetHostName())+strlen(name)+1);
1.1 timbl 176: if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
1.63 frystyk 177: sprintf(result, "file://%s%s", HTGetHostName(), name);
1.1 timbl 178: }
1.75 frystyk 179: if (TRACE) fprintf(TDEST, "File `%s'\n\tmeans node `%s'\n", name, result);
1.1 timbl 180: return result;
181: }
182:
183:
184: /* Determine write access to a file
1.2 timbl 185: ** --------------------------------
1.80 frystyk 186: ** If stat_info is NULL then the function calls stat() on it's own,
187: ** otherwise it uses the information found in stat_info
1.2 timbl 188: ** On exit,
189: ** return value YES if file can be accessed and can be written to.
190: **
191: ** Bugs:
192: ** 1. No code for non-unix systems.
193: ** 2. Isn't there a quicker way?
1.1 timbl 194: */
1.80 frystyk 195: PUBLIC BOOL HTEditable ARGS2(CONST char *, filename, struct stat *, stat_info)
196: {
1.75 frystyk 197: #ifndef NO_UNIX_IO
198: #ifdef NO_GROUPS
1.80 frystyk 199: return NO; /* Safe answer till we find the correct algorithm */
1.1 timbl 200: #else
1.75 frystyk 201: #ifdef NeXT
1.80 frystyk 202: int groups[NGROUPS];
1.75 frystyk 203: #else
1.80 frystyk 204: gid_t groups[NGROUPS];
1.75 frystyk 205: #endif
1.80 frystyk 206: int i;
207: uid_t myUid;
208: int ngroups; /* The number of groups */
1.1 timbl 209: struct stat fileStatus;
1.80 frystyk 210: struct stat *fileptr = stat_info ? stat_info : &fileStatus;
1.1 timbl 211:
1.80 frystyk 212: if (!stat_info) {
1.88 frystyk 213: if (STAT(filename, &fileStatus))
1.80 frystyk 214: return NO; /* Can't even access file! */
215: }
1.1 timbl 216: ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
217: myUid = geteuid(); /* Get my user identifier */
218:
219: if (TRACE) {
220: int i;
1.75 frystyk 221: fprintf(TDEST,
1.19 timbl 222: "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
1.80 frystyk 223: (unsigned int) fileptr->st_mode, (int) fileptr->st_uid,
224: (int) fileptr->st_gid, (int) myUid, ngroups);
1.75 frystyk 225: for (i=0; i<ngroups; i++) fprintf(TDEST, " %d", (int) groups[i]);
226: fprintf(TDEST, ")\n");
1.1 timbl 227: }
228:
1.80 frystyk 229: if (fileptr->st_mode & 0002) /* I can write anyway? */
1.1 timbl 230: return YES;
231:
1.80 frystyk 232: if ((fileptr->st_mode & 0200) /* I can write my own file? */
233: && (fileptr->st_uid == myUid))
1.1 timbl 234: return YES;
235:
1.80 frystyk 236: if (fileptr->st_mode & 0020) /* Group I am in can write? */
1.1 timbl 237: {
238: for (i=0; i<ngroups; i++) {
1.80 frystyk 239: if (groups[i] == fileptr->st_gid)
1.1 timbl 240: return YES;
241: }
242: }
1.75 frystyk 243: if (TRACE) fprintf(TDEST, "\tFile is not editable.\n");
1.1 timbl 244: return NO; /* If no excuse, can't do */
245: #endif
1.80 frystyk 246: #else
247: /*
248: ** We don't know and can't find out. Can we be sure that when opening
249: ** a file in mode "a" that the file is not modified?
250: */
251: return NO;
252: #endif /* !NO_UNIX_IO */
1.1 timbl 253: }
1.80 frystyk 254:
1.1 timbl 255:
1.2 timbl 256: /* Make a save stream
257: ** ------------------
258: **
259: ** The stream must be used for writing back the file.
260: ** @@@ no backup done
261: */
1.29 timbl 262: PUBLIC HTStream * HTFileSaveStream ARGS1(HTRequest *, request)
1.2 timbl 263: {
264:
1.29 timbl 265: CONST char * addr = HTAnchor_address((HTAnchor*)request->anchor);
1.80 frystyk 266: char * filename;
1.33 timbl 267: FILE* fp;
1.80 frystyk 268: HTLocalName(addr, &filename);
1.33 timbl 269:
270: /* @ Introduce CVS handling here one day
271: */
272: /* Take a backup before we do anything silly 931205
273: */
274: if (HTTakeBackup) {
1.69 frystyk 275: char * p;
276: char * backup_filename = (char *) malloc(strlen(filename)+2);
1.33 timbl 277: if (!backup_filename) outofmem(__FILE__, "taking backup");
278: strcpy(backup_filename, filename);
1.34 timbl 279: for(p=backup_filename+strlen(backup_filename);; p--) {
1.33 timbl 280: if ((*p=='/') || (p<backup_filename)) {
281: p[1]=','; /* Insert comma after slash */
282: break;
283: }
284: p[1] = p[0]; /* Move up everything to the right of it */
285: }
286:
1.65 duns 287:
288: #ifdef VMS
289: if ((fp=fopen(filename, "r", "shr=put", "shr=upd"))) { /* File exists */
290: #else /* not VMS */
1.35 luotonen 291: if ((fp=fopen(filename, "r"))) { /* File exists */
1.65 duns 292: #endif /* not VMS */
1.33 timbl 293: fclose(fp);
1.75 frystyk 294: if (TRACE) fprintf(TDEST, "File `%s' exists\n", filename);
1.83 frystyk 295: if (REMOVE(backup_filename)) {
1.75 frystyk 296: if (TRACE) fprintf(TDEST, "Backup file `%s' removed\n",
1.56 frystyk 297: backup_filename);
1.33 timbl 298: }
299: if (rename(filename, backup_filename)) { /* != 0 => Failure */
1.75 frystyk 300: if (TRACE) fprintf(TDEST, "Rename `%s' to `%s' FAILED!\n",
1.56 frystyk 301: filename, backup_filename);
1.33 timbl 302: } else { /* Success */
1.75 frystyk 303: if (TRACE) fprintf(TDEST, "Renamed `%s' to `%s'\n", filename,
1.56 frystyk 304: backup_filename);
1.33 timbl 305: }
306: }
307: free(backup_filename);
308: } /* if take backup */
1.2 timbl 309:
1.89 ! frystyk 310: if ((fp = fopen(filename, "wb")) == NULL) {
1.75 frystyk 311: HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
1.61 frystyk 312: return NULL;
313: } else
1.40 frystyk 314: return HTFWriter_new(fp, NO);
1.2 timbl 315: }
316:
1.10 secret 317:
1.80 frystyk 318: /* FileCleanup
319: **
320: ** This function closes the connection and frees memory.
321: **
322: ** Returns 0 on OK, else -1
323: */
324: PRIVATE int FileCleanup ARGS2(HTRequest *, req, BOOL, abort)
325: {
326: file_info *file;
327: int status = 0;
328: if (!req || !req->net_info) {
329: if (PROT_TRACE) fprintf(TDEST, "FileCleanup. Bad argument!\n");
330: status = -1;
331: } else {
332: file = (file_info *) req->net_info;
1.82 frystyk 333: if (file->sockfd != INVSOC || file->fp) {
1.80 frystyk 334:
335: if (file->target) {
1.83 frystyk 336:
337: /* Free stream with data FROM local file system */
1.80 frystyk 338: if (abort)
339: (*file->target->isa->abort)(file->target, NULL);
340: else
341: (*file->target->isa->_free)(file->target);
342: }
343:
1.82 frystyk 344: #ifndef NO_UNIX_IO
1.80 frystyk 345: if (PROT_TRACE)
346: fprintf(TDEST,"FILE........ Closing socket %d\n",file->sockfd);
347: if ((status = NETCLOSE(file->sockfd)) < 0)
348: HTErrorSysAdd(file->request, ERR_FATAL, socerrno, NO,
349: "NETCLOSE");
350: HTThreadState(file->sockfd, THD_CLOSE);
351: file->sockfd = INVSOC;
1.82 frystyk 352: #else
353: if (PROT_TRACE)
354: fprintf(TDEST,"FILE........ Closing file %p\n", file->fp);
355: fclose(file->fp);
356: file->fp = NULL;
357: #endif
1.83 frystyk 358: }
1.80 frystyk 359: HTThread_clear((HTNetInfo *) file);
360: if (file->isoc)
361: HTInputSocket_free(file->isoc);
362: FREE(file->localname);
363: free(file);
364: req->net_info = NULL;
365: }
366: return status;
367: }
368:
369:
1.1 timbl 370: /* Load a document
371: ** ---------------
372: **
373: ** On entry,
1.80 frystyk 374: ** request This is the request structure
1.1 timbl 375: ** On exit,
1.77 frystyk 376: ** returns HT_ERROR Error has occured or interrupted
377: ** HT_WOULD_BLOCK We are using blocking I/O so this
378: ** indicates that we have a read a chunk
379: ** of data and now want to see what's up
380: ** in the eventloop.
1.80 frystyk 381: ** HT_LOADED if file has been loaded successfully
382: ** HT_NO_DATA if file is empty
383: ** HT_RETRY if file service is unavailable
1.1 timbl 384: */
1.36 luotonen 385: PUBLIC int HTLoadFile ARGS1 (HTRequest *, request)
1.1 timbl 386: {
1.80 frystyk 387: int status = HT_ERROR;
388: file_info *file; /* Specific access information */
1.45 luotonen 389:
1.61 frystyk 390: if (!request || !request->anchor) {
1.75 frystyk 391: if (TRACE) fprintf(TDEST, "HTLoadFile.. Called with bad argument\n");
1.77 frystyk 392: return HT_ERROR;
1.50 frystyk 393: }
1.1 timbl 394:
1.80 frystyk 395: /* Only do the setup first time through. This is actually state FILE_BEGIN
396: but it can't be in the state machine as we need the structure first */
397: if (!request->net_info) {
398: if (PROT_TRACE) {
399: char *url = HTAnchor_physical(request->anchor);
400: fprintf(TDEST, "LoadFile.... Looking for `%s\'\n", url);
401: }
402: if ((file = (file_info *) calloc(1, sizeof(file_info))) == NULL)
403: outofmem(__FILE__, "HTLoadFILE");
404: file->sockfd = INVSOC; /* Invalid socket number */
405: file->request = request;
1.85 frystyk 406: file->state = FS_FILE_BEGIN;
1.80 frystyk 407: request->net_info = (HTNetInfo *) file;
408: HTThread_new((HTNetInfo *) file);
409: } else {
410: file = (file_info *) request->net_info; /* Get existing copy */
1.78 frystyk 411:
1.80 frystyk 412: /* @@@ NEED ALSO TO CHECK FOR ANSI FILE DESCRIPTORS @@@ */
1.83 frystyk 413: if (HTThreadIntr(file->sockfd)) {
414: FileCleanup(request, YES);
415: return HTRequest_isMainDestination(request) ? HT_ERROR : HT_OK;
416: }
1.80 frystyk 417: }
1.36 luotonen 418:
1.80 frystyk 419: /* Now jump into the machine. We know the state from the previous run */
420: while (1) {
421: switch (file->state) {
1.85 frystyk 422: case FS_FILE_BEGIN:
1.80 frystyk 423: if (HTSecure) {
424: if (PROT_TRACE)
425: fprintf(TDEST, "LoadFile.... No access to local file system\n");
1.85 frystyk 426: file->state = FS_FILE_TRY_FTP;
1.80 frystyk 427: break;
428: }
429: if ((status = HTLocalName(HTAnchor_physical(request->anchor),
430: &file->localname)) == -1) {
1.85 frystyk 431: file->state = FS_FILE_ERROR;
1.80 frystyk 432: break;
433: } else if (status == 0) {
1.85 frystyk 434: file->state = FS_FILE_TRY_FTP;
1.80 frystyk 435: break;
1.2 timbl 436: }
1.86 frystyk 437:
438: /* If cache element then jump directly to OPEN FILE state */
439: file->state = HTAnchor_cacheHit(request->anchor) ?
440: FS_FILE_NEED_OPEN_FILE : FS_FILE_DO_CN;
441: break;
442:
443: case FS_FILE_DO_CN:
1.80 frystyk 444: /*
445: ** If we have to do content negotiation then find the object that
446: ** fits best into either what the client has indicated in the
1.86 frystyk 447: ** accept headers or what the client has registered on its own.
1.80 frystyk 448: ** The object chosen can in fact be a directory! However, content
449: ** negotiation only makes sense it we can read the directory!
450: ** We stat the file in order to find the size and to see it if
451: ** exists.
452: */
453: {
454: struct stat stat_info; /* Contains actual file chosen */
455: if (request->ContentNegotiation) {
456: char *new_path=HTMulti(request,file->localname,&stat_info);
457: if (new_path) {
458: FREE(file->localname);
459: file->localname = new_path;
460: HTAnchor_setPhysical(request->anchor, new_path);
461: } else {
1.85 frystyk 462: file->state = FS_FILE_ERROR;
1.80 frystyk 463: break;
464: }
465: } else {
1.88 frystyk 466: if (STAT(file->localname, &stat_info) == -1) {
1.80 frystyk 467: if (PROT_TRACE)
468: fprintf(TDEST, "HTLoadFile.. Can't stat %s\n",
469: file->localname);
1.81 frystyk 470: HTErrorAdd(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
471: NULL, 0, "HTLoadFile");
1.85 frystyk 472: file->state = FS_FILE_ERROR;
1.80 frystyk 473: break;
474: }
475: }
476: /* Check to see if the 'localname' is in fact a directory */
477: if (((stat_info.st_mode) & S_IFMT) == S_IFDIR)
1.85 frystyk 478: file->state = FS_FILE_PARSE_DIR;
1.80 frystyk 479: else {
480: /*
481: ** If empty file then only serve it if it is editable
482: */
483: BOOL editable = HTEditable(file->localname, &stat_info);
484: HTBind_getBindings(request->anchor);
485: if (editable)
486: HTAnchor_appendMethods(request->anchor, METHOD_PUT);
487: if (stat_info.st_size)
488: HTAnchor_setLength(request->anchor, stat_info.st_size);
489:
1.83 frystyk 490: /* Done with relevant metainformation in anchor */
491: HTAnchor_setHeaderParsed(request->anchor);
1.80 frystyk 492:
493: if (!editable && !stat_info.st_size) {
1.81 frystyk 494: HTErrorAdd(request, ERR_FATAL, NO, HTERR_NO_CONTENT,
1.80 frystyk 495: NULL, 0, "HTLoadFile");
1.85 frystyk 496: file->state = FS_FILE_NO_DATA;
1.80 frystyk 497: } else
1.85 frystyk 498: file->state = FS_FILE_NEED_OPEN_FILE;
1.80 frystyk 499: }
1.2 timbl 500: }
1.80 frystyk 501: break;
1.61 frystyk 502:
1.85 frystyk 503: case FS_FILE_NEED_OPEN_FILE:
1.80 frystyk 504: /*
505: ** If we have unix file descriptors then use this otherwise use
506: ** the ANSI C file descriptors
507: */
508: #ifndef NO_UNIX_IO
509: if ((file->sockfd = open(file->localname, O_RDONLY)) == -1) {
510: HTErrorSysAdd(request, ERR_FATAL, errno, NO, "open");
1.85 frystyk 511: file->state = FS_FILE_ERROR;
1.82 frystyk 512: break;
513: }
514: if (PROT_TRACE)
515: fprintf(TDEST,"HTLoadFile.. `%s' opened using fd %d \n",
516: file->localname, file->sockfd);
1.80 frystyk 517:
1.82 frystyk 518: /* If non-blocking protocol then change socket status
519: ** I use FCNTL so that I can ask the status before I set it.
520: ** See W. Richard Stevens (Advan. Prog. in UNIX env, p.364)
521: ** Be CAREFULL with the old `O_NDELAY' - it wont work as read()
522: ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD
523: ** and does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
524: */
1.85 frystyk 525: #ifndef NO_FCNTL
1.84 frystyk 526: if (!HTProtocol_isBlocking(request)) {
1.82 frystyk 527: if ((status = FCNTL(file->sockfd, F_GETFL, 0)) != -1) {
528: status |= O_NONBLOCK; /* POSIX */
529: status = FCNTL(file->sockfd, F_SETFL, status);
530: }
531: if (PROT_TRACE) {
532: if (status == -1)
533: fprintf(TDEST, "HTLoadFile.. Can't make socket non-blocking\n");
534: else
535: fprintf(TDEST,"HTLoadFile.. Using NON_BLOCKING I/O\n");
1.80 frystyk 536: }
1.61 frystyk 537: }
1.85 frystyk 538: #endif /* NO_FCNTL */
1.80 frystyk 539: #else
1.83 frystyk 540: #ifdef VMS
1.82 frystyk 541: if (!(file->fp = fopen(file->localname,"r","shr=put","shr=upd"))) {
1.80 frystyk 542: #else
1.82 frystyk 543: if ((file->fp = fopen(file->localname,"r")) == NULL) {
544: #endif /* !VMS */
1.80 frystyk 545: HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
1.85 frystyk 546: file->state = FS_FILE_ERROR;
1.82 frystyk 547: break;
1.80 frystyk 548: }
1.82 frystyk 549: if (PROT_TRACE)
550: fprintf(TDEST,"HTLoadFile.. `%s' opened using FILE %p\n",
551: file->localname, file->fp);
1.80 frystyk 552: #endif /* !NO_UNIX_IO */
1.85 frystyk 553: file->state = FS_FILE_NEED_TARGET;
1.83 frystyk 554: break;
555:
1.85 frystyk 556: case FS_FILE_NEED_TARGET:
1.83 frystyk 557: /*
558: ** We need to wait for the destinations to get ready
559: */
560: if (HTRequest_isSource(request) && !request->output_stream)
561: return HT_WOULD_BLOCK;
562: /*
563: ** Set up concurrent read/write if this request isn't the
564: ** source for a PUT or POST. As source we don't start reading
565: ** before all destinations are ready. If destination then
566: ** register the input stream and get ready for read
567: */
568: if (HTRequest_isPostWeb(request)) {
569: HTThreadState(file->sockfd, THD_SET_READ);
570: HTRequest_linkDestination(request);
571: }
1.82 frystyk 572:
1.86 frystyk 573: /*
574: ** Set up read buffer and streams.
575: ** If cache element, we know that it's MIME, so call MIME parser
576: ** If ANSI then sockfd=INVSOC
577: */
1.82 frystyk 578: file->isoc = HTInputSocket_new(file->sockfd);
1.86 frystyk 579: if (HTAnchor_cacheHit(request->anchor))
580: file->target = HTMIMEConvert(request, NULL, WWW_MIME,
581: request->output_format,
582: request->output_stream);
583: else
584: file->target = HTStreamStack(HTAnchor_format(request->anchor),
585: request->output_format,
586: request->output_stream,
587: request, YES);
1.85 frystyk 588: file->state = file->target ? FS_FILE_NEED_BODY : FS_FILE_ERROR;
1.80 frystyk 589: break;
1.65 duns 590:
1.85 frystyk 591: case FS_FILE_NEED_BODY:
1.80 frystyk 592: #ifndef NO_UNIX_IO
593: status = HTSocketRead(request, file->target);
1.82 frystyk 594: #else
595: status = HTFileRead(file->fp, request, file->target);
596: #endif
1.80 frystyk 597: if (status == HT_WOULD_BLOCK)
598: return HT_WOULD_BLOCK;
599: else if (status == HT_INTERRUPTED)
1.85 frystyk 600: file->state = FS_FILE_ERROR;
1.80 frystyk 601: else if (status == HT_LOADED) {
1.85 frystyk 602: file->state = FS_FILE_GOT_DATA;
1.61 frystyk 603: } else
1.85 frystyk 604: file->state = FS_FILE_ERROR;
1.80 frystyk 605: break;
1.1 timbl 606:
1.85 frystyk 607: case FS_FILE_PARSE_DIR:
1.80 frystyk 608: #ifdef GOT_READ_DIR
609: file->state = HTBrowseDirectory(request, file->localname) < 0 ?
1.85 frystyk 610: FS_FILE_ERROR : FS_FILE_GOT_DATA;
1.80 frystyk 611: #else
1.85 frystyk 612: file->state = FS_FILE_ERROR;
1.1 timbl 613: #endif
1.80 frystyk 614: break;
615:
1.85 frystyk 616: case FS_FILE_TRY_FTP:
1.80 frystyk 617: {
618: char *url = HTAnchor_physical(request->anchor);
619: HTAnchor *anchor;
620: char *newname = NULL;
621: StrAllocCopy(newname, "ftp:");
622: if (!strncmp(url, "file:", 5))
623: StrAllocCat(newname, url+5);
624: else
625: StrAllocCat(newname, url);
626: anchor = HTAnchor_findAddress(newname);
627: free(newname);
628: FileCleanup(request, NO);
629: return HTLoadAnchorRecursive(anchor, request);
630: }
631: break;
1.1 timbl 632:
1.85 frystyk 633: case FS_FILE_GOT_DATA:
1.80 frystyk 634: FileCleanup(request, NO);
1.83 frystyk 635: if (HTRequest_isPostWeb(request)) {
636: BOOL main = HTRequest_isMainDestination(request);
1.87 frystyk 637: if (HTRequest_isDestination(request)) {
638: HTLink *link =
639: HTAnchor_findLink((HTAnchor *) request->source->anchor,
640: (HTAnchor *) request->anchor);
641: HTAnchor_setLinkResult(link, HT_LINK_OK);
642: }
1.83 frystyk 643: HTRequest_removeDestination(request);
644: return main ? HT_LOADED : HT_OK;
645: }
646: return HT_LOADED;
1.80 frystyk 647: break;
648:
1.85 frystyk 649: case FS_FILE_NO_DATA:
1.80 frystyk 650: FileCleanup(request, NO);
1.83 frystyk 651: if (HTRequest_isPostWeb(request)) {
652: BOOL main = HTRequest_isMainDestination(request);
1.87 frystyk 653: if (HTRequest_isDestination(request)) {
654: HTLink *link =
655: HTAnchor_findLink((HTAnchor *) request->source->anchor,
656: (HTAnchor *) request->anchor);
657: HTAnchor_setLinkResult(link, HT_LINK_OK);
658: }
1.83 frystyk 659: HTRequest_removeDestination(request);
660: return main ? HT_NO_DATA : HT_OK;
661: }
1.80 frystyk 662: return HT_NO_DATA;
663: break;
664:
1.85 frystyk 665: case FS_FILE_RETRY:
1.83 frystyk 666: if (HTRequest_isPostWeb(request)) {
667: BOOL main = HTRequest_isMainDestination(request);
1.87 frystyk 668: HTRequest_killPostWeb(request);
669: if (HTRequest_isDestination(request)) {
670: HTLink *link =
671: HTAnchor_findLink((HTAnchor *) request->source->anchor,
672: (HTAnchor *) request->anchor);
673: HTAnchor_setLinkResult(link, HT_LINK_ERROR);
674: }
1.83 frystyk 675: HTRequest_removeDestination(request);
676: return main ? HT_RETRY : HT_OK;
1.87 frystyk 677: } else
678: FileCleanup(request, YES);
1.80 frystyk 679: return HT_RETRY;
680: break;
681:
1.85 frystyk 682: case FS_FILE_ERROR:
1.83 frystyk 683: /* Clean up the other connections or just this one */
684: if (HTRequest_isPostWeb(request)) {
1.87 frystyk 685: BOOL main = HTRequest_isMainDestination(request);
1.83 frystyk 686: if (file->sockfd == INVSOC)
687: FileCleanup(request, YES); /* If no valid socket */
688: HTRequest_killPostWeb(request);
1.87 frystyk 689: if (HTRequest_isDestination(request)) {
690: HTLink *link =
691: HTAnchor_findLink((HTAnchor *) request->source->anchor,
692: (HTAnchor *) request->anchor);
693: HTAnchor_setLinkResult(link, HT_LINK_ERROR);
694: }
695: HTRequest_removeDestination(request);
696: return main ? HT_ERROR : HT_OK;
1.83 frystyk 697: } else
698: FileCleanup(request, YES);
1.80 frystyk 699: return HT_ERROR;
700: break;
701: }
702: } /* End of while(1) */
1.1 timbl 703: }
704:
1.80 frystyk 705:
706: /*
707: ** Protocol descriptor
708: */
1.84 frystyk 709:
1.80 frystyk 710: GLOBALDEF PUBLIC HTProtocol HTFile = {
711: "file", SOC_NON_BLOCK, HTLoadFile, HTFileSaveStream, NULL
712: };
1.84 frystyk 713:
Webmaster