HTFile.c
上传用户:zlh9724
上传日期:2007-01-04
资源大小:1991k
文件大小:20k
源码类别:

浏览器

开发平台:

Unix_Linux

  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. **
  7. ** This is unix-specific code in general, with some VMS bits.
  8. ** These are routines for file access used by browsers.
  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.
  15. **    Sep 93 (MD)  Access to VMS files allows sharing.
  16. ** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C
  17. ** 22 Feb 94 (MD)  Excluded two routines if we are not READING directories
  18. ** 18 May 94 (HF) Directory stuff removed and stream handling updated,
  19. ** error messages introduced etc.
  20. **
  21. ** Bugs:
  22. ** FTP: Cannot access VMS files from a unix machine.
  23. **      How can we know that the
  24. ** target machine runs VMS?
  25. */
  26. /* Library Includes */
  27. #include "tcp.h"
  28. #include "HTUtils.h"
  29. #include "HTString.h"
  30. #include "HTParse.h"
  31. #include "HTTCP.h"
  32. #include "HTAnchor.h"
  33. #include "HTAtom.h"
  34. #include "HTWriter.h"
  35. #include "HTFWrite.h"
  36. #include "HTFormat.h"
  37. #include "HTWWWStr.h"
  38. #include "HTAccess.h"
  39. #include "HTMulti.h"
  40. #include "HTIcons.h"
  41. #include "HTDir.h"
  42. #include "HTBind.h"
  43. #include "HTSocket.h"
  44. #include "HTNetMan.h"
  45. #include "HTError.h"
  46. #include "HTReqMan.h"
  47. #include "HTFile.h" /* Implemented here */
  48. /* Final states have negative value */
  49. typedef enum _FileState {
  50.     FS_RETRY = -4,
  51.     FS_ERROR = -3,
  52.     FS_NO_DATA = -2,
  53.     FS_GOT_DATA = -1,
  54.     FS_BEGIN = 0,
  55.     FS_DO_CN,
  56.     FS_NEED_OPEN_FILE,
  57.     FS_NEED_TARGET,
  58.     FS_NEED_BODY,
  59.     FS_PARSE_DIR,
  60.     FS_TRY_FTP
  61. } FileState;
  62. /* This is the context structure for the this module */
  63. typedef struct _file_info {
  64.     FileState state;   /* Current state of the connection */
  65.     char * local; /* Local representation of file name */
  66. #ifdef NO_UNIX_IO    
  67.     FILE * fp;        /* If we can't use sockets on local files */
  68. #endif
  69. } file_info;
  70. /* Local definition */
  71. struct _HTStream {
  72.     CONST HTStreamClass * isa;
  73.     /* ... */
  74. };
  75. PRIVATE BOOL HTTakeBackup = YES;
  76. PRIVATE HTDirReadme dir_readme = HT_DIR_README_TOP;
  77. PRIVATE HTDirAccess dir_access = HT_DIR_OK;
  78. PRIVATE HTDirShow dir_show = HT_DS_SIZE+HT_DS_DATE+HT_DS_DES+HT_DS_ICON;
  79. PRIVATE HTDirKey dir_key = HT_DK_CINS;
  80. /* ------------------------------------------------------------------------- */
  81. /* Directory Access
  82. ** ----------------
  83. */
  84. PUBLIC BOOL HTFile_setDirAccess (HTDirAccess mode)
  85. {
  86.     dir_access = mode;
  87.     return YES;
  88. }
  89. PUBLIC HTDirAccess HTFile_dirAccess (void)
  90. {
  91.     return dir_access;
  92. }
  93. /* Directory Readme
  94. ** ----------------
  95. */
  96. PUBLIC BOOL HTFile_setDirReadme (HTDirReadme mode)
  97. {
  98.     dir_readme = mode;
  99.     return YES;
  100. }
  101. PUBLIC HTDirReadme HTFile_dirReadme (void)
  102. {
  103.     return dir_readme;
  104. }
  105. /* HTFile_readDir
  106. ** --------------
  107. ** Reads the directory "path"
  108. ** Returns:
  109. ** HT_ERROR Error
  110. ** HT_FORBIDDEN Directory reading not allowed
  111. ** HT_LOADED Successfully read the directory
  112. */
  113. PRIVATE int HTFile_readDir (HTRequest * request, file_info *file)
  114. {
  115. #ifndef GOT_READ_DIR
  116.     return HT_ERROR;
  117. #else
  118.     DIR *dp;
  119.     struct stat file_info;
  120.     char *url = HTAnchor_physical(request->anchor);
  121.     char fullname[HT_MAX_PATH+1];
  122.     char *name;
  123.     if (PROT_TRACE) TTYPrint(TDEST, "Reading..... directoryn");
  124.     if (dir_access == HT_DIR_FORBID) {
  125. HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
  126.    NULL, 0, "HTFile_readDir");
  127. return HT_FORBIDDEN;
  128.     }
  129.     
  130.     /* Initialize path name for stat() */
  131.     if (*(name = (url+strlen(url)-1)) != '/') {
  132. char *newurl = NULL;
  133. StrAllocCopy(newurl, url);
  134. StrAllocCat(newurl, "/");
  135. HT_FREE(file->local);
  136. file->local = HTWWWToLocal(newurl, "");
  137. HT_FREE(newurl);
  138.     }
  139.     strcpy(fullname, file->local);
  140.     name = fullname+strlen(fullname);  /* Point to end of fullname */
  141.     /* Check if access is enabled */
  142.     if (dir_access == HT_DIR_SELECTIVE) {
  143. strcpy(name, DEFAULT_DIR_FILE);
  144. if (HT_STAT(fullname, &file_info)) {
  145.     if (PROT_TRACE)
  146. TTYPrint(TDEST,
  147. "Read dir.... `%s' not foundn", DEFAULT_DIR_FILE);
  148.     HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
  149.        NULL, 0, "HTFile_readDir");
  150.     return HT_FORBIDDEN;
  151. }
  152.     }
  153.     if ((dp = opendir(file->local))) {
  154. STRUCT_DIRENT *dirbuf;
  155. HTDir *dir = HTDir_new(request, dir_show, dir_key);
  156. char datestr[20];
  157. char sizestr[10];
  158. HTFileMode mode;
  159. #ifdef HT_REENTRANT
  160. STRUCT_DIRENT result;     /* For readdir_r */
  161. while ((dirbuf = (STRUCT_DIRENT *) readdir_r(dp, &result)))
  162. #else
  163. while ((dirbuf = readdir(dp)))
  164. #endif /* HT_REENTRANT */
  165. {
  166.     /* Current and parent directories are never shown in list */
  167.     if (
  168. #ifndef __QNX__ /* doesn't return unused directory slots */
  169.                 !dirbuf->d_ino ||
  170. #endif
  171. !strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
  172. continue;
  173.     /* Make a lstat on the file */
  174.     strcpy(name, dirbuf->d_name);
  175.     if (HT_LSTAT(fullname, &file_info)) {
  176. if (PROT_TRACE)
  177.     TTYPrint(TDEST, "Read dir.... lstat failed: %sn",fullname);
  178. continue;
  179.     }
  180.     /* Convert stat info to fit our setup */
  181.     if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) {
  182. #ifdef VMS
  183. char *dot = strstr(name, ".DIR");      /* strip .DIR part... */
  184. if (dot) *dot = '';
  185. #endif /* VMS */
  186. mode = HT_IS_DIR;
  187. if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-");
  188.     } else {
  189. mode = HT_IS_FILE;
  190. if (dir_show & HT_DS_SIZE)
  191.     HTNumToStr(file_info.st_size, sizestr, 10);
  192.     }
  193.     if (dir_show & HT_DS_DATE)
  194. HTDateDirStr(&file_info.st_mtime, datestr, 20);
  195.     /* Add to the list */
  196.     if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES)
  197. break;
  198. }
  199. closedir(dp);
  200. HTDir_free(dir);
  201.     } else
  202. HTRequest_addSystemError(request,  ERR_FATAL, errno, NO, "opendir");
  203.     return HT_LOADED;
  204. #endif /* GOT_READ_DIR */
  205. }
  206. /* Determine write access to a file
  207. ** --------------------------------
  208. ** If stat_info is NULL then the function calls stat() on it's own,
  209. ** otherwise it uses the information found in stat_info
  210. ** On exit,
  211. ** return value YES if file can be accessed and can be written to.
  212. **
  213. ** Bugs:
  214. ** 1. No code for non-unix systems.
  215. ** 2. Isn't there a quicker way?
  216. */
  217. PRIVATE BOOL HTEditable (CONST char * filename, struct stat * stat_info)
  218. {
  219. #ifndef NO_UNIX_IO
  220. #ifdef NO_GROUPS
  221.     return NO;    /* Safe answer till we find the correct algorithm */
  222. #else
  223. #ifdef NeXT
  224.     int groups[NGROUPS];
  225. #else
  226.     gid_t groups[NGROUPS];
  227. #endif
  228.     int i;
  229.     uid_t myUid;
  230.     int ngroups; /* The number of groups  */
  231.     struct stat fileStatus;
  232.     struct stat *fileptr = stat_info ? stat_info : &fileStatus;
  233.         
  234.     if (!stat_info) {
  235. if (HT_STAT(filename, &fileStatus))
  236.     return NO;   /* Can't even access file! */
  237.     }
  238.     ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong  */
  239.     myUid = geteuid(); /* Get my user identifier */
  240.     if (PROT_TRACE) {
  241.         int i;
  242. TTYPrint(TDEST, 
  243.     "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  244.          (unsigned int) fileptr->st_mode, (int) fileptr->st_uid,
  245.     (int) fileptr->st_gid, (int) myUid, ngroups);
  246. for (i=0; i<ngroups; i++) TTYPrint(TDEST, " %d", (int) groups[i]);
  247. TTYPrint(TDEST, ")n");
  248.     }
  249.     
  250.     if (fileptr->st_mode & 0002) /* I can write anyway? */
  251.      return YES;
  252.     if ((fileptr->st_mode & 0200) /* I can write my own file? */
  253.      && (fileptr->st_uid == myUid))
  254.      return YES;
  255.     if (fileptr->st_mode & 0020) /* Group I am in can write? */
  256.     {
  257.     for (i=0; i<ngroups; i++) {
  258.             if (groups[i] == fileptr->st_gid)
  259.         return YES;
  260. }
  261.     }
  262.     if (PROT_TRACE) TTYPrint(TDEST, "tFile is not editable.n");
  263.     return NO; /* If no excuse, can't do */
  264. #endif
  265. #else
  266.     /*
  267.     ** We don't know and can't find out. Can we be sure that when opening
  268.     ** a file in mode "a" that the file is not modified?
  269.     */
  270.     return NO;
  271. #endif /* !NO_UNIX_IO */
  272. }
  273. /* Make a save stream
  274. ** ------------------
  275. **
  276. ** The stream must be used for writing back the file.
  277. ** @@@ no backup done
  278. */
  279. PUBLIC HTStream * HTFileSaveStream (HTRequest * request)
  280. {
  281.     CONST char * addr = HTAnchor_address((HTAnchor*)request->anchor);
  282.     char * filename = HTWWWToLocal(addr, "");
  283.     FILE* fp;
  284. /* @ Introduce CVS handling here one day
  285. */
  286. /* Take a backup before we do anything silly   931205
  287. */        
  288.     if (HTTakeBackup) {
  289. char * p;
  290.      char * backup_filename;
  291.      if ((backup_filename = (char  *) HT_MALLOC(strlen(filename)+2)) == NULL)
  292.          HT_OUTOFMEM("taking backup");
  293. strcpy(backup_filename, filename);
  294. for(p=backup_filename+strlen(backup_filename);; p--) {
  295.     if ((*p=='/') || (p<backup_filename)) {
  296.         p[1]=','; /* Insert comma after slash */
  297. break;
  298.     }
  299.     p[1] = p[0]; /* Move up everything to the right of it */
  300. }
  301. #ifdef VMS
  302. if ((fp=fopen(filename, "r", "shr=put", "shr=upd"))) { /* File exists */
  303. #else /* not VMS */
  304. if ((fp=fopen(filename, "r"))) { /* File exists */
  305. #endif /* not VMS */
  306.     fclose(fp);
  307.     if (PROT_TRACE) TTYPrint(TDEST, "File `%s' existsn", filename);
  308.     if (REMOVE(backup_filename)) {
  309. if (PROT_TRACE) TTYPrint(TDEST, "Backup file `%s' removedn",
  310.    backup_filename);
  311.     }
  312.     if (rename(filename, backup_filename)) { /* != 0 => Failure */
  313. if (PROT_TRACE) TTYPrint(TDEST, "Rename `%s' to `%s' FAILED!n",
  314.    filename, backup_filename);
  315.     } else { /* Success */
  316. if (PROT_TRACE)
  317.     TTYPrint(TDEST, "Renamed `%s' to `%s'n", filename,
  318.     backup_filename);
  319.     }
  320. }
  321.      HT_FREE(backup_filename);
  322.     } /* if take backup */    
  323.     
  324.     if ((fp = fopen(filename, "wb")) == NULL) {
  325. HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "fopen");
  326. return NULL;
  327.     } else
  328.      return HTFWriter_new(request, fp, NO);
  329. }
  330. /* FileCleanup
  331. ** -----------
  332. **      This function closes the connection and frees memory.
  333. **      Returns YES on OK, else NO
  334. */
  335. PRIVATE int FileCleanup (HTRequest *req, int status)
  336. {
  337.     HTNet *net = req->net;
  338.     file_info *file = (file_info *) net->context;
  339.     /* Free stream with data TO Local file system */
  340.     if (HTRequest_isDestination(req))
  341. HTRequest_removeDestination(req);
  342.     else  if (req->input_stream) {
  343. if (status == HT_INTERRUPTED)
  344.     (*req->input_stream->isa->abort)(req->input_stream, NULL);
  345. else
  346.     (*req->input_stream->isa->_free)(req->input_stream);
  347. req->input_stream = NULL;
  348.     }
  349.     if (file) {
  350. #ifdef NO_UNIX_IO
  351. if (file->fp) {
  352.     if (PROT_TRACE)
  353. TTYPrint(TDEST,"FileCleanup. Closing file %pn", file->fp);
  354.     fclose(file->fp);
  355. }
  356. #endif
  357. HT_FREE(file->local);
  358. HT_FREE(file);
  359.     }
  360.     HTNet_delete(net, req->internal ? HT_IGNORE : status);
  361.     return YES;
  362. }
  363. /* Load a document
  364. ** ---------------
  365. **
  366. ** On entry,
  367. ** request This is the request structure
  368. ** On exit,
  369. ** returns HT_ERROR Error has occured in call back
  370. ** HT_OK Call back was OK
  371. */
  372. PUBLIC int HTLoadFile (SOCKET soc, HTRequest * request, SockOps ops)
  373. {
  374.     int status = HT_ERROR;
  375.     HTNet *net = request->net;      /* Generic protocol information */
  376.     file_info *file;       /* Specific access information */
  377.     HTParentAnchor *anchor = HTRequest_anchor(request);
  378.     /*
  379.     ** Initiate a new file structure and bind to request structure
  380.     ** This is actually state FILE_BEGIN, but it can't be in the state
  381.     ** machine as we need the structure first.
  382.     */
  383.     if (ops == FD_NONE) {
  384. if (PROT_TRACE) TTYPrint(TDEST, "HTLoadFile.. Looking for `%s'n",
  385. HTAnchor_physical(anchor));
  386. if ((file = (file_info *) HT_CALLOC(1, sizeof(file_info))) == NULL)
  387.     HT_OUTOFMEM("HTLoadFILE");
  388. file->state = FS_BEGIN;
  389. net->context = file;
  390.     } if (ops == FD_CLOSE) {       /* Interrupted */
  391. HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
  392.    NULL, 0, "HTLoadHTTP");
  393. FileCleanup(request, HT_INTERRUPTED);
  394. return HT_OK;
  395.     } else
  396. file = (file_info *) net->context; /* Get existing copy */
  397.     /* Now jump into the machine. We know the state from the previous run */
  398.     while (1) {
  399. switch (file->state) {
  400.   case FS_BEGIN:
  401.     if (HTLib_secure()) {
  402. if (PROT_TRACE)
  403.     TTYPrint(TDEST, "LoadFile.... No access to local file systemn");
  404. file->state = FS_TRY_FTP;
  405. break;
  406.     }
  407.     file->local = HTWWWToLocal(HTAnchor_physical(anchor), "");
  408.     if (!file->local) {
  409. file->state = FS_TRY_FTP;
  410. break;
  411.     }
  412.     /* If cache element then jump directly to OPEN FILE state */
  413.     file->state = HTAnchor_cacheHit(anchor) ?
  414. FS_NEED_OPEN_FILE : FS_DO_CN;
  415.     break;
  416.   case FS_DO_CN:
  417.     /*
  418.     ** If we have to do content negotiation then find the object that
  419.     ** fits best into either what the client has indicated in the
  420.     ** accept headers or what the client has registered on its own.
  421.     ** The object chosen can in fact be a directory! However, content
  422.     ** negotiation only makes sense it we can read the directory!
  423.     ** We stat the file in order to find the size and to see it if
  424.     ** exists.
  425.     */
  426.     {
  427. struct stat stat_info;       /* Contains actual file chosen */
  428. if (request->ContentNegotiation) {
  429.     char *new_path=HTMulti(request,file->local,&stat_info);
  430.     if (new_path) {
  431. HT_FREE(file->local);
  432. file->local = new_path;
  433. HTAnchor_setPhysical(anchor, new_path);
  434.     } else {
  435. file->state = FS_ERROR;
  436. break;
  437.     }
  438. } else {
  439.     if (HT_STAT(file->local, &stat_info) == -1) {
  440. if (PROT_TRACE)
  441.     TTYPrint(TDEST, "HTLoadFile.. Can't stat %sn",
  442.     file->local);
  443. HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
  444.    NULL, 0, "HTLoadFile");
  445. file->state = FS_ERROR;
  446. break;
  447.     }
  448. }
  449. /* Check to see if the 'localname' is in fact a directory */
  450. if (((stat_info.st_mode) & S_IFMT) == S_IFDIR)
  451.     file->state = FS_PARSE_DIR;
  452. else {
  453.     /*
  454.     ** If empty file then only serve it if it is editable
  455.     */
  456.     BOOL editable = HTEditable(file->local, &stat_info);
  457.     HTBind_getBindings(anchor);
  458.     if (editable)
  459. HTAnchor_appendMethods(anchor, METHOD_PUT);
  460.     if (stat_info.st_size)
  461. HTAnchor_setLength(anchor, stat_info.st_size);
  462.     /* Done with relevant metainformation in anchor */
  463.     HTAnchor_setHeaderParsed(anchor);
  464.     if (!editable && !stat_info.st_size) {
  465. HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_CONTENT,
  466.    NULL, 0, "HTLoadFile");
  467. file->state = FS_NO_DATA;
  468.     } else
  469. file->state = FS_NEED_OPEN_FILE;
  470. }
  471.     }
  472.     break;
  473.   case FS_NEED_OPEN_FILE:
  474.     /*
  475.     ** If we have unix file descriptors then use this otherwise use
  476.     ** the ANSI C file descriptors
  477.     */
  478. #ifndef NO_UNIX_IO
  479.     if ((net->sockfd = open(file->local, O_RDONLY)) == -1) {
  480. HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "open");
  481. file->state = FS_ERROR;
  482. break;
  483.     }
  484.     if (PROT_TRACE)
  485. TTYPrint(TDEST,"HTLoadFile.. `%s' opened using socket %d n",
  486. file->local, net->sockfd);
  487.     /* If non-blocking protocol then change socket status
  488.     ** I use FCNTL so that I can ask the status before I set it.
  489.     ** See W. Richard Stevens (Advan. Prog. in UNIX env, p.364)
  490.     ** Be CAREFULL with the old `O_NDELAY' - it wont work as read()
  491.     ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD
  492.     ** and does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
  493.     */
  494. #ifndef NO_FCNTL
  495.     if (!request->net->preemptive) {
  496. if ((status = FCNTL(net->sockfd, F_GETFL, 0)) != -1) {
  497.     status |= O_NONBLOCK;     /* POSIX */
  498.     status = FCNTL(net->sockfd, F_SETFL, status);
  499. }
  500. if (PROT_TRACE) {
  501.     if (status == -1)
  502. TTYPrint(TDEST, "HTLoadFile.. Can't make socket non-blockingn");
  503.     else
  504. TTYPrint(TDEST,"HTLoadFile.. Using NON_BLOCKING I/On");
  505. }
  506.     }
  507. #endif /* NO_FCNTL */
  508. #else
  509. #ifdef VMS
  510.     if (!(file->fp = fopen(file->local,"r","shr=put","shr=upd"))) {
  511. #else
  512. #ifdef WWW_WIN_DLL
  513.     if ((file->fp = HTSocket_DLLHackFopen(file->local,"r")) == NULL) {
  514. #else /* !WWW_WIN_DLL */
  515.     if ((file->fp = fopen(file->local,"r")) == NULL) {
  516. #endif /* !WWW_WIN_DLL */
  517. #endif /* !VMS */
  518. HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "fopen");
  519. file->state = FS_ERROR;
  520. break;
  521.     }
  522.     if (PROT_TRACE)
  523. TTYPrint(TDEST,"HTLoadFile.. `%s' opened using FILE %pn",
  524. file->local, file->fp);
  525. #endif /* !NO_UNIX_IO */
  526.     /* Set up stream TO local file system */
  527.     request->input_stream = HTBufWriter_new(net, YES, 512);
  528.     /*
  529.     ** Set up concurrent read/write if this request isn't the
  530.     ** source for a PUT or POST. As source we don't start reading
  531.     ** before all destinations are ready. If destination then
  532.     ** register the input stream and get ready for read
  533.     */
  534.     if (HTRequest_isDestination(request)) {
  535. HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
  536.  HTLoadFile, net->priority);
  537. HTRequest_linkDestination(request);
  538.     }
  539.     file->state = FS_NEED_TARGET;
  540.     if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request))
  541. return HT_OK;
  542. #ifndef NO_UNIX_IO
  543.     if (PROT_TRACE) TTYPrint(TDEST, "HTLoadFile.. returningn");
  544.     HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
  545.      net->cbf, net->priority);
  546.     return HT_WOULD_BLOCK;
  547. #endif
  548.     break;
  549.   case FS_NEED_TARGET:
  550.     /*
  551.     ** Set up read buffer and streams.
  552.     ** If cache element, we know that it's MIME, so call MIME parser
  553.     ** If ANSI then sockfd=INVSOC
  554.     */
  555.     net->isoc = HTInputSocket_new(net->sockfd);
  556.     if (HTAnchor_cacheHit(anchor))HTAnchor_setFormat(anchor, WWW_MIME);
  557.     net->target = HTStreamStack(HTAnchor_format(anchor),
  558. request->output_format,
  559. request->output_stream,
  560. request, YES);
  561.     file->state = net->target ? FS_NEED_BODY : FS_ERROR;
  562.     break;
  563.   case FS_NEED_BODY:
  564. #ifndef NO_UNIX_IO
  565.     status = HTSocketRead(request, net);
  566. #else
  567.     status = HTFileRead(request, net, file->fp);
  568. #endif
  569.     if (status == HT_WOULD_BLOCK)
  570. return HT_OK;
  571.     else if (status == HT_LOADED || status == HT_CLOSED) {
  572. file->state = FS_GOT_DATA;
  573.     } else
  574. file->state = FS_ERROR;
  575.     break;
  576.   case FS_PARSE_DIR:
  577.     status = HTFile_readDir(request, file);
  578.     if (status == HT_LOADED)
  579. file->state = FS_GOT_DATA;
  580.     else
  581. file->state = FS_ERROR;
  582.     break;
  583.   case FS_TRY_FTP:
  584.     {
  585. char *url = HTAnchor_physical(anchor);
  586. HTAnchor *anchor;
  587. char *newname = NULL;
  588. StrAllocCopy(newname, "ftp:");
  589. if (!strncmp(url, "file:", 5))
  590.     StrAllocCat(newname, url+5);
  591. else
  592.     StrAllocCat(newname, url);
  593. anchor = HTAnchor_findAddress(newname);
  594. HTRequest_setAnchor(request, anchor);
  595. HT_FREE(newname);
  596. FileCleanup(request, HT_IGNORE);
  597. return HTLoad(request, YES);
  598.     }
  599.     break;
  600.   case FS_GOT_DATA:
  601.     if (HTRequest_isPostWeb(request)) {
  602. if (HTRequest_isDestination(request)) {
  603.     HTLink *link =
  604. HTAnchor_findLink((HTAnchor *) request->source->anchor,
  605.   (HTAnchor *) anchor);
  606.     HTLink_setResult(link, HT_LINK_OK);
  607. }
  608.     }
  609.     FileCleanup(request, HT_LOADED);
  610.     return HT_OK;
  611.     break;
  612.   case FS_NO_DATA:
  613.     if (HTRequest_isPostWeb(request)) {
  614. if (HTRequest_isDestination(request)) {
  615.     HTLink *link =
  616. HTAnchor_findLink((HTAnchor *) request->source->anchor,
  617.   (HTAnchor *) anchor);
  618.     HTLink_setResult(link, HT_LINK_OK);
  619. }
  620.     }
  621.     FileCleanup(request, HT_NO_DATA);
  622.     return HT_OK;
  623.     break;
  624.   case FS_RETRY:
  625.     if (HTRequest_isPostWeb(request)) {
  626. if (HTRequest_isDestination(request)) {
  627.     HTLink *link =
  628. HTAnchor_findLink((HTAnchor *) request->source->anchor,
  629.   (HTAnchor *) anchor);
  630.     HTLink_setResult(link, HT_LINK_ERROR);
  631. }
  632. HTRequest_killPostWeb(request);
  633.     }
  634.     FileCleanup(request, HT_RETRY);
  635.     return HT_OK;
  636.     break;
  637.   case FS_ERROR:
  638.     if (HTRequest_isPostWeb(request)) {
  639. if (HTRequest_isDestination(request)) {
  640.     HTLink *link =
  641. HTAnchor_findLink((HTAnchor *) request->source->anchor,
  642.   (HTAnchor *) anchor);
  643.     HTLink_setResult(link, HT_LINK_ERROR);
  644. }
  645. HTRequest_killPostWeb(request);
  646.     }
  647.     FileCleanup(request, HT_ERROR);
  648.     return HT_OK;
  649.     break;
  650. }
  651.     } /* End of while(1) */
  652. }