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