home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  48.7 KB  |  1,831 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /*
  19.  * state machine to handle local file and directory URL's
  20.  * and to retreive file cache objects
  21.  *
  22.  * Designed and implemented by Lou Montulli '94
  23.  */
  24.  
  25. #include "mkutils.h"
  26. #include "mkgeturl.h"
  27. #include "mkstream.h"
  28. #include "mkformat.h"
  29. #include "mkparse.h"
  30. #include "mkftp.h"
  31. #include "mkfsort.h"
  32. #include "mkfile.h"
  33. #include "merrors.h"
  34. #include "glhist.h"
  35.  
  36. #include "il_strm.h"            /* Image Lib stream converters. */
  37. #include "libimg.h"             /* Image Lib public API. */
  38.  
  39. #if defined(XP_WIN) || defined(XP_OS2)
  40. #include "errno.h"
  41. #endif
  42.  
  43. #include "xp_error.h"
  44. #include "prefapi.h"
  45.  
  46. /* for XP_GetString() */
  47. #include "xpgetstr.h"
  48. extern int XP_ALERT_OUTMEMORY;
  49. extern int XP_PROGRESS_DIRDONE;
  50. extern int XP_PROGRESS_FILEDONE;
  51. extern int XP_PROGRESS_FILEZEROLENGTH;
  52. extern int XP_PROGRESS_READDIR;
  53. extern int XP_PROGRESS_READFILE;
  54. extern int MK_OUT_OF_MEMORY;
  55. extern int MK_UNABLE_TO_LOCATE_FILE;
  56. extern int MK_UNABLE_TO_OPEN_FILE;
  57. extern int MK_ZERO_LENGTH_FILE;
  58. extern int MK_UNABLE_TO_DELETE_FILE;
  59. extern int MK_UNABLE_TO_DELETE_DIRECTORY;
  60. extern int MK_MKDIR_FILE_ALREADY_EXISTS;
  61. extern int MK_COULD_NOT_CREATE_DIRECTORY;
  62. extern int MK_ERROR_WRITING_FILE;
  63. extern int MK_NOT_A_DIRECTORY;
  64. extern int XP_READING_SEGMENT;
  65. extern int XP_TITLE_DIRECTORY_LISTING;
  66. extern int XP_H1_DIRECTORY_LISTING;
  67. extern int XP_UPTO_HIGHER_LEVEL_DIRECTORY;
  68. extern int XP_TITLE_DIRECTORY_OF_ETC;
  69.  
  70. /* states of the machine
  71.  */
  72. typedef enum {
  73.     NET_CHECK_FILE_TYPE,
  74.     NET_DELETE_FILE,
  75.     NET_MOVE_FILE,
  76.     NET_PUT_FILE,
  77.     NET_MAKE_DIRECTORY,
  78.     NET_FILE_SETUP_STREAM,
  79.     NET_OPEN_FILE,
  80.     NET_SETUP_FILE_STREAM,
  81.     NET_OPEN_DIRECTORY,
  82.     NET_READ_FILE_CHUNK,
  83.     NET_READ_DIRECTORY_CHUNK,
  84.     NET_BEGIN_PRINT_DIRECTORY,
  85.     NET_PRINT_DIRECTORY,
  86.     NET_FILE_DONE,
  87.     NET_FILE_ERROR_DONE,
  88.     NET_FILE_FREE
  89. } net_FileStates;
  90.  
  91. /* forward decl */
  92. PRIVATE int32 net_ProcessFile (ActiveEntry * cur_entry);
  93.  
  94. /* allow URL byterange requests for backwards compatibility,
  95.  * but only leave this in for a short while
  96.  */
  97. #define URL_BYTERANGE_METHOD
  98.  
  99. #define DIR_STRUCT struct dirent
  100.  
  101. typedef struct _FILEConData {
  102.     XP_File           file_ptr;
  103.     XP_Dir            dir_ptr;
  104.     net_FileStates    next_state;
  105.     NET_StreamClass * stream;
  106.     char            * filename;
  107.     Bool           is_dir;
  108.     SortStruct      * sort_base;
  109.     Bool           pause_for_read;
  110.     Bool           is_cache_file;
  111.     Bool           calling_netlib_all_the_time;
  112.     Bool destroy_graph_progress;  /* do we need to destroy graph progress? */
  113.     int32   original_content_length; /* the content length at the time of
  114.                                       * calling graph progress
  115.                                       */
  116.     char             * byterange_string; 
  117.     int32              range_length;  /* the length of the current byte range */
  118. } FILEConData;
  119.  
  120. #define CD_FILE_PTR       connection_data->file_ptr
  121. #define CD_DIR_PTR        connection_data->dir_ptr
  122. #define CD_NEXT_STATE     connection_data->next_state
  123. #define CD_STREAM         connection_data->stream
  124. #define CD_FILENAME       connection_data->filename
  125. #define CD_IS_DIR         connection_data->is_dir
  126. #define CD_SORT_BASE      connection_data->sort_base
  127. #define CD_PAUSE_FOR_READ connection_data->pause_for_read
  128. #define CD_IS_CACHE_FILE  connection_data->is_cache_file
  129.  
  130. #define CE_WINDOW_ID      cur_entry->window_id
  131. #define CE_URL_S          cur_entry->URL_s
  132. #define CE_STATUS         cur_entry->status
  133. #define CE_SOCK           cur_entry->socket
  134. #define CE_BYTES_RECEIVED cur_entry->bytes_received
  135. #define CE_FORMAT_OUT     cur_entry->format_out
  136. #define CD_DESTROY_GRAPH_PROGRESS   connection_data->destroy_graph_progress
  137. #define CD_ORIGINAL_CONTENT_LENGTH  connection_data->original_content_length
  138. #define CD_BYTERANGE_STRING         connection_data->byterange_string
  139. #define CD_RANGE_LENGTH             connection_data->range_length
  140.  
  141. #define PUTS(s)           (*connection_data->stream->put_block) \
  142.                                  (connection_data->stream, s, XP_STRLEN(s))
  143. #define IS_WRITE_READY    (*connection_data->stream->is_write_ready) \
  144.                                  (connection_data->stream)
  145. #define PUTB(b,l)         (*connection_data->stream->put_block) \
  146.                                 (connection_data->stream, b, l)
  147. #define COMPLETE_STREAM   (*connection_data->stream->complete) \
  148.                                  (connection_data->stream)
  149. #define ABORT_STREAM(s)   (*connection_data->stream->abort) \
  150.                                  (connection_data->stream, s)
  151.  
  152. /* try and open the URL path as a normal file
  153.  * if it fails set the next state to try and open
  154.  * a directory.
  155.  */
  156. PRIVATE int
  157. net_check_file_type (ActiveEntry * cur_entry)
  158. {
  159.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  160.     XP_StatStruct stat_entry;
  161.  
  162.     TRACEMSG(("Looking for file: %s", CD_FILENAME));
  163.  
  164.     /* larubbio added xpSARCache check */
  165.     if(-1 == XP_Stat (CD_FILENAME, 
  166.                 &stat_entry, 
  167.                 CD_IS_CACHE_FILE ? 
  168.                     (CE_URL_S->ext_cache_file ? 
  169.                     (CE_URL_S->SARCache ? xpSARCache : xpExtCache) : xpCache) : xpURL))
  170.       {
  171.         if(CE_URL_S->method == URL_PUT_METHOD)
  172.             {
  173.             CD_NEXT_STATE = NET_PUT_FILE;
  174.             return(0);
  175.             }
  176.         else if(CE_URL_S->method == URL_MKDIR_METHOD)
  177.             {
  178.             CD_NEXT_STATE = NET_MAKE_DIRECTORY;
  179.             return(0);
  180.             }
  181.         
  182.  
  183.         CE_URL_S->error_msg = NET_ExplainErrorDetails(
  184.                                             MK_UNABLE_TO_LOCATE_FILE,
  185.                                              *CD_FILENAME ? CD_FILENAME : "/");
  186.         return(MK_UNABLE_TO_LOCATE_FILE);
  187.       }
  188.  
  189.     if(!CD_IS_CACHE_FILE)
  190.       {
  191.         CE_URL_S->last_modified = stat_entry.st_mtime;
  192.       }
  193.  
  194.     if(S_ISDIR(stat_entry.st_mode))
  195.       {
  196.         /* if the current address doesn't end with a
  197.          * slash, add it now.
  198.          */
  199.         if(CE_URL_S->address[XP_STRLEN(CE_URL_S->address)-1] != '/')
  200.             StrAllocCat(CE_URL_S->address, "/");
  201.  
  202.         CE_URL_S->is_directory = TRUE;
  203.         CD_IS_DIR = TRUE;
  204.       }
  205.     else
  206.       {
  207.         CE_URL_S->content_length = (int32) stat_entry.st_size;
  208.       }
  209.  
  210.     if(CD_IS_CACHE_FILE)
  211.       {
  212.         /* if we are looking for a cache file then
  213.          * we are always looking to do a GET of
  214.          * the file regardless of the method in
  215.          * the URL_s
  216.          */
  217.            CD_NEXT_STATE = NET_FILE_SETUP_STREAM;
  218.       }
  219.     else switch(CE_URL_S->method)
  220.       {
  221.         case URL_PUT_METHOD:
  222.             {
  223.               char * msg;
  224.               msg = PR_smprintf("Overwrite file %s?", CD_FILENAME);
  225.               if(FE_Confirm(CE_WINDOW_ID, msg))
  226.                   CD_NEXT_STATE = NET_PUT_FILE;
  227.               else
  228.                   CD_NEXT_STATE = NET_FILE_DONE;
  229.             }
  230.             break;
  231.  
  232.         case URL_MKDIR_METHOD:
  233.             /* error, can't create a directory over
  234.               * an existing file or directory
  235.               */
  236.             CE_URL_S->error_msg = NET_ExplainErrorDetails(
  237.                                                 MK_MKDIR_FILE_ALREADY_EXISTS,
  238.                                                 CD_FILENAME);
  239.             return(MK_MKDIR_FILE_ALREADY_EXISTS);
  240.     
  241.         case URL_HEAD_METHOD:
  242.             /* we just need the relavent file info.
  243.              * it is currently filled into the URL struct
  244.              */
  245.             CD_NEXT_STATE = NET_FILE_DONE;
  246.             break;
  247.  
  248.         case URL_MOVE_METHOD:
  249.             CD_NEXT_STATE = NET_MOVE_FILE;
  250.             break;
  251.  
  252.         case URL_DELETE_METHOD:
  253.             CD_NEXT_STATE = NET_DELETE_FILE;
  254.             break;
  255.     
  256.         case URL_GET_METHOD:
  257.             CD_NEXT_STATE = NET_FILE_SETUP_STREAM;
  258.             break;
  259.  
  260.         case URL_INDEX_METHOD:
  261.             if(!CE_URL_S->is_directory)
  262.               {
  263.                 CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_NOT_A_DIRECTORY, CD_FILENAME);
  264.                 return(MK_NOT_A_DIRECTORY);
  265.               }
  266.             CD_NEXT_STATE = NET_FILE_SETUP_STREAM;
  267.             break;
  268.  
  269.         default:
  270.             XP_ASSERT(0);
  271.             CD_NEXT_STATE = NET_FILE_DONE;
  272.             break;
  273.       }
  274.             
  275.     return(0);
  276. }
  277.  
  278. PRIVATE int
  279. net_delete_file (ActiveEntry * ce)
  280. {
  281. #ifndef LIVEWIRE
  282.     XP_ASSERT(0);
  283.     return(MK_OUT_OF_MEMORY);
  284.  
  285. #else
  286.     FILEConData * cd = (FILEConData *) ce->con_data;
  287.  
  288.     TRACEMSG(("Deleteing file: %s", cd->filename));
  289.  
  290.     if(cd->is_dir)
  291.       {
  292.         if(0 != XP_RemoveDirectory(cd->filename, xpURL))
  293.           {
  294.             /* error */
  295.             ce->URL_s->error_msg = NET_ExplainErrorDetails(
  296.                                                 MK_UNABLE_TO_DELETE_DIRECTORY, 
  297.                                                 cd->filename);
  298.             return(MK_UNABLE_TO_DELETE_DIRECTORY);
  299.           }
  300.       }
  301.     else
  302.       {
  303.         if(0 != XP_FileRemove(cd->filename, xpURL))
  304.             {
  305.             /* error */
  306.             ce->URL_s->error_msg = NET_ExplainErrorDetails(
  307.                                                     MK_UNABLE_TO_DELETE_FILE,
  308.                                                     cd->filename);
  309.             return(MK_UNABLE_TO_DELETE_FILE);
  310.             }
  311.       }
  312.  
  313.     /* success */
  314.     cd->next_state = NET_FILE_DONE;
  315.     return(0);
  316. #endif /* NOT LIVEWIRE */
  317. }
  318.  
  319. PRIVATE int
  320. net_move_file (ActiveEntry * ce)
  321. {
  322. #ifndef LIVEWIRE
  323.     XP_ASSERT(0);
  324.     return(MK_OUT_OF_MEMORY);
  325.  
  326. #else
  327.     FILEConData * cd = (FILEConData *) ce->con_data;
  328.     char *destination;
  329.  
  330.     XP_ASSERT(ce->URL_s->destination);
  331.     if(!ce->URL_s->destination)
  332.         return(MK_UNABLE_TO_LOCATE_FILE);
  333.  
  334.     destination = NET_ParseURL(ce->URL_s->destination, GET_PATH_PART);
  335.  
  336.     if(!destination)
  337.         return(MK_OUT_OF_MEMORY);
  338.  
  339.     TRACEMSG(("Moving file: %s", cd->filename));
  340.  
  341.     if(0 != XP_FileRename(cd->filename, xpURL, destination, xpURL))
  342.       {
  343.           /* error! */
  344.         XP_ASSERT(0);
  345.         /* @@@@@ */
  346.         return(MK_UNABLE_TO_LOCATE_FILE);
  347.       }
  348.  
  349.     /* success */
  350.     cd->next_state = NET_FILE_DONE;
  351.     return(0);
  352. #endif /* !LIVEWIRE */
  353. }
  354.  
  355. PRIVATE int
  356. net_put_file (ActiveEntry * ce)
  357. {
  358. #ifndef LIVEWIRE
  359.     XP_ASSERT(0);
  360.     return(MK_OUT_OF_MEMORY);
  361.  
  362. #else
  363.     FILEConData * cd = (FILEConData *) ce->con_data;
  364.     XP_File fp;
  365.     int status;
  366.  
  367.     if(!(fp = XP_FileOpen(cd->filename, xpURL, XP_FILE_WRITE_BIN)))
  368.       {
  369.         /* error */
  370.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_OPEN_FILE, 
  371.                                                       cd->filename);
  372.         return(MK_UNABLE_TO_OPEN_FILE);
  373.       }
  374.         
  375.     XP_ASSERT(ce->URL_s->post_data);
  376.     XP_ASSERT(!ce->URL_s->post_data_is_file);
  377.  
  378.     status = XP_FileWrite(ce->URL_s->post_data, ce->URL_s->post_data_size, fp);
  379.  
  380.     XP_FileClose(fp);
  381.  
  382.     if(status < ce->URL_s->post_data_size)
  383.       {
  384.         /* error */
  385.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_FILE_WRITE_ERROR, 
  386.                                                       cd->filename);
  387.         return(MK_FILE_WRITE_ERROR);
  388.       }
  389.  
  390.     /* success */
  391.     cd->next_state = NET_FILE_DONE;
  392.     return(0);
  393. #endif /* !LIVEWIRE */
  394. }
  395.  
  396. PRIVATE int
  397. net_make_directory (ActiveEntry * ce)
  398. {
  399. #ifndef LIVEWIRE
  400.     XP_ASSERT(0);
  401.     return(MK_OUT_OF_MEMORY);
  402.  
  403. #else
  404.     FILEConData * cd = (FILEConData *) ce->con_data;
  405.  
  406.     if(0 != XP_MakeDirectory(cd->filename, xpURL))
  407.       {
  408.         /* error */
  409.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  410.                                                 MK_COULD_NOT_CREATE_DIRECTORY,
  411.                                                 cd->filename);
  412.         return(MK_COULD_NOT_CREATE_DIRECTORY);
  413.       }
  414.  
  415.     /* success */
  416.     cd->next_state = NET_FILE_DONE;
  417.     return (0);
  418. #endif /* !LIVEWIRE */
  419. }
  420.  
  421. /* setup the output stream
  422.  */
  423. PRIVATE int
  424. net_file_setup_stream(ActiveEntry * cur_entry)
  425. {
  426.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  427.  
  428.     if(CD_IS_DIR)
  429.       {        
  430.     char * pUrl ;
  431.     int status;
  432.  
  433.     status = PREF_CopyCharPref("protocol.mimefor.file",&pUrl);
  434.  
  435.     if (status >= 0 && pUrl) {
  436.         StrAllocCopy(CE_URL_S->content_type, pUrl);
  437.         XP_FREE(pUrl);
  438.     } else {
  439.          StrAllocCopy(CE_URL_S->content_type, APPLICATION_HTTP_INDEX);
  440.     }
  441.  
  442.         CD_STREAM = NET_StreamBuilder(CE_FORMAT_OUT, CE_URL_S, CE_WINDOW_ID);
  443.  
  444.         if(!CD_STREAM)
  445.           {
  446.             CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
  447.             return(MK_UNABLE_TO_CONVERT);
  448.           }
  449.  
  450. #ifdef MOZILLA_CLIENT
  451.         GH_UpdateGlobalHistory(CE_URL_S);
  452. #endif /* MOZILLA_CLIENT */
  453.  
  454.         CD_NEXT_STATE = NET_OPEN_DIRECTORY;
  455.       }
  456.     else
  457.       {
  458.         /* 
  459.          * don't open the stream yet for files
  460.          */
  461.         CD_NEXT_STATE = NET_OPEN_FILE;
  462.       }
  463.  
  464.     return(0);  /* OK */
  465. }
  466.  
  467. PRIVATE int
  468. net_open_file (ActiveEntry * cur_entry)
  469. {
  470.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  471.  
  472.     TRACEMSG(("MKFILE: Trying to open: %s\n",CD_FILENAME));
  473.  
  474.     if((CD_FILE_PTR = XP_FileOpen(CD_FILENAME, 
  475.                                    (CD_IS_CACHE_FILE ? 
  476.                                      (CE_URL_S->ext_cache_file ? 
  477.                                        (CE_URL_S->SARCache ? xpSARCache : xpExtCache) : xpCache) : xpURL),
  478.                                          XP_FILE_READ_BIN)) != NULL)
  479.       {
  480.         CD_NEXT_STATE = NET_READ_FILE_CHUNK;
  481.       }
  482.     else
  483.       {
  484.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_OPEN_FILE, 
  485.                                                       CD_FILENAME);
  486.  
  487.         /* if errno is equal to "Too many open files"
  488.          * return a special error
  489.          */
  490. #ifndef XP_MAC
  491.         if(errno == EMFILE)
  492.             return(MK_TOO_MANY_OPEN_FILES);
  493.         else
  494. #endif
  495.             return(MK_UNABLE_TO_OPEN_FILE);
  496.       }
  497.  
  498.     if (!CE_URL_S->load_background)
  499.         NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_READFILE));
  500.  
  501.     /* CE_SOCK = XP_Fileno(CD_FILE_PTR); */
  502.     CE_SOCK = NULL;
  503.     cur_entry->local_file = TRUE;
  504.     NET_SetFileReadSelect(CE_WINDOW_ID, XP_Fileno(CD_FILE_PTR)); 
  505.  
  506.     /* @@@ now that we have determined that it is a local file
  507.      * set URL_s->cache_file so that the streams think
  508.      * that this file came from the cache
  509.      */
  510.     StrAllocCopy(CE_URL_S->cache_file, CD_FILENAME);
  511.  
  512.     CD_PAUSE_FOR_READ = TRUE;
  513.  
  514.     CD_NEXT_STATE = NET_SETUP_FILE_STREAM;
  515.  
  516.     if (!CE_URL_S->load_background) {
  517.         FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
  518.         CD_DESTROY_GRAPH_PROGRESS = TRUE;  /* we will need to destroy it */
  519.     }
  520.     CD_ORIGINAL_CONTENT_LENGTH = CE_URL_S->content_length;
  521.  
  522.     return(0);  /* ok */
  523. }
  524.  
  525.  
  526. #ifdef MOZILLA_CLIENT
  527. #ifdef MOZ_MAIL_NEWS
  528. extern int net_InitializeNewsFeData (ActiveEntry * cur_entry);
  529. extern int IMAP_InitializeImapFeData (ActiveEntry * cur_entry);
  530. #endif /* MOZ_MAIL_NEWS */
  531. #endif /* MOZILLA_CLIENT */
  532.  
  533. PRIVATE int
  534. net_setup_file_stream (ActiveEntry * cur_entry)
  535. {
  536.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  537.     int32 count;
  538.  
  539.     /* set up the stream now
  540.      */
  541.     TRACEMSG(("Trying to look at content type: %s",CE_URL_S->content_type));
  542.     if(!CE_URL_S->content_type)
  543.       {
  544.         int img_type;
  545.         
  546.         /* read the first chunk of the file
  547.          */
  548.         count = XP_FileRead(NET_Socket_Buffer, NET_Socket_Buffer_Size, CD_FILE_PTR);
  549.     
  550.         if(!count)
  551.           {
  552.             NET_ClearFileReadSelect(CE_WINDOW_ID, XP_Fileno(CD_FILE_PTR));
  553.             NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_FILEZEROLENGTH));
  554.             XP_FileClose(CD_FILE_PTR);
  555.             CE_SOCK = NULL;
  556.             CD_FILE_PTR = 0;
  557.             CD_NEXT_STATE = NET_FILE_ERROR_DONE;
  558.             CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_ZERO_LENGTH_FILE, 
  559.                                                           CD_FILENAME);
  560.             return(MK_ZERO_LENGTH_FILE);
  561.           }
  562.     
  563. #ifdef MOZILLA_CLIENT
  564.         img_type = IL_Type(NET_Socket_Buffer, count);
  565.         if (img_type && img_type != IL_NOTFOUND)
  566.           {
  567.             switch(img_type)
  568.               {
  569.                 case IL_GIF:
  570.                     StrAllocCopy(CE_URL_S->content_type, IMAGE_GIF);
  571.                     break;
  572.                 case IL_XBM:
  573.                     StrAllocCopy(CE_URL_S->content_type, IMAGE_XBM);
  574.                     break;
  575.                 case IL_JPEG:
  576.                     StrAllocCopy(CE_URL_S->content_type, IMAGE_JPG);
  577.                     break;
  578.                         case IL_PNG:
  579.                     StrAllocCopy(CE_URL_S->content_type, IMAGE_PNG);
  580.                     break;
  581.  
  582.                 case IL_PPM:
  583.                     StrAllocCopy(CE_URL_S->content_type, IMAGE_PPM);
  584.                     break;
  585.                 default:
  586.                     assert(0);  /* should NEVER get this!! */
  587.                     break;
  588.               }
  589.                     
  590.           }
  591.         else
  592. #endif /* MOZILLA_CLIENT */
  593.         {
  594. #if defined(XP_MAC)
  595.               char *    macType, *macEncoding;
  596.               Bool    useDefault;
  597.  
  598.             FE_FileType(CE_URL_S->address, &useDefault, &macType, &macEncoding);
  599.             if (useDefault)
  600.             {
  601. #endif
  602.                 NET_cinfo * type = NET_cinfo_find_type(CE_URL_S->address);
  603.                 NET_cinfo * enc = NET_cinfo_find_enc(CE_URL_S->address);
  604.  
  605. #if defined(XP_MAC) 
  606.                 if (macType)
  607.                     XP_FREE(macType);
  608. #endif                
  609.  
  610.                 StrAllocCopy(CE_URL_S->content_type, type->type); 
  611.                 StrAllocCopy(CE_URL_S->content_encoding, enc->encoding); 
  612.                 
  613.     
  614.                 if(type->is_default && NET_ContainsHTML(NET_Socket_Buffer, count))
  615.                     StrAllocCopy(CE_URL_S->content_type, TEXT_HTML);
  616.  
  617. #if defined(XP_MAC)
  618.             }
  619.             else
  620.             {
  621.                 CE_URL_S->content_type = macType;
  622.                 CE_URL_S->content_encoding = macEncoding;
  623.             }
  624. #endif
  625.           }
  626.       }
  627.  
  628.     CD_RANGE_LENGTH = 0;
  629.     CE_BYTES_RECEIVED = 0;
  630.  
  631.     /* check to see if we need to do a byterange here
  632.      * If we are then set the ranges in the URL
  633.      * so that the streams know the byte range.
  634.      * Once we parse one byterange copy
  635.      */
  636.     if(CD_BYTERANGE_STRING && *CD_BYTERANGE_STRING)
  637.       {
  638.         int32 low=0, high=0;
  639.         char *cp, *dash;
  640.  
  641.         cp = XP_STRCHR(CD_BYTERANGE_STRING, ',');
  642.         if(cp)
  643.             *cp = '\0';
  644.  
  645.         dash = XP_STRCHR(CD_BYTERANGE_STRING, '-');
  646.  
  647.         if(!dash)
  648.           {
  649.             /* no dash is an invalid byte range
  650.              * return zero and we will come back
  651.              * in here and give the whole file or
  652.              * the next byterange
  653.              */
  654.             return(0);
  655.           }
  656.         else
  657.           {
  658.             *dash = '\0';
  659.             
  660.             if(CD_BYTERANGE_STRING == dash)
  661.                 low = -1;
  662.             else
  663.                 low = atol(CD_BYTERANGE_STRING);
  664.             high = atol(dash+1);
  665.           }
  666.  
  667.  
  668.         if(low < 0 && high < 1)
  669.           {
  670.             /* if both low and high are non-positive
  671.              * it's an error
  672.              */
  673.             return(0);
  674.           }
  675.         else if(low < 0)
  676.           {
  677.             /* This is a byte range that specifies
  678.              * the last bytes of a file.  For
  679.              * instance  http://host/dir/foo;byterange=-500
  680.              */
  681.             low = CE_URL_S->content_length-(atoi(CD_BYTERANGE_STRING+1));
  682.             high = CE_URL_S->content_length;
  683.           }
  684.         else if(high < 1)
  685.           {
  686.             /* This is a byte range that specifies
  687.              * the a byte range from a specified point 
  688.              * till the end of the file.  For
  689.              * instance  http://host/dir/foo;byterange=500-
  690.              */
  691.             high = CE_URL_S->content_length;
  692.           }
  693.  
  694.         if(low >= CE_URL_S->content_length)
  695.           {
  696.             /* error to have the low byte range be larger than
  697.              * the file
  698.              */
  699.             return(0);
  700.           }
  701.         else if(high >= CE_URL_S->content_length)
  702.           {
  703.             high = CE_URL_S->content_length;
  704.           }
  705.  
  706.         CE_URL_S->low_range = low;
  707.         CE_URL_S->high_range = high;
  708.  
  709.         CD_RANGE_LENGTH = (high-low)+1;
  710.  
  711.         if(cp)
  712.             XP_MEMCPY(CD_BYTERANGE_STRING, cp+1, XP_STRLEN(cp+1)+1);
  713.         else
  714.             CD_BYTERANGE_STRING = 0;
  715.       }
  716.  
  717.     if (CLEAR_CACHE_BIT(CE_FORMAT_OUT) == FO_PRESENT)
  718.     {
  719.       if (NET_URL_Type(CE_URL_S->address) == NEWS_TYPE_URL)
  720.       {
  721. #ifdef MOZILLA_CLIENT
  722. #ifdef MOZ_MAIL_NEWS
  723.         /* #### DISGUSTING KLUDGE to make cacheing work for news articles. */
  724.         CE_STATUS = net_InitializeNewsFeData (cur_entry);
  725.         if (CE_STATUS < 0)
  726.           {
  727.             /* #### what error message? */
  728.             return CE_STATUS;
  729.           }
  730. #endif /* MOZ_MAIL_NEWS */        
  731. #else
  732.         XP_ASSERT(0);
  733. #endif /* MOZILLA_CLIENT */
  734.       }
  735.       else if (!XP_STRNCMP(CE_URL_S->address, "Mailbox://", 10))
  736.       {
  737. #ifdef MOZILLA_CLIENT
  738. #ifdef MOZ_MAIL_NEWS
  739.         /* #### DISGUSTING KLUDGE to make cacheing work for imap articles. */
  740.         CE_STATUS = IMAP_InitializeImapFeData (cur_entry);
  741.         if (CE_STATUS < 0)
  742.           {
  743.             /* #### what error message? */
  744.             return CE_STATUS;
  745.           }
  746. #endif /* MOZ_MAIL_NEWS */        
  747. #else
  748.         XP_ASSERT(0);
  749. #endif /* MOZILLA_CLIENT */
  750.       }
  751.     }
  752.  
  753.     CD_STREAM = NET_StreamBuilder(CE_FORMAT_OUT, CE_URL_S, CE_WINDOW_ID);
  754.  
  755.     if(!CD_STREAM)
  756.       {
  757.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
  758.         return(MK_UNABLE_TO_CONVERT);
  759.       }
  760.  
  761. #ifdef MOZILLA_CLIENT
  762.     GH_UpdateGlobalHistory(CE_URL_S);
  763. #endif /* MOZILLA_CLIENT */
  764.  
  765.     /* seek back to the beginning so that
  766.      * we are not forced to write out this
  767.      * first block without calling is_write_ready
  768.      *
  769.      * this also seeks to the beginning of the range that we
  770.      * are going to send
  771.       */
  772.     XP_FileSeek(CD_FILE_PTR, CE_URL_S->low_range, SEEK_SET);
  773.  
  774.     CD_NEXT_STATE = NET_READ_FILE_CHUNK;
  775.     CD_PAUSE_FOR_READ = TRUE;
  776.  
  777.     return(CE_STATUS);  /* ok */
  778. }
  779.  
  780. PRIVATE int
  781. net_open_directory (ActiveEntry * cur_entry)
  782. {
  783.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  784.  
  785.     TRACEMSG(("Trying to open directory: %s", CD_FILENAME));
  786.  
  787.     if((CD_DIR_PTR = XP_OpenDir (CD_FILENAME, xpURL)) != NULL)
  788.       {
  789.         CD_NEXT_STATE = NET_READ_DIRECTORY_CHUNK;
  790.       }
  791.     else
  792.       {
  793.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_OPEN_FILE,
  794.                                                       CD_FILENAME);
  795.         return(MK_UNABLE_TO_OPEN_FILE);
  796.       }
  797.  
  798.     CD_IS_DIR = TRUE;
  799.  
  800.     FE_GraphProgressInit(CE_WINDOW_ID, CE_URL_S, CE_URL_S->content_length);
  801.     CD_DESTROY_GRAPH_PROGRESS = TRUE;  /* we will need to destroy it */
  802.     CD_ORIGINAL_CONTENT_LENGTH = CE_URL_S->content_length;
  803.     NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_READDIR));
  804.  
  805.     /* make sure the last character isn't a slash
  806.      */
  807.     if(CD_FILENAME[XP_STRLEN(CD_FILENAME)-1] == '/')
  808.         CD_FILENAME[XP_STRLEN(CD_FILENAME)-1] = '\0';
  809.  
  810.     return(0);  /* ok */
  811. }
  812.  
  813. /* read part of a file and push it out the stream
  814.  */
  815. PRIVATE int
  816. net_read_file_chunk(ActiveEntry * cur_entry)
  817. {
  818.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  819.     int count;
  820.     int32 read_size = IS_WRITE_READY;
  821.  
  822.     /* If the stream sink doesn't want any data yet, do nothing. */
  823.     if (read_size == 0)
  824.       {
  825.         CD_PAUSE_FOR_READ = TRUE;
  826.         return NET_READ_FILE_CHUNK;
  827.       }
  828.  
  829.     read_size = MIN(read_size, NET_Socket_Buffer_Size);
  830.  
  831.     if(CD_RANGE_LENGTH)
  832.         read_size = MIN(read_size, CD_RANGE_LENGTH-CE_BYTES_RECEIVED);
  833.     
  834.     count = XP_FileRead(NET_Socket_Buffer, read_size, CD_FILE_PTR);
  835.  
  836.     if(count < 1 || (CD_RANGE_LENGTH && CE_BYTES_RECEIVED >= CD_RANGE_LENGTH))
  837.       {
  838.         if(CE_URL_S->real_content_length > CE_URL_S->content_length)
  839.           {
  840.             /* this is a case of a partial cache file.
  841.              * we need to fall out of here and
  842.              * get the rest of the file from the
  843.              * network
  844.              */
  845.             cur_entry->save_stream = CD_STREAM;
  846.             CD_STREAM = NULL;
  847.             NET_ClearFileReadSelect(CE_WINDOW_ID, XP_Fileno(CD_FILE_PTR));
  848.             if (!CE_URL_S->load_background)
  849.                 NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_FILEDONE));
  850.             XP_FileClose(CD_FILE_PTR);
  851.             CE_SOCK = NULL;
  852.             CD_FILE_PTR = 0;
  853.             CD_NEXT_STATE = NET_FILE_FREE;
  854.             return(MK_GET_REST_OF_PARTIAL_FILE_FROM_NETWORK);
  855.           }
  856.         else if(CD_BYTERANGE_STRING && *CD_BYTERANGE_STRING)
  857.           {
  858.             /* go get more byte ranges */
  859.             COMPLETE_STREAM;
  860.             CD_NEXT_STATE = NET_SETUP_FILE_STREAM;
  861.             if (!CE_URL_S->load_background)
  862.                 NET_Progress(CE_WINDOW_ID, XP_GetString( XP_READING_SEGMENT ) );
  863.             return(0);
  864.           }
  865.  
  866.         NET_ClearFileReadSelect(CE_WINDOW_ID, XP_Fileno(CD_FILE_PTR));
  867.         if (!CE_URL_S->load_background)
  868.             NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_FILEDONE));
  869.         XP_FileClose(CD_FILE_PTR);
  870.         CE_SOCK = NULL;
  871.         CD_FILE_PTR = 0;
  872.         CD_NEXT_STATE = NET_FILE_DONE;
  873.         return(MK_DATA_LOADED);
  874.       }
  875.  
  876.     CE_BYTES_RECEIVED += count;
  877.  
  878.     CE_STATUS = PUTB(NET_Socket_Buffer, count);
  879.  
  880.     if (!CE_URL_S->load_background)
  881.         FE_GraphProgress(CE_WINDOW_ID, CE_URL_S, CE_BYTES_RECEIVED, count,
  882.                          CE_URL_S->content_length);
  883.  
  884.     CD_PAUSE_FOR_READ = TRUE;
  885.  
  886.     return(CE_STATUS);
  887. }
  888.  
  889. PRIVATE int
  890. net_read_directory_chunk (ActiveEntry * cur_entry)
  891. {
  892.  
  893.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  894.     NET_FileEntryInfo * file_entry;
  895.     XP_DirEntryStruct * dir_entry;
  896.     XP_StatStruct       stat_entry;
  897.     char *full_path=0;
  898.     int32 len;
  899.  
  900.     CD_SORT_BASE = NET_SortInit();
  901.  
  902.     while((dir_entry = XP_ReadDir(CD_DIR_PTR)) != 0)
  903.       {
  904.     
  905.         /* skip . and ..
  906.          */
  907.         if(!XP_STRCMP(dir_entry->d_name, "..") || !XP_STRCMP(dir_entry->d_name, "."))
  908.             continue;
  909.  
  910.         file_entry = NET_CreateFileEntryInfoStruct();
  911.         if(!file_entry)
  912.           {
  913.             CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  914.             return(MK_OUT_OF_MEMORY);
  915.           }
  916.  
  917.         /* escape and copy
  918.          */
  919.         file_entry->filename = NET_Escape(dir_entry->d_name, URL_PATH);
  920.  
  921.         /* make a full path */
  922.         len = XP_STRLEN(CD_FILENAME) + XP_STRLEN(dir_entry->d_name) + 30;
  923.         FREEIF(full_path);
  924.         full_path = (char *)XP_ALLOC(len*sizeof(char));
  925.  
  926.         if(!full_path)
  927.             return(MK_OUT_OF_MEMORY);
  928.  
  929.         XP_STRCPY(full_path, CD_FILENAME);
  930.         XP_STRCAT(full_path, "/");
  931.         XP_STRCAT(full_path, dir_entry->d_name);
  932.  
  933.         if(XP_Stat(full_path, &stat_entry, xpURL) != -1)
  934.           {
  935.             TRACEMSG(("Found stat info for file %s\n",full_path));
  936.  
  937.             if(S_ISDIR(stat_entry.st_mode))
  938.               {
  939.                 file_entry->special_type = NET_DIRECTORY;
  940.                 StrAllocCat(file_entry->filename, "/");
  941.               }
  942.             else
  943.               {
  944.                 file_entry->cinfo = NET_cinfo_find_type(file_entry->filename);
  945.               }
  946.  
  947.             file_entry->size = (int32) stat_entry.st_size;
  948.             file_entry->date = stat_entry.st_mtime;
  949.  
  950.             TRACEMSG(("Got file: %s, %ld",file_entry->filename, file_entry->size));
  951.  
  952.             NET_SortAdd(CD_SORT_BASE, file_entry);
  953.           }
  954.       }
  955.  
  956.     FREEIF(full_path);
  957.  
  958.     NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_DIRDONE));
  959.  
  960.     XP_CloseDir(CD_DIR_PTR);
  961.     CD_DIR_PTR = 0;
  962.  
  963.     CD_NEXT_STATE = NET_BEGIN_PRINT_DIRECTORY;
  964.  
  965.     return(0); 
  966. }
  967.  
  968. /* emit application/http-index-format */
  969. PRIVATE int
  970. net_BeginPrintDirectory(ActiveEntry * ce)
  971. {
  972.     FILEConData * cd = (FILEConData *) ce->con_data;
  973.  
  974.     cd->next_state = NET_PRINT_DIRECTORY;
  975.  
  976.     return(ce->status);
  977. }
  978.  
  979.  
  980. #define PD_PUTS(s)  \
  981. do { \
  982. if(status > -1) \
  983.     status = (*stream->put_block)(stream, s, XP_STRLEN(s)); \
  984. } while(0)
  985.  
  986. PUBLIC int
  987. NET_PrintDirectory(SortStruct **sort_base, NET_StreamClass * stream, char * path, URL_Struct *URL_s)
  988. {
  989.     NET_FileEntryInfo * file_entry;
  990.     char out_buf[3096];
  991.     char *esc_path = NET_Escape(path, URL_PATH);
  992.     int i;
  993.     int status=0;
  994.  
  995.     NET_DoFileSort(*sort_base);
  996.  
  997.     /* emit 300: URL CRLF */
  998.     XP_STRCPY(out_buf, "300: ");
  999.     PD_PUTS(out_buf);
  1000.     PR_snprintf(out_buf, sizeof(out_buf), URL_s->address);
  1001.     PD_PUTS(out_buf);
  1002.  
  1003.     XP_STRCPY(out_buf, CRLF);
  1004.     PD_PUTS(out_buf);
  1005.  
  1006.     /* emit 200: Filename Size Content-Type File-type Last-Modified */
  1007.     XP_STRCPY(out_buf, "200: Filename Content-Length Content-Type File-type Last-Modified"CRLF);
  1008.     PD_PUTS(out_buf);
  1009.  
  1010.     for(i=0; status > -1 && (file_entry = (NET_FileEntryInfo *) 
  1011.                                 NET_SortRetrieveNumber(*sort_base, i)) != 0;
  1012.              i++)
  1013.       {
  1014.  
  1015.  
  1016.         char * esc_time = NET_Escape(ctime(&file_entry->date), URL_XALPHAS);
  1017.  
  1018.         XP_STRTOK(file_entry->filename, "/");
  1019.         PR_snprintf(out_buf, sizeof(out_buf), "201: %s %ld %s %s %s"CRLF, 
  1020.                     file_entry->filename, 
  1021.                     file_entry->size,
  1022.                     file_entry->special_type == NET_DIRECTORY ? "application/http-index-format" :
  1023.                         file_entry->cinfo ? file_entry->cinfo->type : TEXT_PLAIN,
  1024.                     file_entry->special_type == NET_DIRECTORY ? "Directory" : "File",
  1025.                     esc_time);
  1026.  
  1027.         FREE(esc_time);
  1028.         
  1029.         PD_PUTS(out_buf);
  1030.  
  1031.         NET_FreeEntryInfoStruct(file_entry);
  1032.       }
  1033.  
  1034.     NET_SortFree(*sort_base);
  1035.     *sort_base = 0;
  1036.  
  1037.     FREEIF(esc_path);
  1038.     if(status < 0)
  1039.         return(status);
  1040.     return(MK_DATA_LOADED);
  1041. }
  1042.  
  1043. /* if the url is a local file this function returns
  1044.  * the portion of the url that represents the
  1045.  * file path as a malloc'd string.  If the
  1046.  * url is not a local file url this function
  1047.  * returns NULL
  1048.  */
  1049. PRIVATE char *
  1050. net_return_local_file_part_from_url(char *address)
  1051. {
  1052.     char *host;
  1053. #ifdef XP_WIN
  1054.     char *new_address;
  1055.     char *rv;
  1056. #endif
  1057.     
  1058.     if (address == NULL)
  1059.         return NULL;
  1060.  
  1061.     /* imap urls are never local */
  1062.     if (!strncasecomp(address,"mailbox://",10))
  1063.         return NULL;
  1064.  
  1065.     /* mailbox url's are always local, but don't always point to a file */
  1066.     if(!strncasecomp(address, "mailbox:", 8))
  1067.     {
  1068.         char *filename = NET_ParseURL(address, GET_PATH_PART);
  1069.         if (!filename)
  1070.             filename = XP_STRDUP("");
  1071.         return filename;
  1072.     }
  1073.  
  1074.     if(strncasecomp(address, "file:", 5))
  1075.         return(NULL);
  1076.     
  1077.     host = NET_ParseURL(address, GET_HOST_PART);
  1078.  
  1079.     if(!host || *host == '\0' || !strcasecomp(host, "LOCALHOST"))
  1080.       {
  1081.         FREEIF(host);
  1082.         return(NET_UnEscape(NET_ParseURL(address, GET_PATH_PART)));
  1083.       }
  1084.  
  1085. #ifdef XP_WIN
  1086.     /* get the address minus the search and hash data */
  1087.     new_address = NET_ParseURL(address, 
  1088.                             GET_PROTOCOL_PART | GET_HOST_PART | GET_PATH_PART);
  1089.     NET_UnEscape(new_address);
  1090.  
  1091.     /* check for local drives as hostnames
  1092.      */
  1093.     if(XP_STRLEN(host) == 2
  1094.         && isalpha(host[0]) 
  1095.         && (host[1] == '|' || host[1] == ':'))
  1096.       {
  1097.         FREE(host);
  1098.         /* skip "file:/" */
  1099.         rv = XP_STRDUP(new_address+6);
  1100.         FREE(new_address);
  1101.         return(rv);
  1102.       }
  1103.  
  1104.     if(1)
  1105.       {
  1106.         XP_StatStruct       stat_entry;
  1107.         /* try stating the url just in case since
  1108.          * the local file system can
  1109.          * have url's of the form \\prydain\dist
  1110.          */
  1111.         if(-1 != XP_Stat(address+5, &stat_entry, xpURL))
  1112.           {
  1113.             FREE(host);
  1114.             /* skip "file:" */
  1115.             rv = XP_STRDUP(address+5);
  1116.             FREE(new_address);
  1117.             return(rv);
  1118.           }
  1119.       }
  1120. #endif /* XP_WIN */
  1121.  
  1122.     FREE(host);
  1123.  
  1124.     return(NULL);
  1125. }
  1126.  
  1127. /* returns TRUE if the url is a local file
  1128.  * url
  1129.  */
  1130. PUBLIC XP_Bool
  1131. NET_IsLocalFileURL(char *address)
  1132. {
  1133.     char * cp = net_return_local_file_part_from_url(address);
  1134.  
  1135.     if(cp)
  1136.       {
  1137.         FREE(cp);
  1138.         return(TRUE);
  1139.       }
  1140.     else
  1141.       {
  1142.         return(FALSE);
  1143.       }
  1144. }
  1145.  
  1146. /* load local files and
  1147.  * display directories
  1148.  *
  1149.  * if cache_file is not null then we this module is being used to
  1150.  * read a localy cached url.  Use the passed in cache_file variable
  1151.  * as the location of the file and use the URL->address as the
  1152.  * URL for StreamBuilding etc.
  1153.  */
  1154. PRIVATE int32
  1155. net_FileLoad (ActiveEntry * cur_entry)
  1156. {
  1157.     char * cp;
  1158.     FILEConData * connection_data;
  1159.  
  1160. #ifndef NSPR20_DISABLED
  1161.     NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mkfile");
  1162. #endif /* NSPR20_DISABLED */
  1163.  
  1164.     /* make space for the connection data */
  1165.     cur_entry->con_data = XP_NEW(FILEConData);
  1166.     if(!cur_entry->con_data)
  1167.       {
  1168.         FE_Alert(CE_WINDOW_ID, XP_GetString(XP_ALERT_OUTMEMORY));
  1169.         CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
  1170.         CE_STATUS = MK_OUT_OF_MEMORY;
  1171.         return(CE_STATUS);
  1172.       }
  1173.  
  1174.     /* zero out the structure 
  1175.      */
  1176.     XP_MEMSET(cur_entry->con_data, 0, sizeof(FILEConData));
  1177.  
  1178.     /* set this to make the CD_ macros work */
  1179.     connection_data = cur_entry->con_data;
  1180.  
  1181.     if(!CE_URL_S->cache_file)
  1182.       {
  1183.         char *path;
  1184.  
  1185.         if(!(path = net_return_local_file_part_from_url(CE_URL_S->address)))
  1186.             {
  1187.             FREE(cur_entry->con_data);
  1188.             cur_entry->con_data = 0;
  1189.             return(MK_USE_FTP_INSTEAD); /* use ftp */
  1190.             }
  1191.  
  1192.         CD_FILENAME = path;
  1193.       }
  1194.     else
  1195.       {
  1196.         StrAllocCopy(CD_FILENAME, CE_URL_S->cache_file);
  1197.         CD_IS_CACHE_FILE = TRUE;
  1198.       }
  1199.  
  1200.     TRACEMSG(("Load File: looking for file: %s\n",CD_FILENAME));
  1201.  
  1202.     /* don't cache local files or local directory listings 
  1203.      * if we are only looking to cache this file fail now
  1204.      */
  1205.     if(cur_entry->format_out == FO_CACHE_ONLY)
  1206.         return -1;
  1207.     cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
  1208.  
  1209.     /* all non-cache filenames must begin with slash
  1210.      */
  1211.     if(!CD_IS_CACHE_FILE == TRUE && *CD_FILENAME != '/')
  1212.       {
  1213.         CE_URL_S->error_msg = NET_ExplainErrorDetails(
  1214.                                             MK_UNABLE_TO_LOCATE_FILE,
  1215.                                              *CD_FILENAME ? CD_FILENAME : "/");
  1216.         CE_STATUS = MK_UNABLE_TO_LOCATE_FILE;
  1217.         return(MK_UNABLE_TO_LOCATE_FILE);
  1218.       }
  1219.  
  1220. #define BYTERANGE_TOKEN "bytes="
  1221.  
  1222. #ifdef URL_BYTERANGE_METHOD
  1223.     /* if the URL has ";bytes=" in it then it
  1224.      * is a special partial byterange url.
  1225.      * Parse out the byterange part and store it
  1226.      * away
  1227.      */
  1228. #define URL_BYTERANGE_TOKEN ";"BYTERANGE_TOKEN
  1229.     if (CD_IS_CACHE_FILE && (cp = strcasestr(CE_URL_S->address, URL_BYTERANGE_TOKEN)) != NULL)
  1230.       {
  1231.         StrAllocCopy(CD_BYTERANGE_STRING, cp+XP_STRLEN(URL_BYTERANGE_TOKEN));
  1232.         XP_STRTOK(CD_BYTERANGE_STRING, ";");
  1233.       }
  1234.     else if ((cp = strcasestr(CD_FILENAME, URL_BYTERANGE_TOKEN)) != NULL)
  1235.       {
  1236.         *cp = '\0';
  1237.         /* remove any other weird ; stuff */
  1238.         XP_STRTOK(cp+1, ";");
  1239.         StrAllocCopy(CD_BYTERANGE_STRING, cp+XP_STRLEN(URL_BYTERANGE_TOKEN));
  1240.       }
  1241. #endif /* URL_BYTERANGE_METHOD */
  1242.  
  1243.     /* this is the byterange header method. 
  1244.      * both the URL byterange and the header
  1245.      * byterange methods can coexist peacefully
  1246.      */
  1247.     if(CE_URL_S->range_header && !XP_STRNCMP(CE_URL_S->range_header, BYTERANGE_TOKEN, XP_STRLEN(BYTERANGE_TOKEN)))
  1248.       {
  1249.         StrAllocCopy(CD_BYTERANGE_STRING, CE_URL_S->range_header+XP_STRLEN(BYTERANGE_TOKEN));
  1250.       }
  1251.  
  1252.     /* lets do a local file read 
  1253.      */
  1254.     cur_entry->local_file = TRUE;  /* se we don't select on the socket id */
  1255.  
  1256.        CD_NEXT_STATE = NET_CHECK_FILE_TYPE;
  1257.  
  1258.     return(net_ProcessFile(cur_entry));
  1259. }
  1260.  
  1261. PRIVATE int32
  1262. net_ProcessFile (ActiveEntry * cur_entry)
  1263. {
  1264.     FILEConData * connection_data = (FILEConData *) cur_entry->con_data;
  1265.  
  1266.     TRACEMSG(("Entering ProcessFile with state #%d\n",CD_NEXT_STATE));
  1267.  
  1268.     CD_PAUSE_FOR_READ = FALSE;
  1269.  
  1270.     while(!CD_PAUSE_FOR_READ)
  1271.       {
  1272.  
  1273.         switch (CD_NEXT_STATE)
  1274.           {
  1275.             case NET_CHECK_FILE_TYPE:
  1276.                 CE_STATUS = net_check_file_type(cur_entry);
  1277.                 break;
  1278.     
  1279.             case NET_DELETE_FILE:
  1280.                 CE_STATUS = net_delete_file(cur_entry);
  1281.                 break;
  1282.  
  1283.             case NET_PUT_FILE:
  1284.                 CE_STATUS = net_put_file(cur_entry);
  1285.                 break;
  1286.  
  1287.             case NET_MOVE_FILE:
  1288.                 CE_STATUS = net_move_file(cur_entry);
  1289.                 break;
  1290.  
  1291.             case NET_MAKE_DIRECTORY:
  1292.                 CE_STATUS = net_make_directory(cur_entry);
  1293.                 break;
  1294.  
  1295.             case NET_FILE_SETUP_STREAM:
  1296.                 CE_STATUS = net_file_setup_stream(cur_entry);
  1297.                 break;
  1298.     
  1299.             case NET_OPEN_FILE:
  1300.                 CE_STATUS = net_open_file(cur_entry);
  1301.                 break;
  1302.  
  1303.             case NET_SETUP_FILE_STREAM:
  1304.                 CE_STATUS = net_setup_file_stream(cur_entry);
  1305.                 break;
  1306.     
  1307.             case NET_OPEN_DIRECTORY:
  1308.                 CE_STATUS = net_open_directory(cur_entry);
  1309.                 break;
  1310.     
  1311.             case NET_READ_FILE_CHUNK:
  1312.                 CE_STATUS = net_read_file_chunk(cur_entry);
  1313.                 break;
  1314.     
  1315.             case NET_READ_DIRECTORY_CHUNK:
  1316.                 CE_STATUS = net_read_directory_chunk(cur_entry);
  1317.                 break;
  1318.  
  1319.             case NET_BEGIN_PRINT_DIRECTORY:
  1320.                 CE_STATUS = net_BeginPrintDirectory(cur_entry);
  1321.                 break;
  1322.     
  1323.             case NET_PRINT_DIRECTORY:
  1324.                 if((*CD_STREAM->is_write_ready)(CD_STREAM))
  1325.                 {
  1326.                     CE_STATUS = NET_PrintDirectory(&CD_SORT_BASE, CD_STREAM, CD_FILENAME, CE_URL_S);
  1327.                     CD_NEXT_STATE = NET_FILE_DONE;
  1328.                 }
  1329.                 else
  1330.                 {
  1331.                     /* come back into this state */
  1332.                     if(!connection_data->calling_netlib_all_the_time)
  1333.                     {
  1334.                         connection_data->calling_netlib_all_the_time = TRUE;
  1335.                         NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mkfile");
  1336.                     }
  1337.                     cur_entry->memory_file = TRUE;
  1338.                     cur_entry->socket = NULL;
  1339.                     CD_PAUSE_FOR_READ = TRUE;
  1340.                 }
  1341.                 break;
  1342.     
  1343.             case NET_FILE_DONE:
  1344.                 if(CD_STREAM)
  1345.                        COMPLETE_STREAM;
  1346.                 CD_NEXT_STATE = NET_FILE_FREE;
  1347.                 break;
  1348.     
  1349.             case NET_FILE_ERROR_DONE:
  1350.                 if(CD_STREAM)
  1351.                     ABORT_STREAM(CE_STATUS);
  1352.                 if(CD_DIR_PTR)
  1353.                     XP_CloseDir(CD_DIR_PTR);
  1354.                 if(CD_FILE_PTR)
  1355.                   {
  1356.                     CE_SOCK = NULL;
  1357.                     NET_ClearFileReadSelect(CE_WINDOW_ID, XP_Fileno(CD_FILE_PTR));
  1358.                     XP_FileClose(CD_FILE_PTR);
  1359.                   }
  1360.                 CD_NEXT_STATE = NET_FILE_FREE;
  1361.                 break;
  1362.     
  1363.             case NET_FILE_FREE:
  1364.                 if(connection_data->calling_netlib_all_the_time)
  1365.                 {
  1366.                    NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkfile");
  1367.                    connection_data->calling_netlib_all_the_time = FALSE;
  1368.                 }
  1369.  
  1370.                 if(CD_DESTROY_GRAPH_PROGRESS)
  1371.                     FE_GraphProgressDestroy(CE_WINDOW_ID,
  1372.                                             CE_URL_S,
  1373.                                             CD_ORIGINAL_CONTENT_LENGTH,
  1374.                                             CE_BYTES_RECEIVED);
  1375.                 FREE(CD_FILENAME);
  1376.                 FREEIF(CD_STREAM);
  1377.                 FREE(cur_entry->con_data);
  1378.  
  1379. #ifndef NSPR20_DISABLED
  1380.                 NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mkfile");
  1381. #endif /* NSPR20_DISABLED */
  1382.  
  1383.                 return(-1); /* done */
  1384.           }
  1385.     
  1386.         if(CE_STATUS < 0 && CD_NEXT_STATE != NET_FILE_FREE)
  1387.           {
  1388.             CD_PAUSE_FOR_READ = FALSE;
  1389.             CD_NEXT_STATE = NET_FILE_ERROR_DONE;
  1390.           }
  1391.  
  1392.       }
  1393.  
  1394.     return(0);  /* keep going */
  1395. }
  1396.  
  1397. PRIVATE int32
  1398. net_InterruptFile(ActiveEntry * cur_entry)
  1399. {
  1400.     FILEConData * connection_data = (FILEConData *)cur_entry->con_data;
  1401.  
  1402.     CD_NEXT_STATE = NET_FILE_ERROR_DONE;
  1403.     CE_STATUS = MK_INTERRUPTED;
  1404.  
  1405.     return(net_ProcessFile(cur_entry));
  1406. }
  1407.  
  1408. PRIVATE void
  1409. net_CleanupFile(void)
  1410. {
  1411.     return;
  1412. }
  1413.  
  1414. typedef struct {
  1415.     HTTPIndexParserData *parse_data;
  1416.     MWContext             *context;
  1417.     int                      format_out;
  1418.     URL_Struct            *URL_s;
  1419. } index_format_conv_data_object;
  1420.  
  1421. PRIVATE int net_IdxConvPut(NET_StreamClass *stream, char *s, int32 l)
  1422. {
  1423.     index_format_conv_data_object *obj=stream->data_object;    
  1424.     return(NET_HTTPIndexParserPut(obj->parse_data, s, l));
  1425. }
  1426.  
  1427. PRIVATE int net_IdxConvWriteReady(NET_StreamClass *stream)
  1428. {    
  1429.     return(MAX_WRITE_READY);
  1430. }
  1431.  
  1432. /* take the parsed data and generate HTML */
  1433. PRIVATE void net_IdxConvComplete(NET_StreamClass *inputStream)
  1434. {
  1435.     index_format_conv_data_object *obj=inputStream->data_object;
  1436.     int32 num_files = NET_HTTPIndexParserGetTotalFiles(obj->parse_data);
  1437.     NET_FileEntryInfo * file_entry;
  1438.     int32  max_name_length, i, status = 0, window_width, len;
  1439.     char   out_buf[3096], *name, *date, *ptr;
  1440.     NET_StreamClass *stream;
  1441.     NET_cinfo * cinfo;
  1442.     char *base_url=NULL, *path=NULL;
  1443.  
  1444.     /* direct the stream to the html parser */
  1445.     StrAllocCopy(obj->URL_s->content_type, TEXT_HTML);
  1446.  
  1447.     stream = NET_StreamBuilder(obj->format_out,
  1448.                                 obj->URL_s,
  1449.                                 obj->context);
  1450.     if(!stream)
  1451.         goto cleanup;
  1452.  
  1453.     /* Ask layout how wide the window is in the <PRE> font, to decide
  1454.        how much space we should allocate for the file name column. */
  1455. # define LISTING_OVERHEAD 51
  1456. #ifdef MOZILLA_CLIENT
  1457.     window_width = LO_WindowWidthInFixedChars (obj->context);
  1458. #else
  1459.     window_width = 80;
  1460. #endif /* MOZILLA_CLIENT */
  1461.  
  1462.     max_name_length = (window_width - LISTING_OVERHEAD);
  1463.     if (max_name_length < 12) /* 8.3 */
  1464.         max_name_length = 12;
  1465.     else if (max_name_length > 50)
  1466.         max_name_length = 50;
  1467.  
  1468.     /* the the base url and path */
  1469.     if((base_url = NET_HTTPIndexParserGetBaseURL(obj->parse_data)) != NULL)
  1470.     {
  1471.         path = NET_ParseURL(base_url, GET_PATH_PART);
  1472.  
  1473.         if (path && path != '\0')  /* Not Empty path: */
  1474.         {
  1475.             int end;
  1476.             NET_UnEscape(path);
  1477.  
  1478.             end = XP_STRLEN(path)-1;
  1479.             /* if the path ends with a slash kill it.
  1480.               * that includes the path "/"
  1481.               */
  1482.             if(path[end] == '/')
  1483.               {
  1484.                  path[end] = 0; /* kill the last slash */
  1485.               }
  1486.  
  1487.             /* output "directory of ..." message */
  1488.             PR_snprintf(out_buf, sizeof(out_buf),
  1489.                         XP_GetString( XP_TITLE_DIRECTORY_OF_ETC ),
  1490.                         *path ? path : "/", *path ? path : "/");
  1491.             PD_PUTS(out_buf);
  1492.         }
  1493.     }
  1494.  
  1495.     if(NET_HTTPIndexParserGetHTMLMessage(obj->parse_data))
  1496.       {
  1497.         XP_STRCPY(out_buf, CRLF"<HR>"CRLF);
  1498.         PD_PUTS(out_buf);
  1499.  
  1500.         PD_PUTS(NET_HTTPIndexParserGetHTMLMessage(obj->parse_data));
  1501.  
  1502.         if(!NET_HTTPIndexParserGetTextMessage(obj->parse_data))
  1503.         {
  1504.             XP_STRCPY(out_buf, CRLF"<HR>"CRLF);
  1505.             PD_PUTS(out_buf);
  1506.         }
  1507.       }
  1508.  
  1509.     PR_snprintf(out_buf, sizeof(out_buf), "<PRE>"CRLF);
  1510.     PD_PUTS(out_buf); /* output the line */
  1511.  
  1512.     if(NET_HTTPIndexParserGetTextMessage(obj->parse_data))
  1513.       {
  1514.         char *msg;
  1515.  
  1516.         XP_STRCPY(out_buf, "<HR>");
  1517.         PD_PUTS(out_buf);
  1518.  
  1519.         msg = NET_HTTPIndexParserGetTextMessage(obj->parse_data);
  1520.  
  1521.         status = NET_ScanForURLs (NULL, msg,
  1522.                                   XP_STRLEN(msg),
  1523.                                   out_buf, sizeof(out_buf),
  1524.                                   TRUE);
  1525.         PD_PUTS(out_buf);
  1526.  
  1527.         XP_STRCPY(out_buf, "<HR>");
  1528.         PD_PUTS(out_buf);
  1529.       }
  1530.  
  1531.     /* allow the user to go up if path is not empty
  1532.     */
  1533.     if(path && *path != '\0')
  1534.     {
  1535.         char *cp = XP_STRRCHR(path, '/');
  1536.         XP_STRCPY(out_buf, "<A HREF=\"");
  1537.         PD_PUTS(out_buf);
  1538.         if(cp)
  1539.         {
  1540.             *cp = '\0';
  1541.             PD_PUTS(path);
  1542.             *cp = '/';
  1543.             XP_STRCPY(out_buf, "/");
  1544.             PD_PUTS(out_buf);
  1545.         }
  1546.         XP_STRNCPY_SAFE(out_buf, 
  1547.                         XP_GetString(XP_UPTO_HIGHER_LEVEL_DIRECTORY), 
  1548.                         sizeof(out_buf));
  1549.         PD_PUTS(out_buf);
  1550.     }
  1551.  
  1552.     for(i=0; i < num_files; i++)
  1553.       {
  1554.  
  1555.         file_entry = NET_HTTPIndexParserGetFileNum(obj->parse_data, i);
  1556.  
  1557.         if(!file_entry)
  1558.             continue;
  1559.  
  1560.         if(!file_entry->date)
  1561.           {
  1562.             PR_snprintf(out_buf, sizeof(out_buf), " <A HREF=\"%s\">",
  1563.                        file_entry->filename);
  1564.             PD_PUTS(out_buf); /* output the line */
  1565.  
  1566.             PR_snprintf(out_buf, sizeof(out_buf), "%s</A>\n",
  1567.                        NET_UnEscape(file_entry->filename));
  1568.             PD_PUTS(out_buf); /* output the line */
  1569.           }
  1570.         else
  1571.           {
  1572.             int max_size_for_this_name;
  1573.  
  1574.             date = ctime(&file_entry->date);
  1575.             if(date)
  1576.                 date[24] = '\0';  /* strip return */
  1577.             else
  1578.                 date = "";
  1579.     
  1580.             PR_snprintf(out_buf, sizeof(out_buf), " <A HREF=\"%s\">", 
  1581.                        file_entry->filename);
  1582.                PD_PUTS(out_buf); /* output the line */
  1583.     
  1584.             /* do content_type icon stuff here */
  1585.             cinfo = NET_cinfo_find_info_by_type(file_entry->content_type);
  1586.  
  1587.             if(file_entry->special_type == NET_DIRECTORY 
  1588.                     || file_entry->special_type == NET_SYM_LINK
  1589.                     || file_entry->special_type == NET_SYM_LINK_TO_DIR
  1590.                     || file_entry->special_type == NET_SYM_LINK_TO_FILE)
  1591.               {
  1592.                 PR_snprintf(out_buf, 
  1593.                             sizeof(out_buf), 
  1594.                             "<IMG ALIGN=absbottom "
  1595.                             "BORDER=0 SRC=\"internal-gopher-menu\">");
  1596.               }
  1597.             else if(cinfo && cinfo->icon)
  1598.               {
  1599.                 PR_snprintf(out_buf, sizeof(out_buf),
  1600.                            "<IMG ALIGN=absbottom BORDER=0 SRC=\"%.512s\">",
  1601.                            cinfo->icon);
  1602.               }
  1603.             else 
  1604.               {
  1605.                 PR_snprintf(out_buf, 
  1606.                             sizeof(out_buf), 
  1607.                             "<IMG ALIGN=absbottom BORDER=0 "
  1608.                                "SRC=\"internal-gopher-unknown\">");
  1609.               }
  1610.  
  1611.                PD_PUTS(out_buf); /* output the line */
  1612.     
  1613. #define SIZE_LISTING_OVERHEAD 9
  1614.  
  1615.             /* print the filename
  1616.              */
  1617.             name = NET_UnEscape (file_entry->filename);
  1618.             len = XP_STRLEN (name);
  1619.             max_size_for_this_name = max_name_length;
  1620.             if(!file_entry->size)
  1621.                 max_size_for_this_name += SIZE_LISTING_OVERHEAD;
  1622.  
  1623.             if(len > max_size_for_this_name)
  1624.               {
  1625.                   XP_STRCPY (out_buf, " ");
  1626.                   XP_MEMCPY (out_buf + 1, name, max_name_length - 3);
  1627.                   XP_STRCPY (out_buf + 1 + max_name_length - 3, "...</A>");
  1628.                 }
  1629.               else
  1630.                 {
  1631.                   XP_STRCPY (out_buf, " ");
  1632.                   XP_STRCAT (out_buf, name);
  1633.                   XP_STRCAT (out_buf, "</A>");
  1634.                 }
  1635.                PD_PUTS(out_buf); /* output the line */
  1636.             
  1637.             /* put a standard number of spaces between the name and date
  1638.              */
  1639.             for(ptr = out_buf; len < max_size_for_this_name; len++)
  1640.               {
  1641.                 *ptr++ = ' ';
  1642.               }
  1643.             *ptr = '\0';
  1644.  
  1645.             /* start from the end of out_buf
  1646.              *
  1647.              * note: add 4 to the date to get rid of the day of the week
  1648.              */
  1649.             if(file_entry->size)
  1650.               {
  1651.                  PR_snprintf(&out_buf[XP_STRLEN(out_buf)], 
  1652.                         sizeof(out_buf) - XP_STRLEN(out_buf), 
  1653.                         " %5ld %s %s ",
  1654.                         file_entry->size > 1024 ?
  1655.                             file_entry->size/1024 : file_entry->size,
  1656.                         file_entry->size > 1024 ? "Kb" : " b",
  1657.                         date+4);
  1658.               }
  1659.             else
  1660.               {
  1661.                 PR_snprintf(&out_buf[XP_STRLEN(out_buf)],
  1662.                            sizeof(out_buf) - XP_STRLEN(out_buf), 
  1663.                            " %s ", date+4);
  1664.               }
  1665.  
  1666.             PD_PUTS(out_buf); /* output the line */
  1667.     
  1668.             if(file_entry->special_type == NET_SYM_LINK
  1669.                 || file_entry->special_type == NET_SYM_LINK_TO_DIR
  1670.                 || file_entry->special_type == NET_SYM_LINK_TO_FILE)
  1671.               {
  1672.                 PR_snprintf(out_buf, sizeof(out_buf), "Symbolic link\n");
  1673.               }
  1674.             else if(file_entry->special_type == NET_DIRECTORY)
  1675.               {
  1676.                 PR_snprintf(out_buf, sizeof(out_buf), "Directory\n");
  1677.               }
  1678.             else if(cinfo && cinfo->desc)
  1679.               {
  1680.                 PR_snprintf(out_buf, 
  1681.                             sizeof(out_buf)-1, 
  1682.                             "%s", 
  1683.                             cinfo->desc);
  1684.                 XP_STRCAT(out_buf, "\n");
  1685.               }
  1686.             else
  1687.               {
  1688.                 XP_STRCPY(out_buf, "\n");
  1689.               }
  1690.                 
  1691.             PD_PUTS(out_buf);
  1692.           }
  1693.       }
  1694.  
  1695.     XP_STRCPY(out_buf, "\n</PRE>");
  1696.     PD_PUTS(out_buf);
  1697.  
  1698. cleanup:
  1699.     NET_HTTPIndexParserFree(obj->parse_data);
  1700.     FREEIF(path);
  1701. }
  1702.  
  1703. PRIVATE void net_IdxConvAbort(NET_StreamClass *stream, int32 status)
  1704. {
  1705.     index_format_conv_data_object *obj=stream->data_object;    
  1706.     NET_HTTPIndexParserFree(obj->parse_data);
  1707. }
  1708.  
  1709. PUBLIC NET_StreamClass *
  1710. NET_HTTPIndexFormatToHTMLConverter(int         format_out,
  1711.                                void       *data_object,
  1712.                                URL_Struct *URL_s,
  1713.                                MWContext  *window_id)
  1714. {
  1715.     index_format_conv_data_object* obj;
  1716.     NET_StreamClass* stream;
  1717.  
  1718.     TRACEMSG(("Setting up display stream. Have URL: %s\n", URL_s->address));
  1719.  
  1720.     stream = XP_NEW(NET_StreamClass);
  1721.     if(stream == NULL)
  1722.         return(NULL);
  1723.  
  1724.     obj = XP_NEW(index_format_conv_data_object);
  1725.     if (obj == NULL)
  1726.       {
  1727.         FREE(stream);
  1728.         return(NULL);
  1729.       }
  1730.  
  1731.     XP_MEMSET(obj, 0, sizeof(index_format_conv_data_object));
  1732.  
  1733.     obj->parse_data = NET_HTTPIndexParserInit();
  1734.  
  1735.     if(!obj->parse_data)
  1736.       {
  1737.         FREE(stream);
  1738.         FREE(obj);
  1739.       }
  1740.  
  1741.     obj->context = window_id;
  1742.     obj->URL_s = URL_s;
  1743.     obj->format_out = format_out;
  1744.  
  1745.     stream->name           = "HTTP index format converter";
  1746.     stream->complete       = (MKStreamCompleteFunc) net_IdxConvComplete;
  1747.     stream->abort          = (MKStreamAbortFunc) net_IdxConvAbort;
  1748.     stream->put_block      = (MKStreamWriteFunc) net_IdxConvPut;
  1749.     stream->is_write_ready = (MKStreamWriteReadyFunc) net_IdxConvWriteReady;
  1750.     stream->data_object    = obj;  /* document info object */
  1751.     stream->window_id      = window_id;
  1752.  
  1753.     return(stream);
  1754. }
  1755.  
  1756. #include "libmocha.h"
  1757.  
  1758. #define SANE_BUFLEN    1024
  1759.  
  1760. NET_StreamClass *
  1761. net_CloneWysiwygLocalFile(MWContext *window_id, URL_Struct *URL_s,
  1762.               uint32 nbytes, const char * wysiwyg_url,
  1763.               const char * base_href)
  1764. {
  1765.     char *filename;
  1766.     XP_File fromfp;
  1767.     NET_StreamClass *stream;
  1768.     int32 buflen, len;
  1769.     char *buf;
  1770.  
  1771.     filename = net_return_local_file_part_from_url(URL_s->address);
  1772.     if (!filename)
  1773.         return NULL;
  1774.     fromfp = XP_FileOpen(filename, xpURL, XP_FILE_READ_BIN);
  1775.     FREE(filename);
  1776.     if (!fromfp)
  1777.         return NULL;
  1778.     stream = LM_WysiwygCacheConverter(window_id, URL_s, wysiwyg_url,
  1779.                       base_href);
  1780.     if (!stream)
  1781.       {
  1782.         XP_FileClose(fromfp);
  1783.         return 0;
  1784.       }
  1785.     buflen = stream->is_write_ready(stream);
  1786.     if (buflen > SANE_BUFLEN)
  1787.         buflen = SANE_BUFLEN;
  1788.     buf = (char *)XP_ALLOC(buflen * sizeof(char));
  1789.     if (!buf)
  1790.       {
  1791.         XP_FileClose(fromfp);
  1792.         return 0;
  1793.       }
  1794.     while (nbytes != 0)
  1795.       {
  1796.         len = buflen;
  1797.         if ((uint32)len > nbytes)
  1798.             len = (int32)nbytes;
  1799.         len = XP_FileRead(buf, len, fromfp);
  1800.         if (len <= 0)
  1801.             break;
  1802.         if (stream->put_block(stream, buf, len) < 0)
  1803.             break;
  1804.         nbytes -= len;
  1805.       }
  1806.     FREE(buf);
  1807.     XP_FileClose(fromfp);
  1808.     if (nbytes != 0)
  1809.       {
  1810.         /* NB: Our caller must clear top_state->mocha_write_stream. */
  1811.         stream->abort(stream, MK_UNABLE_TO_CONVERT);
  1812.         XP_DELETE(stream);
  1813.         return 0;
  1814.       }
  1815.     return stream;
  1816. }
  1817.  
  1818. void
  1819. NET_InitFileProtocol(void)
  1820. {
  1821.     static NET_ProtoImpl file_proto_impl;
  1822.  
  1823.     file_proto_impl.init = net_FileLoad;
  1824.     file_proto_impl.process = net_ProcessFile;
  1825.     file_proto_impl.interrupt = net_InterruptFile;
  1826.     file_proto_impl.cleanup = net_CleanupFile;
  1827.  
  1828.     NET_RegisterProtocolImplementation(&file_proto_impl, FILE_TYPE_URL);
  1829.     NET_RegisterProtocolImplementation(&file_proto_impl, FILE_CACHE_TYPE_URL);
  1830. }
  1831.