1 /*								       HTFile.c
2 **	FILE ACCESS
3 **
4 **	(c) COPYRIGHT MIT 1995.
5 **	Please first read the full copyright statement in the file COPYRIGH.
6 **	@(#) $Id$
7 **
8 **	This is unix-specific code in general, with some VMS bits.
9 **	These are routines for file access used by browsers.
10 **
11 ** History:
12 **	   Feb 91	Written Tim Berners-Lee CERN/CN
13 **	   Apr 91	vms-vms access included using DECnet syntax
14 **	26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
15 **			Fixed access bug for relative names on VMS.
16 **	   Sep 93 (MD)  Access to VMS files allows sharing.
17 **	15 Nov 93 (MD)	Moved HTVMSname to HTVMSUTILS.C
18 **	22 Feb 94 (MD)  Excluded two routines if we are not READING directories
19 **	18 May 94 (HF)	Directory stuff removed and stream handling updated,
20 **			error messages introduced etc.
21 **		  HFN	Separated transport
22 **
23 ** Bugs:
24 **	FTP: Cannot access VMS files from a unix machine.
25 **      How can we know that the
26 **	target machine runs VMS?
27 */
28 
29 /* Library Includes */
30 #include "wwwsys.h"
31 #include "WWWUtil.h"
32 #include "WWWCore.h"
33 #include "WWWDir.h"
34 #include "WWWTrans.h"
35 #include "HTReqMan.h"
36 #include "HTBind.h"
37 #include "HTMulti.h"
38 #include "HTFile.h"		/* Implemented here */
39 
40 /* Final states have negative value */
41 typedef enum _FileState {
42     FS_RETRY		= -4,
43     FS_ERROR		= -3,
44     FS_NO_DATA		= -2,
45     FS_GOT_DATA		= -1,
46     FS_BEGIN		= 0,
47     FS_PENDING,
48     FS_DO_CN,
49     FS_NEED_OPEN_FILE,
50     FS_NEED_BODY,
51     FS_PARSE_DIR,
52     FS_TRY_FTP
53 } FileState;
54 
55 /* This is the context structure for the this module */
56 typedef struct _file_info {
57     FileState		state;		  /* Current state of the connection */
58     char *		local;		/* Local representation of file name */
59     struct stat		stat_info;	      /* Contains actual file chosen */
60     HTNet *		net;
61     HTTimer *		timer;
62 } file_info;
63 
64 struct _HTStream {
65     const HTStreamClass *	isa;
66 };
67 
68 struct _HTInputStream {
69     const HTInputStreamClass *	isa;
70 };
71 
72 PRIVATE HTDirReadme	dir_readme = HT_DIR_README_TOP;
73 PRIVATE HTDirAccess	dir_access = HT_DIR_OK;
74 PRIVATE HTDirShow	dir_show = HT_DS_SIZE+HT_DS_DATE+HT_DS_DES+HT_DS_ICON;
75 PRIVATE HTDirKey	dir_key = HT_DK_CINS;
76 PRIVATE BOOL		file_suffix_binding = YES;
77 
78 /* ------------------------------------------------------------------------- */
79 
80 /*	Directory Access
81 **	----------------
82 */
HTFile_setDirAccess(HTDirAccess mode)83 PUBLIC BOOL HTFile_setDirAccess (HTDirAccess mode)
84 {
85     dir_access = mode;
86     return YES;
87 }
88 
HTFile_dirAccess(void)89 PUBLIC HTDirAccess HTFile_dirAccess (void)
90 {
91     return dir_access;
92 }
93 
94 /*	Directory Readme
95 **	----------------
96 */
HTFile_setDirReadme(HTDirReadme mode)97 PUBLIC BOOL HTFile_setDirReadme (HTDirReadme mode)
98 {
99     dir_readme = mode;
100     return YES;
101 }
102 
HTFile_dirReadme(void)103 PUBLIC HTDirReadme HTFile_dirReadme (void)
104 {
105     return dir_readme;
106 }
107 
108 /*
109 **  Should we find the bindings between file suffixes and media types
110 **  here or not?
111 */
HTFile_doFileSuffixBinding(BOOL mode)112 PUBLIC BOOL HTFile_doFileSuffixBinding (BOOL mode)
113 {
114     file_suffix_binding = mode;
115     return YES;
116 }
117 
HTFile_fileSuffixBinding(void)118 PUBLIC BOOL HTFile_fileSuffixBinding (void)
119 {
120     return file_suffix_binding;
121 }
122 
123 /*	HTFile_readDir
124 **	--------------
125 **	Reads the directory "path"
126 **	Returns:
127 **		HT_ERROR	Error
128 **		HT_FORBIDDEN	Directory reading not allowed
129 **		HT_LOADED	Successfully read the directory
130 */
HTFile_readDir(HTRequest * request,file_info * file)131 PRIVATE int HTFile_readDir (HTRequest * request, file_info *file)
132 {
133 #ifdef HAVE_READDIR
134     DIR * dp;
135     struct stat file_info;
136     HTParentAnchor * anchor = HTRequest_anchor(request);
137     char *url = HTAnchor_physical(anchor);
138     char fullname[HT_MAX_PATH+1];
139     char *name;
140     HTTRACE(PROT_TRACE, "Reading..... directory\n");
141     if (dir_access == HT_DIR_FORBID) {
142 	HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
143 		   NULL, 0, "HTFile_readDir");
144 	return HT_FORBIDDEN;
145     }
146 
147     /* Initialize path name for stat() */
148     if (*(name = (url+strlen(url)-1)) != '/') {
149 	char *newurl = NULL;
150 	StrAllocCopy(newurl, url);
151 	StrAllocCat(newurl, "/");
152 	HT_FREE(file->local);
153 	file->local = HTWWWToLocal(newurl, "", HTRequest_userProfile(request));
154 	HT_FREE(newurl);
155     }
156     strcpy(fullname, file->local);
157     name = fullname+strlen(fullname);		 /* Point to end of fullname */
158 
159     /* Check if access is enabled */
160     if (dir_access == HT_DIR_SELECTIVE) {
161 	strcpy(name, DEFAULT_DIR_FILE);
162 	if (HT_STAT(fullname, &file_info)) {
163 	    HTTRACE(PROT_TRACE, "Read dir.... `%s\' not found\n" _ DEFAULT_DIR_FILE);
164 	    HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
165 		       NULL, 0, "HTFile_readDir");
166 	    return HT_FORBIDDEN;
167 	}
168     }
169 
170     if ((dp = opendir(file->local))) {
171 	struct dirent * dirbuf;
172 	HTDir *dir = HTDir_new(request, dir_show, dir_key);
173 	char datestr[20];
174 	char sizestr[10];
175 	HTFileMode mode;
176 #ifdef HT_REENTRANT
177 	struct dirent result;				    /* For readdir_r */
178 #endif
179 
180 #ifdef HAVE_READDIR_R_2
181         while ((dirbuf = (struct dirent *) readdir_r(dp, &result)))
182 #elif defined(HAVE_READDIR_R_3)
183         while (readdir_r(dp, &result, &dirbuf) == 0)
184 #else
185 	while ((dirbuf = readdir(dp)))
186 #endif /* HAVE_READDIR_R_2 */
187 	{
188 	    /* Current and parent directories are never shown in list */
189 #ifdef HAVE_DIRENT_INO
190 	    if (!dirbuf->d_ino ||
191 		!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
192 #else
193 	    if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
194 #endif
195 		continue;
196 
197 	    /* Make a lstat on the file */
198 	    strcpy(name, dirbuf->d_name);
199 	    if (HT_LSTAT(fullname, &file_info)) {
200 		HTTRACE(PROT_TRACE, "Read dir.... lstat failed: %s\n" _ fullname);
201 		continue;
202 	    }
203 
204 	    /* Convert stat info to fit our setup */
205 	    if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) {
206 #ifdef VMS
207 		char *dot = strstr(name, ".DIR");      /* strip .DIR part... */
208 		if (dot) *dot = '\0';
209 #endif /* VMS */
210 		mode = HT_IS_DIR;
211 		if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-");
212 	    } else {
213 		mode = HT_IS_FILE;
214 		if (dir_show & HT_DS_SIZE)
215 		    HTNumToStr(file_info.st_size, sizestr, 10);
216 	    }
217 	    if (dir_show & HT_DS_DATE)
218 		HTDateDirStr(&file_info.st_mtime, datestr, 20);
219 
220 	    /* Add to the list */
221 	    if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES)
222 		break;
223 	}
224 	closedir(dp);
225 	HTDir_free(dir);
226 	return HT_LOADED;
227     } else {
228 	HTRequest_addSystemError(request,  ERR_FATAL, errno, NO, "opendir");
229 	return HT_ERROR;
230     }
231 #else
232     return HT_ERROR;	/* needed for WWW_MSWINDOWS */
233 #endif /* HAVE_READDIR */
234 }
235 
236 /*	Determine write access to a file
237 **	--------------------------------
238 **	If stat_info is NULL then the function calls stat() on it's own,
239 **	otherwise it uses the information found in stat_info
240 ** On exit,
241 **	return value	YES if file can be accessed and can be written to.
242 **
243 ** Bugs:
244 **	1.	No code for non-unix systems.
245 **	2.	Isn't there a quicker way?
246 */
HTEditable(const char * filename,struct stat * stat_info)247 PRIVATE BOOL HTEditable (const char * filename, struct stat * stat_info)
248 {
249 #ifdef GETGROUPS_T
250     int i;
251     uid_t myUid;
252     int	ngroups;			/* The number of groups  */
253     struct stat	fileStatus;
254     struct stat *fileptr = stat_info ? stat_info : &fileStatus;
255     GETGROUPS_T groups[NGROUPS];
256     if (!stat_info) {
257 	if (HT_STAT(filename, &fileStatus))
258 	    return NO;				  /* Can't even access file! */
259     }
260     ngroups = getgroups(NGROUPS, groups);	/* Groups to which I belong  */
261     myUid = geteuid();				/* Get my user identifier */
262 
263 #ifdef HTDEBUG
264     if (PROT_TRACE) {
265         int i;
266 	HTTRACE(PROT_TRACE,
267 		"File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (" _
268 		(unsigned int) fileptr->st_mode _
269 		(int) fileptr->st_uid _ (int) fileptr->st_gid _
270 		(int) myUid _ ngroups);
271 	for (i=0; i<ngroups; i++) HTTRACE(PROT_TRACE, " %d" _ (int) groups[i]);
272 	HTTRACE(PROT_TRACE, ")\n");
273     }
274 #endif /* HTDEBUG */
275 
276     if (fileptr->st_mode & 0002)		/* I can write anyway? */
277     	return YES;
278 
279     if ((fileptr->st_mode & 0200)		/* I can write my own file? */
280      && (fileptr->st_uid == myUid))
281     	return YES;
282 
283     if (fileptr->st_mode & 0020)		/* Group I am in can write? */
284     {
285    	for (i=0; i<ngroups; i++) {
286             if (groups[i] == fileptr->st_gid)
287 	        return YES;
288 	}
289     }
290     HTTRACE(PROT_TRACE, "\tFile is not editable.\n");
291     return NO;					/* If no excuse, can't do */
292 #else
293     /*
294     ** We don't know and can't find out. Can we be sure that when opening
295     ** a file in mode "a" that the file is not modified?
296     */
297     return NO;
298 #endif /* GETGROUPS_T */
299 }
300 
301 /*	FileCleanup
302 **	-----------
303 **      This function closes the connection and frees memory.
304 **      Returns YES on OK, else NO
305 */
FileCleanup(HTRequest * req,int status)306 PRIVATE int FileCleanup (HTRequest *req, int status)
307 {
308     HTNet * net = HTRequest_net(req);
309     file_info * file = (file_info *) HTNet_context(net);
310     HTStream * input = HTRequest_inputStream(req);
311 
312     /* Free stream with data TO Local file system */
313     if (input) {
314         if (status == HT_INTERRUPTED)
315             (*input->isa->abort)(input, NULL);
316         else
317             (*input->isa->_free)(input);
318         HTRequest_setInputStream(req, NULL);
319     }
320 
321     /*
322     **  Remove if we have registered a timer function as a callback
323     */
324     if (file->timer) {
325 	HTTimer_delete(file->timer);
326 	file->timer = NULL;
327     }
328 
329     if (file) {
330 	HT_FREE(file->local);
331 	HT_FREE(file);
332     }
333     HTNet_delete(net, status);
334     return YES;
335 }
336 
337 
338 /*	Load a document
339 **	---------------
340 **
341 ** On entry,
342 **	request		This is the request structure
343 ** On exit,
344 **	returns		HT_ERROR	Error has occured in call back
345 **			HT_OK		Call back was OK
346 */
347 PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type);
348 
HTLoadFile(SOCKET soc,HTRequest * request)349 PUBLIC int HTLoadFile (SOCKET soc, HTRequest * request)
350 {
351     file_info *file;			      /* Specific access information */
352     HTNet * net = HTRequest_net(request);
353     HTParentAnchor * anchor = HTRequest_anchor(request);
354 
355     HTTRACE(PROT_TRACE, "HTLoadFile.. Looking for `%s\'\n" _
356 			    HTAnchor_physical(anchor));
357     if ((file = (file_info *) HT_CALLOC(1, sizeof(file_info))) == NULL)
358 	HT_OUTOFMEM("HTLoadFILE");
359     file->state = FS_BEGIN;
360     file->net = net;
361     HTNet_setContext(net, file);
362     HTNet_setEventCallback(net, FileEvent);
363     HTNet_setEventParam(net, file);  /* callbacks get http* */
364 
365     return FileEvent(soc, file, HTEvent_BEGIN);	    /* get it started - ops is ignored */
366 }
367 
ReturnEvent(HTTimer * timer,void * param,HTEventType type)368 PRIVATE int ReturnEvent (HTTimer * timer, void * param, HTEventType type)
369 {
370     file_info * file = (file_info *) param;
371     if (timer != file->timer)
372 	HTDEBUGBREAK("File timer %p not in sync\n" _ timer);
373     HTTRACE(PROT_TRACE, "HTLoadFile.. Continuing %p with timer %p\n" _ file _ timer);
374 
375     /*
376     **  Delete the timer
377     */
378     HTTimer_delete(file->timer);
379     file->timer = NULL;
380 
381     /*
382     **  Now call the event again
383     */
384     return FileEvent(INVSOC, file, HTEvent_READ);
385 }
386 
387 
FileEvent(SOCKET soc,void * pVoid,HTEventType type)388 PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type)
389 {
390     file_info *file = pVoid;			      /* Specific access information */
391     int status = HT_ERROR;
392     HTNet * net = file->net;
393     HTRequest * request = HTNet_request(net);
394     HTParentAnchor * anchor = HTRequest_anchor(request);
395 
396     /*
397     ** Initiate a new file structure and bind to request structure
398     ** This is actually state FILE_BEGIN, but it can't be in the state
399     ** machine as we need the structure first.
400     */
401     if (type == HTEvent_CLOSE) {				      /* Interrupted */
402 	HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
403 			   NULL, 0, "HTLoadFile");
404 	FileCleanup(request, HT_INTERRUPTED);
405 	return HT_OK;
406     }
407 
408 
409     /* Now jump into the machine. We know the state from the previous run */
410     while (1) {
411 	switch (file->state) {
412 	case FS_BEGIN:
413 
414 	    /* We only support safe (GET, HEAD, etc) methods for the moment */
415 	    if (!HTMethod_isSafe(HTRequest_method(request))) {
416 		HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
417 				   NULL, 0, "HTLoadFile");
418 		file->state = FS_ERROR;
419 		break;
420 	    }
421 
422 	    /* Check whether we have access to local disk at all */
423 	    if (HTLib_secure()) {
424 		HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n");
425 		file->state = FS_TRY_FTP;
426 		break;
427 	    }
428 	    file->local = HTWWWToLocal(HTAnchor_physical(anchor), "",
429 				       HTRequest_userProfile(request));
430 	    if (!file->local) {
431 		file->state = FS_TRY_FTP;
432 		break;
433 	    }
434 
435 	    /* Create a new host object and link it to the net object */
436 	    {
437 		HTHost * host = NULL;
438 		if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
439 		HTNet_setHost(net, host);
440 		if (HTHost_addNet(host, net) == HT_PENDING) {
441 		    HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
442 		    /* move to the hack state */
443 		    file->state = FS_PENDING;
444 		    return HT_OK;
445 		}
446 	    }
447 	    file->state = FS_DO_CN;
448 	    break;
449 
450 	case FS_PENDING:
451 	    /*
452 	    ** 2000/08/10 JK : This is a funny state. Because of the
453 	    ** internal libwww stacks, when doing multiple local
454 	    ** requests (e.g., while using the Robot), we need to ask
455 	    ** again for the host object. If we had jumped directly to
456 	    ** the FS_DO_CN state, libwww would have blocked because
457 	    ** of socket starvation.
458 	    ** This state is similar to FS_BEGINNING, but just requests
459 	    ** the host object.
460 	    ** YES. THIS IS AN UGLY HACK!!
461 	    */
462 	    {
463 		HTHost * host = NULL;
464 		if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
465 		HTNet_setHost(net, host);
466 		if (HTHost_addNet(host, net) == HT_PENDING) {
467 		    HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
468 		    file->state = FS_PENDING;
469 		    return HT_OK;
470 		}
471 	    }
472 	    file->state = FS_DO_CN;
473 	    break;
474 
475 	case FS_DO_CN:
476 	    /*
477 	    ** If we have to do content negotiation then find the object that
478 	    ** fits best into either what the client has indicated in the
479 	    ** accept headers or what the client has registered on its own.
480 	    ** The object chosen can in fact be a directory! However, content
481 	    ** negotiation only makes sense if we can read the directory!
482 	    ** We stat the file in order to find the size and to see it if
483 	    ** exists.
484 	    */
485 	    if (HTRequest_negotiation(request) &&
486 		HTMethod_isSafe(HTRequest_method(request))) {
487  		char * conneg = HTMulti(request, file->local,&file->stat_info);
488 		if (conneg) {
489 		    HT_FREE(file->local);
490 		    file->local = conneg;
491 		    HTAnchor_setPhysical(anchor, conneg);
492 		    HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg);
493 		} else {
494 		    HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n");
495 		    HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
496 				       NULL, 0, "HTLoadFile");
497 		    file->state = FS_ERROR;
498 		    break;
499 		}
500 	    } else {
501 		if (HT_STAT(file->local, &file->stat_info) == -1) {
502 		    HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local);
503 		    HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
504 				       NULL, 0, "HTLoadFile");
505 		    file->state = FS_ERROR;
506 		    break;
507 		}
508 	    }
509 
510 	    /*
511 	    ** Check to see if the 'localname' is in fact a directory.
512 	    ** Note that we can't do a HEAD on a directory
513 	    */
514 	    if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) {
515 		if (HTRequest_method(request) == METHOD_GET)
516 		    file->state = FS_PARSE_DIR;
517 		else {
518 		    HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
519 				       NULL, 0, "HTLoadFile");
520 		    file->state = FS_NO_DATA;
521 		}
522 		break;
523 	    }
524 
525 	    /*
526 	    ** If empty file then only serve it if it is editable. We also get
527 	    ** the bindings for the file suffixes in lack of better bindings
528 	    */
529 	    {
530 		BOOL editable = HTEditable(file->local, &file->stat_info);
531 		if (file_suffix_binding) HTBind_getAnchorBindings(anchor);
532 		if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT);
533 
534 		/* Set the file size */
535 		if (file->stat_info.st_size)
536 		    HTAnchor_setLength(anchor, file->stat_info.st_size);
537 
538 		/* Set the file last modified time stamp */
539 		if (file->stat_info.st_mtime > 0)
540 		    HTAnchor_setLastModified(anchor, file->stat_info.st_mtime);
541 
542 		/* Check to see if we can edit it */
543 		if (!editable && !file->stat_info.st_size) {
544 		    HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
545 				       NULL, 0, "HTLoadFile");
546 		    file->state = FS_NO_DATA;
547 		} else {
548 		    file->state = (HTRequest_method(request)==METHOD_GET) ?
549 			FS_NEED_OPEN_FILE : FS_GOT_DATA;
550 		}
551 	    }
552 	    break;
553 
554 	  case FS_NEED_OPEN_FILE:
555 	    status = HTFileOpen(net, file->local, HT_FB_RDONLY);
556 	    if (status == HT_OK) {
557 		/*
558 		** Create the stream pipe FROM the channel to the application.
559 		** The target for the input stream pipe is set up using the
560 		** stream stack.
561 		*/
562 		{
563 		    HTStream * rstream = HTStreamStack(HTAnchor_format(anchor),
564 						       HTRequest_outputFormat(request),
565 						       HTRequest_outputStream(request),
566 						       request, YES);
567 		    HTNet_setReadStream(net, rstream);
568 		    HTRequest_setOutputConnected(request, YES);
569 		}
570 
571 		/*
572 		** Create the stream pipe TO the channel from the application
573 		** and hook it up to the request object
574 		*/
575 		{
576 		    HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
577 		    HTRequest_setInputStream(request, (HTStream *) output);
578 		}
579 
580 		/*
581 		** Set up concurrent read/write if this request isn't the
582 		** source for a PUT or POST. As source we don't start reading
583 		** before all destinations are ready. If destination then
584 		** register the input stream and get ready for read
585 		*/
586 		if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request))
587 		    return HT_OK;
588 		HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0,
589 				   "HTLoadFile");
590 		file->state = FS_NEED_BODY;
591 
592 		/* If we are _not_ using preemptive mode and we are Unix fd's
593 		** then return here to get the same effect as when we are
594 		** connecting to a socket. That way, HTFile acts just like any
595 		** other protocol module even though we are in fact doing
596 		** blocking connect
597 		*/
598 		if (HTEvent_isCallbacksRegistered()) {
599 		    if (!HTRequest_preemptive(request)) {
600 			if (!HTNet_preemptive(net)) {
601 			    HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
602 			    HTHost_register(HTNet_host(net), net, HTEvent_READ);
603 			} else if (!file->timer) {
604 			    HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
605 			    file->timer =
606 				HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO);
607 			}
608 			return HT_OK;
609 		    }
610 		}
611 	    } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
612 		return HT_OK;
613 	    else {
614 		HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL,
615 				   NULL, 0, "HTLoadFile");
616 		file->state = FS_ERROR;		       /* Error or interrupt */
617 	    }
618 	    break;
619 
620 	  case FS_NEED_BODY:
621 	    status = HTHost_read(HTNet_host(net), net);
622 	    if (status == HT_WOULD_BLOCK)
623 		return HT_OK;
624 	    else if (status == HT_LOADED || status == HT_CLOSED) {
625 		file->state = FS_GOT_DATA;
626 	    } else {
627 		HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN,
628 				   NULL, 0, "HTLoadFile");
629 		file->state = FS_ERROR;
630 	    }
631 	    break;
632 
633 	  case FS_PARSE_DIR:
634 	    status = HTFile_readDir(request, file);
635 	    if (status == HT_LOADED)
636 		file->state = FS_GOT_DATA;
637 	    else
638 		file->state = FS_ERROR;
639 	    break;
640 
641 	  case FS_TRY_FTP:
642 	    {
643 		char *url = HTAnchor_physical(anchor);
644 		HTAnchor *anchor;
645 		char *newname = NULL;
646 		StrAllocCopy(newname, "ftp:");
647 		if (!strncmp(url, "file:", 5))
648 		    StrAllocCat(newname, url+5);
649 		else
650 		    StrAllocCat(newname, url);
651 		anchor = HTAnchor_findAddress(newname);
652 		HTRequest_setAnchor(request, anchor);
653 		HT_FREE(newname);
654 		FileCleanup(request, HT_IGNORE);
655 		return HTLoad(request, YES);
656 	    }
657 	    break;
658 
659 	  case FS_GOT_DATA:
660 	    FileCleanup(request, HT_LOADED);
661 	    return HT_OK;
662 	    break;
663 
664 	  case FS_NO_DATA:
665 	    FileCleanup(request, HT_NO_DATA);
666 	    return HT_OK;
667 	    break;
668 
669 	  case FS_RETRY:
670 	    FileCleanup(request, HT_RETRY);
671 	    return HT_OK;
672 	    break;
673 
674 	  case FS_ERROR:
675 	    FileCleanup(request, HT_ERROR);
676 	    return HT_OK;
677 	    break;
678 	}
679     } /* End of while(1) */
680 }
681 
682 
683 /* Calculate the required buffer size (in bytes) for directory
684 ** entries read from the given directory handle.  Return -1 if this
685 ** this cannot be done.
686 */
HTFile_dirent_buf_size(DIR * dirp)687 PUBLIC size_t HTFile_dirent_buf_size(DIR * dirp)
688 {
689     long name_max;
690 #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
691         name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
692         if (name_max == -1)
693 #if defined(NAME_MAX)
694                 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
695 #else
696                 return (size_t)(-1);
697 #endif
698 #else
699 #if defined(NAME_MAX)
700             name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
701 #else
702 #error "buffer size for readdir_r cannot be determined"
703 #endif
704 #endif
705         return (size_t)offsetof(struct dirent, d_name) + name_max + 1;
706 }
707