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

  1. /* -*- Mode: C; tab-width: 8; 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. /* state machine to read FTP sites
  19.  *
  20.  * Designed and originally implemented by Lou Montulli '94
  21.  *
  22.  * State machine to implement File Transfer Protocol,
  23.  * all the protocol state machines work basically the same way.
  24.  */
  25.  
  26. #include "rosetta.h"
  27. #include "mkutils.h"
  28.  
  29. #define FTP_PORT 21
  30.  
  31. #define FTP_MODE_UNKNOWN 0
  32. #define FTP_MODE_ASCII   1
  33. #define FTP_MODE_BINARY  2
  34.  
  35. #include "mkftp.h"  
  36. #include "glhist.h"
  37. #include "mkgeturl.h"
  38. #include "mkparse.h"
  39. #include "mktcp.h"
  40. #include "mksort.h"
  41. #include "mkfsort.h"   /* file sorting */
  42. #include "mkformat.h"
  43. #include "mkfile.h"  
  44. #include "merrors.h"
  45. #include "shist.h"
  46. #include "ssl.h"
  47. #include "prtime.h"
  48. #include "pwcacapi.h"
  49. #include "secnav.h"
  50.  
  51. #ifdef XP_OS2 /* DSR072196 */
  52. #include "os2sock.h"
  53. #endif /* XP_OS2 */
  54.  
  55. #ifdef XP_UNIX
  56. #if !defined(__osf__) && !defined(AIXV3) && !defined(_HPUX_SOURCE) && !defined(__386BSD__) && !defined(__linux) && !defined(SCO_SV)
  57. #include <sys/filio.h>
  58. #endif
  59. #endif /* XP_UNIX */ 
  60.  
  61. #include "xp_error.h"
  62. #include "prefapi.h"
  63.  
  64. /* for XP_GetString() */
  65. #include "xpgetstr.h"
  66. extern int XP_HTML_FTPERROR_NOLOGIN;
  67. extern int XP_HTML_FTPERROR_TRANSFER;
  68. extern int XP_PROGRESS_RECEIVE_FTPDIR;
  69. extern int XP_PROGRESS_RECEIVE_FTPFILE;
  70. extern int XP_PROGRESS_READFILE;
  71. extern int XP_PROGRESS_FILEDONE;
  72. extern int XP_PROMPT_ENTER_PASSWORD;
  73. extern int MK_COULD_NOT_PUT_FILE;
  74. extern int MK_TCP_READ_ERROR;
  75. extern int MK_TCP_WRITE_ERROR;
  76. extern int MK_UNABLE_TO_ACCEPT_SOCKET;
  77. extern int MK_UNABLE_TO_CHANGE_FTP_MODE;
  78. extern int MK_UNABLE_TO_CREATE_SOCKET;
  79. extern int MK_UNABLE_TO_LOCATE_FILE;
  80. extern int MK_UNABLE_TO_OPEN_FILE;
  81. extern int MK_UNABLE_TO_SEND_PORT_COMMAND;
  82. extern int MK_UNABLE_TO_DELETE_DIRECTORY;
  83. extern int MK_COULD_NOT_CREATE_DIRECTORY;
  84. extern int XP_ERRNO_EWOULDBLOCK;
  85. extern int MK_OUT_OF_MEMORY;
  86. extern int MK_DISK_FULL;
  87. extern int XP_COULD_NOT_LOGIN_TO_FTP_SERVER;
  88. extern int XP_ERROR_COULD_NOT_MAKE_CONNECTION_NON_BLOCKING;
  89. extern int XP_POSTING_FILE;
  90. extern int XP_TITLE_DIRECTORY_OF_ETC;
  91. extern int XP_UPTO_HIGHER_LEVEL_DIRECTORY ;
  92.  
  93.  
  94. #define PUTSTRING(s)           (*cd->stream->put_block) \
  95.                                  (cd->stream, s, XP_STRLEN(s))
  96. #define PUTBLOCK(b,l)         (*cd->stream->put_block) \
  97.                                 (cd->stream, b, l)
  98. #define COMPLETE_STREAM   (*cd->stream->complete) \
  99.                                  (cd->stream)
  100. #define ABORT_STREAM(s)   (*cd->stream->abort) \
  101.                                  (cd->stream, s)
  102.  
  103.  
  104. #define FTP_GENERIC_TYPE     0
  105. #define FTP_UNIX_TYPE        1
  106. #define FTP_DCTS_TYPE        2
  107. #define FTP_NCSA_TYPE        3
  108. #define FTP_PETER_LEWIS_TYPE 4
  109. #define FTP_MACHTEN_TYPE     5
  110. #define FTP_CMS_TYPE         6
  111. #define FTP_TCPC_TYPE        7
  112. #define FTP_VMS_TYPE         8
  113. #define FTP_NT_TYPE          9
  114.  
  115. #define OUTPUT_BUFFER_SIZE 2048
  116.  
  117. PRIVATE XP_List * connection_list=0;  /* a pointer to a list of open connections */
  118.  
  119. PRIVATE Bool       net_use_pasv=TRUE;
  120. PRIVATE char *    ftp_last_password=0;
  121. PRIVATE char *    ftp_last_password_host=0;
  122. PRIVATE XP_Bool    net_send_email_address_as_password=FALSE;
  123.  
  124. #define PC_FTP_PASSWORD_KEY "pass"
  125. #define PC_FTP_MODULE_KEY   "ftp"
  126.  
  127. typedef struct _FTPConnection {
  128.     char   *hostname;       /* hostname string (may contain :port) */
  129.     PRFileDesc *csock;          /* control socket */
  130.     int     server_type;    /* type of server */
  131.     int     cur_mode;       /* ascii, binary, unknown */
  132.     Bool use_list;       /* use LIST? (or NLST) */
  133.     Bool busy;           /* is the connection in use already? */
  134.     Bool no_pasv;        /* should we NOT use pasv mode since is doesn't work? */
  135.     Bool prev_cache;     /* did this connection come from the cache? */
  136.     Bool no_restart;     /* do we know that restarts don't work? */
  137. } FTPConnection;
  138.  
  139. /* define the states of the machine 
  140.  */
  141. typedef enum _FTPStates {
  142. FTP_WAIT_FOR_RESPONSE,
  143. FTP_CONTROL_CONNECT,
  144. FTP_CONTROL_CONNECT_WAIT,
  145. FTP_SEND_USERNAME,
  146. FTP_SEND_USERNAME_RESPONSE,
  147. FTP_GET_PASSWORD,
  148. FTP_GET_PASSWORD_RESPONSE,
  149. FTP_SEND_PASSWORD,
  150. FTP_SEND_PASSWORD_RESPONSE,
  151. FTP_SEND_ACCT,
  152. FTP_SEND_ACCT_RESPONSE,
  153. FTP_SEND_REST_ZERO,
  154. FTP_SEND_REST_ZERO_RESPONSE,
  155. FTP_SEND_SYST,
  156. FTP_SEND_SYST_RESPONSE,
  157. FTP_SEND_MAC_BIN,
  158. FTP_SEND_MAC_BIN_RESPONSE,
  159. FTP_SEND_PWD,
  160. FTP_SEND_PWD_RESPONSE,
  161. FTP_SEND_PASV,
  162. FTP_SEND_PASV_RESPONSE,
  163. FTP_PASV_DATA_CONNECT,
  164. FTP_PASV_DATA_CONNECT_WAIT,
  165. FTP_FIGURE_OUT_WHAT_TO_DO,
  166. FTP_SEND_DELETE_FILE,
  167. FTP_SEND_DELETE_FILE_RESPONSE,
  168. FTP_SEND_DELETE_DIR,
  169. FTP_SEND_DELETE_DIR_RESPONSE,
  170. FTP_SEND_MKDIR,
  171. FTP_SEND_MKDIR_RESPONSE,
  172. FTP_SEND_PORT,
  173. FTP_SEND_PORT_RESPONSE,
  174. FTP_GET_FILE_TYPE,
  175. FTP_SEND_BIN,
  176. FTP_SEND_BIN_RESPONSE,
  177. FTP_SEND_ASCII,
  178. FTP_SEND_ASCII_RESPONSE,
  179. FTP_GET_FILE_SIZE,
  180. FTP_GET_FILE_SIZE_RESPONSE,
  181. FTP_GET_FILE_MDTM,
  182. FTP_GET_FILE_MDTM_RESPONSE,
  183. FTP_GET_FILE,
  184. FTP_BEGIN_PUT,
  185. FTP_BEGIN_PUT_RESPONSE,
  186. FTP_DO_PUT,
  187. FTP_SEND_REST,
  188. FTP_SEND_REST_RESPONSE,
  189. FTP_SEND_RETR,
  190. FTP_SEND_RETR_RESPONSE,
  191. FTP_SEND_CWD,
  192. FTP_SEND_CWD_RESPONSE,
  193. FTP_SEND_LIST_OR_NLST,
  194. FTP_SEND_LIST_OR_NLST_RESPONSE,
  195. FTP_SETUP_STREAM,
  196. FTP_WAIT_FOR_ACCEPT,
  197. FTP_START_READ_FILE,
  198. FTP_READ_CACHE,
  199. FTP_READ_FILE,
  200. FTP_START_READ_DIRECTORY,
  201. FTP_READ_DIRECTORY,
  202. FTP_PRINT_DIR,
  203. FTP_DONE,
  204. FTP_ERROR_DONE,
  205. FTP_FREE_DATA
  206. } FTPStates;
  207.  
  208. typedef struct _FTPConData {
  209.     FTPStates     next_state;        /* the next state of the machine */
  210.     Bool          pause_for_read;    /* should we pause (return) for read? */
  211.  
  212.     NET_StreamClass *stream;         /* the outgoing data stream */
  213.  
  214.     FTPConnection   *cc;  /* information about the control connection */
  215.  
  216.     PRFileDesc *dsock;                /* data socket */
  217.     PRFileDesc *listen_sock;          /* socket to listen on */
  218.     Bool    restart;              /* is this a restarted fetch? */
  219.     Bool    use_list;             /* use LIST or NLST */
  220.     Bool    pasv_mode;            /* using pasv mode? */
  221.     Bool    is_directory;         /* is the url a directory? */
  222.     Bool    retr_already_failed;  /* did RETR fail? */
  223.     Bool    store_password;          /* cache password? */
  224.     char   *data_buf;             /* unprocessed data from the socket */
  225.     int32   data_buf_size;        /* size of the unprocesed data */
  226.  
  227.     Bool use_default_path;        /* set if we should use PWD to figure out
  228.                                    * current path and use that 
  229.                                    */
  230.  
  231.     /* used by response */
  232.     FTPStates   next_state_after_response;  /* the next state after 
  233.                                              * full response recieved */
  234.     int         response_code;              /* code returned in response */
  235.     char       *return_msg;                 /* message from response */
  236.     int         cont_response;              /* a continuation code */
  237.  
  238.     char   *data_con_address;     /* address and port for PASV data connection*/
  239.     char   *filename;             /* name of the file we are getting */ 
  240.     char   *file_to_post;         /* local filename to put */
  241.     XP_File file_to_post_fp;      /* filepointer */
  242.     char   *post_file_to;         /* If non-NULL, specifies the destination of
  243.                                      the source file, file_to_post.  If NULL, 
  244.                                      generate the destination name automatically
  245.                                      from file_to_post. */
  246.     int32   total_size_of_files_to_post;   /* total bytes */
  247.     char   *path;                 /* path as returned by MKParse */
  248.     int     type;                 /* a type indicator for the file */
  249.     char   *username;             /* the username if not anonymous */
  250.     char   *password;             /* the password if not anonymous */
  251.  
  252.     char   *cwd_message_buffer;   /* used to print out server help messages */
  253.     char   *login_message_buffer; /* used to print out server help messages */
  254.  
  255.     char   *output_buffer;          /* buffer before sending to PUTSTRING */
  256.  
  257.     SortStruct *sort_base;        /* base pointer to Qsorting list */
  258.  
  259.     Bool destroy_graph_progress;  /* do we need to destroy graph progress? */    
  260.     Bool destroy_file_upload_progress_dialog;  /* do we need to destroy the dialog? */
  261.     Bool calling_netlib_all_the_time;  /* is SetCallNetlibAllTheTime set? */
  262.     int32   original_content_length; /* the content length at the time of
  263.                                       * calling graph progress
  264.                                       */
  265.  
  266.     TCP_ConData *tcp_con_data;     /* Data pointer for tcp connect 
  267.                                     * state machine */
  268.  
  269.     int32   buff_size;             /* used by ftp_send_file */
  270.     int32   amt_of_buff_wrt;       /* amount of data already written
  271.                                     * used by ftp_send_file()
  272.                                     */
  273.     void   *write_post_data_data;
  274.     XP_File partial_cache_fp;      /* pointer to partial cache file */
  275. } FTPConData;
  276.  
  277. /* forward decls */
  278. static const char *pref_email_as_ftp_password = "security.email_as_ftp_password";
  279. PRIVATE int32 net_ProcessFTP(ActiveEntry * ce);
  280.  
  281. /* function prototypes
  282.  */
  283. PRIVATE NET_FileEntryInfo * net_parse_dir_entry (char *entry, int server_type);
  284.  
  285. /* call this to enable/disable FTP PASV mode.
  286.  * Note: PASV mode is on by default
  287.  */
  288. PUBLIC void
  289. NET_UsePASV(Bool do_it)
  290. {
  291.     net_use_pasv = do_it;
  292. }
  293.  
  294. /* callback routine invoked by prefapi when the pref value changes */
  295. /* fix Mac missing prototype warnings */
  296. MODULE_PRIVATE int PR_CALLBACK
  297. net_ftp_pref_changed(const char * newpref, void * data);
  298.  
  299. MODULE_PRIVATE int PR_CALLBACK
  300. net_ftp_pref_changed(const char * newpref, void * data)
  301. {
  302.     PREF_GetBoolPref(pref_email_as_ftp_password, &net_send_email_address_as_password);
  303.     return PREF_NOERROR;
  304. }
  305.  
  306. PRIVATE XP_Bool
  307. net_get_send_email_address_as_password(void)
  308. {
  309. #ifdef XP_WIN
  310.     static XP_Bool    net_read_FTP_password_pref = FALSE;
  311.  
  312.     if (!net_read_FTP_password_pref) {
  313.         PREF_GetBoolPref(pref_email_as_ftp_password, &net_send_email_address_as_password);
  314.         PREF_RegisterCallback(pref_email_as_ftp_password, net_ftp_pref_changed, NULL);
  315.         net_read_FTP_password_pref = TRUE;
  316.     }
  317. #endif
  318.  
  319.     return net_send_email_address_as_password;
  320. }
  321.  
  322. #if defined(XP_MAC) || defined(XP_UNIX)
  323. /* call this with TRUE to enable the sending of the email
  324.  * address as the default user "anonymous" password.
  325.  * The default is OFF.  
  326.  */
  327. PUBLIC void
  328. NET_SendEmailAddressAsFTPPassword(Bool do_it)
  329. {
  330.     net_send_email_address_as_password = (XP_Bool)do_it;
  331. }
  332. #endif
  333.  
  334. PRIVATE NET_StreamClass *
  335. net_ftp_make_stream(FO_Present_Types format_out, 
  336.                     URL_Struct *URL_s, 
  337.                     MWContext *window_id)
  338. {
  339.     MWContext *stream_context;
  340.  
  341. #ifdef MOZILLA_CLIENT
  342.     /* if the context can't handle HTML then we
  343.      * need to generate an HTML dialog to handle
  344.      * the message
  345.      */
  346.     if(URL_s->files_to_post && EDT_IS_EDITOR(window_id))
  347.       {
  348.         Chrome chrome_struct;
  349.         XP_MEMSET(&chrome_struct, 0, sizeof(Chrome));
  350.         chrome_struct.is_modal = TRUE;
  351.         chrome_struct.allow_close = TRUE;
  352.         chrome_struct.allow_resize = TRUE;
  353.         chrome_struct.show_scrollbar = TRUE;
  354.         chrome_struct.w_hint = 400;
  355.         chrome_struct.h_hint = 300;
  356. #ifdef XP_MAC
  357.         /* on Mac, topmost windows are floating windows not dialogs */
  358.         chrome_struct.topmost = FALSE;
  359.         /* disable commands to change to minimal menu bar; */
  360.         /* avoids confusion about which commands are present */
  361.         chrome_struct.disable_commands = TRUE;
  362. #else
  363.         chrome_struct.topmost = TRUE;
  364. #endif
  365.  
  366.         stream_context = FE_MakeNewWindow(window_id,
  367.         NULL,
  368.         NULL,
  369.         &chrome_struct);
  370.         if(!stream_context)
  371.             return (NULL);
  372.       }
  373.     else
  374. #endif /* MOZILLA_CLIENT */
  375.       {
  376.         stream_context = window_id;
  377.       }
  378.  
  379.     return(NET_StreamBuilder(format_out, URL_s, stream_context));
  380.  
  381. }
  382.  
  383. PRIVATE int
  384. net_ftp_show_error(ActiveEntry *ce, char *line)
  385. {
  386.     FTPConData * cd = (FTPConData *)ce->con_data;
  387.  
  388.     /* show the error
  389.      */
  390.     StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);
  391.     ce->URL_s->expires = 1; /* immediate expire date */
  392.     cd->stream = net_ftp_make_stream(ce->format_out, ce->URL_s, ce->window_id);
  393.  
  394.     if(cd->stream)
  395.       {
  396.         XP_STRCPY(cd->output_buffer, XP_GetString(XP_HTML_FTPERROR_TRANSFER));
  397.         if(ce->status > -1)
  398.             ce->status = PUTSTRING(cd->output_buffer);
  399.         if(line && ce->status > -1)
  400.             ce->status = PUTSTRING(cd->return_msg);
  401.         XP_STRCPY(cd->output_buffer, "</PRE>");
  402.         if(ce->status > -1)
  403.             ce->status = PUTSTRING(cd->output_buffer);
  404.       }
  405.       
  406.     cd->next_state = FTP_ERROR_DONE;
  407.     return MK_INTERRUPTED;
  408. }
  409.  
  410. /* get a reply from the server
  411.  */
  412. PRIVATE int 
  413. net_ftp_response (ActiveEntry *ce)
  414. {
  415.     FTPConData * cd = (FTPConData *)ce->con_data;
  416.     int result_code;        /* numerical result code from server */
  417.     int status;
  418.     char cont_char;
  419.     char * line;
  420.  
  421.     status = NET_BufferedReadLine(cd->cc->csock, &line, &cd->data_buf, 
  422.                         &cd->data_buf_size, &cd->pause_for_read);
  423.  
  424.     if(status == 0)
  425.       {
  426.         /* this shouldn't really happen, but...
  427.          */
  428.          cd->next_state = cd->next_state_after_response;
  429.          cd->pause_for_read = FALSE; /* don't pause */
  430.         return(0);
  431.       }
  432.     else if(status < 0)
  433.       {
  434.         int rv = PR_GetError();
  435.  
  436.         if (rv == PR_WOULD_BLOCK_ERROR)
  437.           {
  438.             cd->pause_for_read = TRUE;
  439.             return(0);
  440.           }
  441.  
  442.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, rv);
  443.  
  444.         /* return TCP error
  445.          */
  446.         return MK_TCP_READ_ERROR;
  447.       }
  448.     else if(!line)
  449.       {
  450.          return status; /* wait for line */
  451.       }
  452.  
  453.     TRACEMSG(("    Rx: %s", line));
  454.  
  455.     /* Login messages or CWD messages 
  456.      * Their so handy, those help messages are :)
  457.      */
  458.     if(cd->cc->server_type == FTP_UNIX_TYPE && !XP_STRNCMP(line, "250-",4))
  459.       {
  460.         StrAllocCat(cd->cwd_message_buffer, line+4);
  461.         StrAllocCat(cd->cwd_message_buffer, "\n");  /* nasty */
  462.       }
  463.     else if(!XP_STRNCMP(line,"230-",4) || !XP_STRNCMP(line,"220-",4))
  464.       {
  465.         StrAllocCat(cd->login_message_buffer, line+4);
  466.         StrAllocCat(cd->login_message_buffer, "\n");  /* nasty */
  467.       }
  468.     else if(cd->cont_response == 230)
  469.       {
  470.          /* this does the same thing as the one directly above it
  471.          * but it checks for 230 response code instead
  472.            * this is to fix buggy ftp servers that don't follow specs
  473.          */
  474.         if(XP_STRNCMP(line, "230", 3))
  475.           {
  476.             /* make sure it's not  "230 Guest login ok...
  477.              */
  478.             StrAllocCat(cd->login_message_buffer, line);
  479.             StrAllocCat(cd->login_message_buffer, "\n");  /* nasty */
  480.           }
  481.       }
  482.  
  483.      result_code = 0;          /* defualts */
  484.      cont_char = ' ';  /* defualts */
  485.      sscanf(line, "%d%c", &result_code, &cont_char);
  486.  
  487.      if(cd->cont_response == -1) 
  488.        {
  489.          if (cont_char == '-')  /* start continuation */
  490.              cd->cont_response = result_code;
  491.  
  492.          StrAllocCopy(cd->return_msg, line+4);
  493.        } 
  494.      else 
  495.        {    /* continuing */
  496.            if (cd->cont_response == result_code && cont_char == ' ')
  497.              cd->cont_response = -1;    /* ended */
  498.  
  499.          StrAllocCat(cd->return_msg, "\n");
  500.          if(XP_STRLEN(line) > 3)
  501.            {
  502.              if(isdigit(line[0]))
  503.                  StrAllocCat(cd->return_msg, line+4);
  504.              else
  505.                  StrAllocCat(cd->return_msg, line);
  506.            }
  507.          else
  508.            {
  509.              StrAllocCat(cd->return_msg, line);
  510.            }
  511.        }    
  512.  
  513.      if(cd->cont_response == -1)  /* all done with this response? */
  514.        {
  515.           cd->next_state = cd->next_state_after_response;
  516.           cd->pause_for_read = FALSE; /* don't pause */
  517.        }
  518.      if(result_code == 551)
  519.        {
  520.          return net_ftp_show_error(ce, line + 4);
  521.        }
  522.         
  523.     cd->response_code = result_code/100;
  524.  
  525.     return(1);  /* everything ok */
  526. }
  527.  
  528. PRIVATE int
  529. net_send_username(FTPConData * cd)
  530. {
  531.  
  532.     /* look for the word UNIX in the hello message
  533.      * if it is there assume a unix server that can
  534.      * use the LIST command
  535.      */    
  536.     if(cd->return_msg && strcasestr(cd->return_msg, "UNIX"))
  537.       {
  538.         cd->cc->server_type = FTP_UNIX_TYPE;
  539.         cd->cc->use_list = TRUE;
  540.       }
  541.  
  542.     if (cd->username)
  543.       {
  544.         PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "USER %.256s%c%c", cd->username, CR, LF);
  545.       }
  546.     else
  547.       {
  548.         PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "USER anonymous%c%c", CR, LF);
  549.       }
  550.     
  551.     TRACEMSG(("FTP Rx: %.256s", cd->output_buffer));
  552.  
  553.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  554.     cd->next_state_after_response = FTP_SEND_USERNAME_RESPONSE;
  555.     cd->pause_for_read = TRUE;
  556.  
  557.     return((int) NET_BlockingWrite(cd->cc->csock, 
  558.                              cd->output_buffer, 
  559.                              XP_STRLEN(cd->output_buffer)));
  560. }
  561.  
  562. PRIVATE int
  563. net_send_username_response(ActiveEntry * ce)
  564. {
  565.     FTPConData * cd = (FTPConData *)ce->con_data;
  566.  
  567.     if (cd->response_code == 3)  /* Send password */
  568.       {
  569.         if(!cd->password)
  570.             cd->next_state = FTP_GET_PASSWORD;
  571.         else 
  572.             cd->next_state = FTP_SEND_PASSWORD;
  573.       }
  574.     else if (cd->response_code == 2) /* Logged in */
  575.       {
  576.           if( cd->cc->no_restart )
  577.               cd->next_state = FTP_SEND_SYST;
  578.           else
  579.               cd->next_state = FTP_SEND_REST_ZERO;
  580.       }
  581.     else
  582.       {
  583.         /* show the error
  584.          */
  585.          StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);
  586.         ce->URL_s->expires = 1; /* immediate expire date */
  587.         cd->stream = net_ftp_make_stream(ce->format_out, ce->URL_s, ce->window_id);
  588.  
  589.         if(cd->stream)
  590.           {
  591.             PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, XP_GetString(XP_HTML_FTPERROR_NOLOGIN)) ;
  592.             if(ce->status > -1)
  593.                 ce->status = PUTSTRING(cd->output_buffer);
  594.             if(cd->return_msg && ce->status > -1)
  595.                 ce->status = PUTSTRING(cd->return_msg);
  596.             PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "</PRE>");
  597.             if(ce->status > -1)
  598.                 ce->status = PUTSTRING(cd->output_buffer);
  599.  
  600.             if(ce->status > -1)
  601.                    COMPLETE_STREAM;
  602.             else
  603.                 ABORT_STREAM(ce->status);
  604.  
  605.             FREE(cd->stream);
  606.             cd->stream = NULL;
  607.           }
  608.         
  609.         cd->next_state = FTP_ERROR_DONE;
  610.  
  611.         /* make sure error_msg is empty */
  612.         FREE_AND_CLEAR(ce->URL_s->error_msg);
  613.  
  614.         return MK_UNABLE_TO_LOGIN;
  615.  
  616.       }
  617.  
  618.     return(0); /* good */
  619. }
  620.  
  621.  
  622. PRIVATE int
  623. net_send_password(FTPConData * cd)
  624. {
  625.  
  626.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, 
  627.                 "PASS %.256s%c%c", 
  628.                 NET_UnEscape(cd->password), 
  629.                 CR, LF);
  630.  
  631.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  632.     cd->next_state_after_response = FTP_SEND_PASSWORD_RESPONSE;
  633.     cd->pause_for_read = TRUE;
  634.  
  635.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  636.  
  637.     return((int) NET_BlockingWrite(cd->cc->csock, 
  638.                              cd->output_buffer, 
  639.                              XP_STRLEN(cd->output_buffer)));
  640. }
  641.  
  642. PRIVATE char *
  643. gen_ftp_password_key(char *address, char *username)
  644. {
  645.     char *rv=NULL;
  646.  
  647.     if(!address || !username)
  648.         return NULL;
  649.  
  650.     StrAllocCopy(rv, address);
  651.     StrAllocCat(rv, "\t");
  652.     StrAllocCat(rv, username);
  653.  
  654.     return rv;
  655. }
  656.  
  657. PRIVATE int
  658. net_send_password_response(ActiveEntry * ce)
  659. {
  660.     FTPConData * cd = (FTPConData *)ce->con_data;
  661.  
  662.     if (cd->response_code == 3)
  663.       {
  664.         cd->next_state = FTP_SEND_ACCT;
  665.       }
  666.     else if (cd->response_code == 2) /* logged in */
  667.       {
  668.           if(cd->store_password && cd->username)
  669.           {
  670.             /* store the password in the cache */
  671.             char *key = gen_ftp_password_key(ce->URL_s->address, cd->username);    
  672.  
  673.             if(key)
  674.             {
  675.                 PCNameValueArray * array = PC_NewNameValueArray();
  676.         
  677.                 PC_AddToNameValueArray(array, PC_FTP_PASSWORD_KEY, cd->password);
  678.                 
  679.                 PC_StorePasswordNameValueArray(PC_FTP_MODULE_KEY, key, array);
  680.  
  681.                 XP_FREE(key);
  682.  
  683.                 PC_FreeNameValueArray(array);
  684.             }
  685.           }
  686.  
  687.           if( cd->cc->no_restart )
  688.               cd->next_state = FTP_SEND_SYST;
  689.           else
  690.               cd->next_state = FTP_SEND_REST_ZERO;
  691.       }
  692.     else
  693.       {
  694.         FREEIF(ftp_last_password_host);
  695.         FREEIF(ftp_last_password);
  696.  
  697.         cd->next_state = FTP_ERROR_DONE;
  698.         FE_Alert(ce->window_id, cd->return_msg ? cd->return_msg :
  699.                                  XP_GetString( XP_COULD_NOT_LOGIN_TO_FTP_SERVER ) );
  700.         /* no error status message in this case 
  701.          */
  702.         return MK_UNABLE_TO_LOGIN;  /* negative return code */
  703.       }
  704.  
  705.     return(0); /* good */
  706. }
  707.     
  708.  
  709. PRIVATE int
  710. net_send_acct(FTPConData * cd)
  711. {
  712.  
  713.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "ACCT noaccount%c%c", CR, LF);
  714.  
  715.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  716.     cd->next_state_after_response = FTP_SEND_ACCT_RESPONSE;
  717.     cd->pause_for_read = TRUE;
  718.  
  719.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  720.  
  721.     return((int) NET_BlockingWrite(cd->cc->csock, 
  722.                              cd->output_buffer, 
  723.                              XP_STRLEN(cd->output_buffer)));
  724. }
  725.  
  726. PRIVATE int
  727. net_send_acct_response(ActiveEntry * ce)
  728. {
  729.     FTPConData * cd = (FTPConData *)ce->con_data;
  730.  
  731.     if (cd->response_code == 2)
  732.       {
  733.           if( cd->cc->no_restart )
  734.               cd->next_state = FTP_SEND_SYST;
  735.           else
  736.               cd->next_state = FTP_SEND_REST_ZERO;
  737.       }
  738.     else
  739.       {
  740.         cd->next_state = FTP_ERROR_DONE;
  741.         FE_Alert(ce->window_id, cd->return_msg ? cd->return_msg : 
  742.                                  XP_GetString( XP_COULD_NOT_LOGIN_TO_FTP_SERVER ) );
  743.         /* no error status message in this case 
  744.          */
  745.         return MK_UNABLE_TO_LOGIN;  /* negative return code */
  746.       }
  747.  
  748.     return(0); /* good */
  749. }
  750.  
  751. PRIVATE int
  752. net_send_rest_zero(ActiveEntry * ce)
  753. {
  754.     FTPConData * cd = (FTPConData *)ce->con_data;
  755.     
  756.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "REST 0%c%c", CR, LF);
  757.  
  758.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  759.     cd->next_state_after_response = FTP_SEND_REST_ZERO_RESPONSE;
  760.     cd->pause_for_read = TRUE;
  761.  
  762.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  763.  
  764.     return((int) NET_BlockingWrite(cd->cc->csock, 
  765.                              cd->output_buffer, 
  766.                              XP_STRLEN(cd->output_buffer)));
  767. }
  768.  
  769. PRIVATE int
  770. net_send_rest_zero_response(ActiveEntry * ce)
  771. {
  772.     FTPConData * cd = (FTPConData *)ce->con_data;
  773.  
  774.     if( cd->response_code == 3 )
  775.     {
  776.         TRACEMSG(("Server can do restarts!"));
  777.         ce->URL_s->server_can_do_restart = TRUE;
  778.         cd->cc->no_restart = FALSE;
  779.     }
  780.     else
  781.     {
  782.         TRACEMSG(("Server can NOT do restarts!"));
  783.         ce->URL_s->server_can_do_restart = FALSE;
  784.         cd->cc->no_restart = TRUE;
  785.     }
  786.  
  787.     cd->next_state = FTP_SEND_SYST;
  788.     return 0;
  789. }
  790.  
  791. PRIVATE int
  792. net_send_syst(FTPConData * cd)
  793. {
  794.     
  795.  
  796.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "SYST%c%c", CR, LF);
  797.  
  798.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  799.     cd->next_state_after_response = FTP_SEND_SYST_RESPONSE;
  800.     cd->pause_for_read = TRUE;
  801.  
  802.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  803.  
  804.     return((int) NET_BlockingWrite(cd->cc->csock, 
  805.                              cd->output_buffer, 
  806.                              XP_STRLEN(cd->output_buffer)));
  807. }
  808.  
  809.  
  810. PRIVATE int
  811. net_send_syst_response(FTPConData * cd)
  812. {
  813.     if (cd->response_code == 2)
  814.       {
  815.         TRACEMSG(("SYST response: %s",cd->return_msg));
  816.  
  817.         /* default next state will be to try PASV mode
  818.          *
  819.          * TEST: set this default next state to FTP_SEND_PORT
  820.          * to test the non-pasv mode ftp
  821.          */
  822.         if(cd->use_default_path)
  823.             cd->next_state = FTP_SEND_PWD;
  824.         else
  825.             cd->next_state = FTP_FIGURE_OUT_WHAT_TO_DO;
  826.  
  827.                 /* we got a line -- what kind of server are we talking to? */
  828.         if (!XP_STRNCMP(cd->return_msg, "UNIX Type: L8 MAC-OS MachTen", 28))
  829.           {
  830.             cd->cc->server_type = FTP_MACHTEN_TYPE;
  831.             cd->cc->use_list = TRUE;
  832.           }
  833.         else if (XP_STRSTR(cd->return_msg, "UNIX") != NULL)
  834.           {
  835.              cd->cc->server_type = FTP_UNIX_TYPE;
  836.              cd->cc->use_list = TRUE;
  837.           }
  838.         else if (XP_STRSTR(cd->return_msg, "Windows_NT") != NULL)
  839.           {
  840.              cd->cc->server_type = FTP_NT_TYPE;
  841.              cd->cc->use_list = TRUE;
  842.           }
  843.         else if (XP_STRNCMP(cd->return_msg, "VMS", 3) == 0)
  844.           {
  845.              cd->cc->server_type = FTP_VMS_TYPE;
  846.              cd->cc->use_list = TRUE;
  847.           }
  848.         else if ((XP_STRNCMP(cd->return_msg, "VM/CMS", 6) == 0)
  849.                                  || (XP_STRNCMP(cd->return_msg, "VM ", 3) == 0))
  850.           {
  851.              cd->cc->server_type = FTP_CMS_TYPE;
  852.           }
  853.         else if (XP_STRNCMP(cd->return_msg, "DCTS", 4) == 0)
  854.           {
  855.              cd->cc->server_type = FTP_DCTS_TYPE;
  856.           }
  857.         else if (XP_STRSTR(cd->return_msg, "MAC-OS TCP/Connect II") != NULL)
  858.           {
  859.              cd->cc->server_type = FTP_TCPC_TYPE;
  860.               cd->next_state = FTP_SEND_PWD;
  861.           }
  862.         else if (XP_STRNCMP(cd->return_msg, "MACOS Peter's Server", 20) == 0)
  863.           {
  864.              cd->cc->server_type = FTP_PETER_LEWIS_TYPE;
  865.              cd->cc->use_list = TRUE;
  866.              cd->next_state = FTP_SEND_MAC_BIN; 
  867.           }
  868.         else if(cd->cc->server_type == FTP_GENERIC_TYPE)
  869.           {
  870.               cd->next_state = FTP_SEND_PWD; 
  871.           }
  872.       }
  873.     else
  874.       {
  875.         cd->next_state = FTP_SEND_PWD;
  876.       }
  877.  
  878.     TRACEMSG(("Set server type to: %d\n",cd->cc->server_type));
  879.  
  880.     return(0);  /* good */
  881.  
  882.  
  883. PRIVATE int
  884. net_send_mac_bin(FTPConData * cd)
  885. {
  886.  
  887.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "MACB%c%c", CR, LF);
  888.  
  889.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  890.     cd->next_state_after_response = FTP_SEND_MAC_BIN_RESPONSE;
  891.     cd->pause_for_read = TRUE;
  892.  
  893.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  894.  
  895.     return((int) NET_BlockingWrite(cd->cc->csock, 
  896.                              cd->output_buffer, 
  897.                              XP_STRLEN(cd->output_buffer)));
  898. }
  899.  
  900.  
  901. PRIVATE int
  902. net_send_mac_bin_response(FTPConData * cd)
  903. {
  904.     if(cd->response_code == 2)
  905.       {  /* succesfully set mac binary */
  906.         if(cd->cc->server_type == FTP_UNIX_TYPE)
  907.             cd->cc->server_type = FTP_NCSA_TYPE;  /* we were unsure here */
  908.       }
  909.  
  910.     cd->next_state = FTP_FIGURE_OUT_WHAT_TO_DO;
  911.     
  912.     return(0); /* continue */
  913. }
  914.  
  915. PRIVATE
  916. int
  917. net_figure_out_what_to_do(ActiveEntry *ce)
  918. {
  919.     FTPConData * cd = (FTPConData *)ce->con_data;
  920.  
  921.     switch(ce->URL_s->method)
  922.       {
  923.         case URL_MKDIR_METHOD:
  924.             cd->next_state = FTP_SEND_MKDIR;
  925.             break;
  926.  
  927.         case URL_DELETE_METHOD:
  928.             cd->next_state = FTP_SEND_DELETE_FILE;
  929.             break;
  930.  
  931.         case URL_PUT_METHOD:
  932.             /* trim the path to the last slash */
  933.             {
  934.                 char * slash = XP_STRRCHR(cd->path, '/');
  935.                 if(slash)
  936.                     *slash = '\0';
  937.             }
  938.             /* fall through */
  939.             goto get_method_case;
  940.                     
  941.         case URL_POST_METHOD:
  942.             /* we support POST if files_to_post
  943.              * is filed in
  944.              */
  945.             XP_ASSERT(ce->URL_s->files_to_post);
  946.             /* fall through */
  947.  
  948.         case URL_GET_METHOD:
  949. get_method_case:  /* ack! goto alert */
  950.  
  951.             /* don't send PASV if the server cant do it */
  952.             if(cd->cc->no_pasv || !net_use_pasv)
  953.                 cd->next_state = FTP_SEND_PORT;
  954.             else
  955.                 cd->next_state = FTP_SEND_PASV;
  956.             break;
  957.  
  958.         default:
  959.             XP_ASSERT(0);
  960.       }
  961.  
  962.     return(0);
  963. }
  964.  
  965. PRIVATE int
  966. net_send_mkdir(ActiveEntry *ce)
  967. {
  968.     FTPConData * cd = (FTPConData *)ce->con_data;
  969.  
  970.     PR_snprintf(cd->output_buffer, 
  971.                 OUTPUT_BUFFER_SIZE, 
  972.                 "MKD %s"CRLF, 
  973.                 cd->path);
  974.  
  975.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  976.     cd->next_state_after_response = FTP_SEND_MKDIR_RESPONSE;
  977.     cd->pause_for_read = TRUE;
  978.  
  979.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  980.  
  981.     return((int) NET_BlockingWrite(cd->cc->csock,
  982.                              cd->output_buffer,
  983.                              XP_STRLEN(cd->output_buffer)));
  984. }
  985.  
  986. PRIVATE int
  987. net_send_mkdir_response(ActiveEntry *ce)
  988. {
  989.     FTPConData * cd = (FTPConData *)ce->con_data;
  990.  
  991.     if (cd->response_code !=2) 
  992.       {
  993.         /* error */
  994.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  995.                                                 MK_COULD_NOT_CREATE_DIRECTORY,
  996.                                                 cd->path);
  997.         return(MK_COULD_NOT_CREATE_DIRECTORY);
  998.       }
  999.     
  1000.     /* success */
  1001.  
  1002.     cd->next_state = FTP_DONE;
  1003.  
  1004.     return(0);
  1005. }
  1006.  
  1007.  
  1008. PRIVATE int
  1009. net_send_delete_file(ActiveEntry *ce)
  1010. {
  1011.     FTPConData * cd = (FTPConData *)ce->con_data;
  1012.  
  1013.     PR_snprintf(cd->output_buffer, 
  1014.                 OUTPUT_BUFFER_SIZE, 
  1015.                 "DELE %s"CRLF, 
  1016.                 cd->path);
  1017.  
  1018.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1019.     cd->next_state_after_response = FTP_SEND_DELETE_FILE_RESPONSE;
  1020.     cd->pause_for_read = TRUE;
  1021.  
  1022.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1023.  
  1024.     return((int) NET_BlockingWrite(cd->cc->csock,
  1025.                              cd->output_buffer,
  1026.                              XP_STRLEN(cd->output_buffer)));
  1027. }
  1028.  
  1029. PRIVATE int
  1030. net_send_delete_file_response(ActiveEntry *ce)
  1031. {
  1032.     FTPConData * cd = (FTPConData *)ce->con_data;
  1033.  
  1034.     if (cd->response_code !=2) 
  1035.       {
  1036.         /* error */
  1037.         /* try and delete it as a file */
  1038.         cd->next_state = FTP_SEND_DELETE_DIR;
  1039.         return(0);
  1040.       }
  1041.     
  1042.     /* success */
  1043.  
  1044.     cd->next_state = FTP_DONE;
  1045.  
  1046.     return(0);
  1047. }
  1048.  
  1049. PRIVATE int
  1050. net_send_delete_dir(ActiveEntry *ce)
  1051. {
  1052.     FTPConData * cd = (FTPConData *)ce->con_data;
  1053.  
  1054.     PR_snprintf(cd->output_buffer, 
  1055.                 OUTPUT_BUFFER_SIZE, 
  1056.                 "RMD %s"CRLF, 
  1057.                 cd->path);
  1058.  
  1059.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1060.     cd->next_state_after_response = FTP_SEND_DELETE_DIR_RESPONSE;
  1061.     cd->pause_for_read = TRUE;
  1062.  
  1063.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1064.  
  1065.     return((int) NET_BlockingWrite(cd->cc->csock,
  1066.                              cd->output_buffer,
  1067.                              XP_STRLEN(cd->output_buffer)));
  1068. }
  1069.  
  1070. PRIVATE int
  1071. net_send_delete_dir_response(ActiveEntry *ce)
  1072. {
  1073.     FTPConData * cd = (FTPConData *)ce->con_data;
  1074.  
  1075.     if (cd->response_code !=2) 
  1076.       {
  1077.         /* error */
  1078.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  1079.                                                 MK_UNABLE_TO_DELETE_DIRECTORY,
  1080.                                                 cd->path);
  1081.         return(MK_UNABLE_TO_DELETE_DIRECTORY);
  1082.       }
  1083.     
  1084.     /* success */
  1085.  
  1086.     cd->next_state = FTP_DONE;
  1087.  
  1088.     return(0);
  1089. }
  1090.  
  1091. PRIVATE int
  1092. net_send_pwd(FTPConData * cd)
  1093. {
  1094.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "PWD%c%c", CR, LF);
  1095.  
  1096.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1097.     cd->next_state_after_response = FTP_SEND_PWD_RESPONSE;
  1098.     cd->pause_for_read = TRUE;
  1099.  
  1100.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1101.  
  1102.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1103.                              cd->output_buffer, 
  1104.                              XP_STRLEN(cd->output_buffer)));
  1105. }
  1106.  
  1107. PRIVATE int
  1108. net_send_pwd_response(ActiveEntry *ce)
  1109. {
  1110.     FTPConData * cd = (FTPConData *)ce->con_data;
  1111.     char *cp = XP_STRCHR(cd->return_msg+5,'"');
  1112.  
  1113.     if(cp) 
  1114.         *cp = '\0';
  1115.  
  1116.     /* default next state */
  1117.     /* don't send PASV if the server cant do it */
  1118.     cd->next_state = FTP_FIGURE_OUT_WHAT_TO_DO;
  1119.  
  1120.     if (cd->cc->server_type == FTP_TCPC_TYPE)
  1121.       {
  1122.         cd->cc->server_type = (cd->return_msg[1] == '/' ? 
  1123.                                 FTP_NCSA_TYPE : FTP_TCPC_TYPE);
  1124.       }
  1125.     else if(cd->cc->server_type == FTP_GENERIC_TYPE)
  1126.       {
  1127.         if (cd->return_msg[1] == '/')
  1128.           {
  1129.             /* path names beginning with / imply Unix,
  1130.              * right?
  1131.              */
  1132.               cd->cc->server_type = FTP_UNIX_TYPE;
  1133.               cd->cc->use_list = TRUE;
  1134.               cd->next_state = FTP_SEND_MAC_BIN;  /* see if it's a mac */
  1135.            }
  1136.          else if (cd->return_msg[XP_STRLEN(cd->return_msg)-1] == ']')
  1137.            {
  1138.              /* path names ending with ] imply VMS, right? */
  1139.              cd->cc->server_type = FTP_VMS_TYPE;
  1140.              cd->cc->use_list = TRUE;
  1141.            }
  1142.       }
  1143.  
  1144.     /* use the path returned by PWD if use_default_path is set
  1145.      * don't do this for vms hosts
  1146.      */
  1147.     if(cd->use_default_path && cd->cc->server_type != FTP_VMS_TYPE)
  1148.       {
  1149.         char *path = XP_STRTOK(&cd->return_msg[1], "\"");
  1150.         char *ptr;
  1151.         char *existing_path = cd->path;
  1152.  
  1153.         /* NT can return a drive letter.  We can't
  1154.          * deal with a drive letter right now
  1155.          * so we need to just strip the drive letter
  1156.          */
  1157.         if(*path != '/') {
  1158.           ptr = XP_STRCHR(path, '/');
  1159.           /* If that didn't work, look for a backslash (e.g., OS/2) */
  1160.           if( ptr == (char *)0 ) {
  1161.               ptr = XP_STRCHR(path, '\\');
  1162.               /* If that worked, then go flip the slashes so the URL is legal */
  1163.               if( ptr != (char *)0 ) {
  1164.                   char *p;
  1165.                   for( p = ptr; *p != '\0'; p++ ){
  1166.                       if( *p == '\\' ) {
  1167.                           *p = '/';
  1168.                       }
  1169.                   }
  1170.               }
  1171.           }
  1172.         }
  1173.         else
  1174.           ptr = path;
  1175.  
  1176.         if(ptr)
  1177.           {
  1178.             char * new_url;
  1179.  
  1180.             cd->path = NULL;
  1181.             StrAllocCopy(cd->path, ptr);
  1182.  
  1183.             if(existing_path && existing_path[0] != '\0')
  1184.                {
  1185.                   /* if the new path has a / on the
  1186.                  * end remove it since the
  1187.                  * existing path already has
  1188.                  * a slash at the beginning
  1189.                  */
  1190.                 if(cd->path[0] != '\0')
  1191.                   {
  1192.                     char *end = &cd->path[XP_STRLEN(cd->path)-1];
  1193.                     if(*end == '/')
  1194.                         *end = '\0';
  1195.                   }
  1196.                 StrAllocCat(cd->path, existing_path);
  1197.                }
  1198.  
  1199.             /* strip the path part from the URL
  1200.              * and add the new path
  1201.              */
  1202.             new_url = NET_ParseURL(ce->URL_s->address, 
  1203.                                    GET_PROTOCOL_PART | GET_USERNAME_PART | GET_HOST_PART);
  1204.  
  1205.             StrAllocCat(new_url, cd->path);
  1206.             
  1207.             FREE(ce->URL_s->address);
  1208.  
  1209.             ce->URL_s->address = new_url;
  1210.           }
  1211.  
  1212.  
  1213.         /* make sure we don't do this again for some reason */
  1214.         cd->use_default_path = FALSE;
  1215.       }
  1216.  
  1217.      if (cd->cc->server_type == FTP_NCSA_TYPE ||
  1218.                         cd->cc->server_type == FTP_TCPC_TYPE ||
  1219.                         cd->cc->server_type == FTP_PETER_LEWIS_TYPE)
  1220.          cd->next_state = FTP_SEND_MAC_BIN;
  1221.  
  1222.      return(0); /* good */
  1223. }
  1224.  
  1225.  
  1226. PRIVATE int
  1227. net_send_pasv(FTPConData * cd)
  1228. {
  1229.  
  1230.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "PASV%c%c", CR, LF);
  1231.  
  1232.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1233.     cd->next_state_after_response = FTP_SEND_PASV_RESPONSE;
  1234.  
  1235.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1236.  
  1237.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1238.                              cd->output_buffer, 
  1239.                              XP_STRLEN(cd->output_buffer)));
  1240. }
  1241.  
  1242.  
  1243. PRIVATE int
  1244. net_send_pasv_response(FTPConData * cd)
  1245. {
  1246.     int    h0, h1, h2, h3; 
  1247.     int32  p0, p1;
  1248.     int32  pasv_port;
  1249.     char  *cp;
  1250.     int    status;
  1251.  
  1252.     if (cd->response_code !=2) 
  1253.       {
  1254.         cd->next_state = FTP_SEND_PORT; /* default next state */
  1255.         cd->pasv_mode = FALSE;
  1256.         cd->cc->no_pasv = TRUE;  /* pasv doesn't work don't try it again */
  1257.         return(0); /* continue */
  1258.       }
  1259.  
  1260.     cd->cc->no_pasv = FALSE;  /* pasv does work */
  1261.  
  1262.     TRACEMSG(("PASV response: %s\n",cd->return_msg));
  1263.  
  1264.     /* find first comma or end of line */
  1265.     for(cp=cd->return_msg; *cp && *cp != ','; cp++)
  1266.         ; /* null body */
  1267.  
  1268.     /* find the begining of digits */
  1269.     while (--cp > cd->return_msg && '0' <= *cp && *cp <= '9')
  1270.         ; /* null body */   
  1271.  
  1272.     /* go past the '(' when the return is of the
  1273.      * form (128,234,563,356,356)
  1274.      */
  1275.     if(*cp < '0' || *cp > '9')
  1276.         cp++;
  1277.  
  1278.     status = sscanf(cp,
  1279. #ifdef __alpha
  1280.         "%d,%d,%d,%d,%d,%d",
  1281. #else
  1282.         "%d,%d,%d,%d,%ld,%ld",
  1283. #endif
  1284.         &h0, &h1, &h2, &h3, &p0, &p1);
  1285.     if (status<6)
  1286.       {
  1287.         TRACEMSG(("FTP: PASV reply has no inet address!\n"));
  1288.         cd->next_state = FTP_SEND_PORT; /* default next state */
  1289.         cd->pasv_mode = FALSE;
  1290.         return(0); /* continue */
  1291.       }
  1292.  
  1293.     pasv_port = ((int32) (p0<<8)) + p1;
  1294.     TRACEMSG(("FTP: Server is listening on port %d\n", pasv_port));
  1295.  
  1296.     /* Open connection for data:
  1297.      */
  1298.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "%d.%d.%d.%d:%ld",h0,h1,h2,h3,pasv_port);
  1299.     StrAllocCopy(cd->data_con_address, cd->output_buffer);
  1300.  
  1301.     cd->next_state = FTP_PASV_DATA_CONNECT;
  1302.  
  1303.     return(0); /* OK */
  1304. }
  1305.  
  1306.  
  1307. PRIVATE int
  1308. net_send_port(ActiveEntry * ce)
  1309. {
  1310.     FTPConData * cd = (FTPConData *) ce->con_data;
  1311.     int    status;
  1312.     PRNetAddr pr_addr;
  1313.     PRSocketOptionData opt;
  1314.     PRStatus sock_status;
  1315.     
  1316.     TRACEMSG(("Entering get_listen_socket with control socket: %d\n", cd->cc->csock));
  1317.  
  1318.     cd->pasv_mode = FALSE;  /* not using pasv mode if we get here */
  1319.  
  1320.     /*  Create internet socket
  1321.     */
  1322.     cd->listen_sock = PR_NewTCPSocket();
  1323.     
  1324.     if(cd->listen_sock == NULL)
  1325.       {
  1326.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CREATE_SOCKET);
  1327.         return MK_UNABLE_TO_CREATE_SOCKET;
  1328.       }
  1329.  
  1330. #if defined(XP_WIN16) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK))
  1331.     opt.option = PR_SockOpt_Linger;
  1332.     opt.value.linger.polarity = PR_TRUE;
  1333.     opt.value.linger.linger = PR_INTERVAL_NO_WAIT;
  1334.     PR_SetSocketOption(cd->listen_sock, &opt);
  1335. #endif
  1336.       /* lets try to set the socket non blocking
  1337.        * so that we can do multi threading 
  1338.        */
  1339.     opt.option = PR_SockOpt_Nonblocking;
  1340.     opt.value.non_blocking = PR_TRUE;
  1341.     sock_status = PR_SetSocketOption(cd->listen_sock, &opt);
  1342.     if (sock_status != PR_SUCCESS)
  1343.         NET_Progress(ce->window_id, 
  1344.                      XP_GetString( XP_ERROR_COULD_NOT_MAKE_CONNECTION_NON_BLOCKING ) );
  1345.  
  1346. HG31456
  1347.  
  1348.     /* init the addr, leave port zero to let bind allocate one */
  1349.     PR_InitializeNetAddr(PR_IpAddrAny, 0, &pr_addr);
  1350.  
  1351.     status = PR_GetSockName(cd->cc->csock, &pr_addr);
  1352.     if (status != PR_SUCCESS) 
  1353.       {
  1354.         TRACEMSG(("Failure in getsockname for control connection: %d\n", 
  1355.                                 cd->cc->csock));
  1356.         return -1;
  1357.       }
  1358.     
  1359.     status=PR_Bind(cd->listen_sock, &pr_addr);
  1360.     if (status != PR_SUCCESS) 
  1361.     {
  1362.         TRACEMSG(("Failure in bind for listen socket: %d\n", cd->listen_sock));
  1363.            return -1;
  1364.     }
  1365.  
  1366.     /* TRACEMSG(("FTP: This host is %s\n", PR_NetAddrToString(&pr_addr))); */
  1367.     
  1368.     /* get the name again to find out the port */
  1369.     status = PR_GetSockName(cd->listen_sock, &pr_addr);
  1370.     if (status != PR_SUCCESS)   
  1371.         return -1;
  1372.  
  1373.     /* TRACEMSG(("FTP: bound to %s\n",PR_NetAddrToString(&pr_addr))); */
  1374.  
  1375.     /*  Now we must tell the other side where to connect
  1376.      */
  1377.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "PORT %d,%d,%d,%d,%d,%d%c%c",
  1378.             (int)* ((unsigned char*) (&pr_addr.inet.ip)+0),
  1379.             (int)* ((unsigned char*) (&pr_addr.inet.ip)+1),
  1380.             (int)* ((unsigned char*) (&pr_addr.inet.ip)+2),
  1381.             (int)* ((unsigned char*) (&pr_addr.inet.ip)+3),
  1382.             (int)* ((unsigned char*) (&pr_addr.inet.port)+0),
  1383.             (int)* ((unsigned char*) (&pr_addr.inet.port)+1),
  1384.             CR, LF);
  1385.  
  1386.     /*  Inform TCP that we will accept connections
  1387.      */
  1388.     if (PR_Listen(cd->listen_sock, 1) < 0) 
  1389.       {
  1390.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LISTEN_ON_SOCKET);
  1391.         return MK_UNABLE_TO_LISTEN_ON_SOCKET;
  1392.       }
  1393.  
  1394.     TRACEMSG(("TCP: listen socket(), bind() and listen() all OK.\n"));
  1395.  
  1396.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1397.     cd->next_state_after_response = FTP_SEND_PORT_RESPONSE;
  1398.     cd->pause_for_read = TRUE;
  1399.  
  1400.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  1401.  
  1402.     /* Inform the server of the port number we will listen on
  1403.      * The port_command came from above
  1404.      */
  1405.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1406.                              cd->output_buffer, 
  1407.                              XP_STRLEN(cd->output_buffer)));
  1408.  
  1409. } /* get_listen */
  1410.  
  1411. PRIVATE int
  1412. net_send_port_response(ActiveEntry * ce)
  1413. {
  1414.     FTPConData * cd = (FTPConData *) ce->con_data;
  1415.  
  1416.     if (cd->response_code !=2)
  1417.       {
  1418.         TRACEMSG(("FTP: Bad response (port_command: %s)\n",cd->return_msg));
  1419.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_SEND_PORT_COMMAND);
  1420.  
  1421.         return MK_UNABLE_TO_SEND_PORT_COMMAND;
  1422.       }
  1423.  
  1424.     /* else */
  1425.  
  1426.     cd->next_state = FTP_GET_FILE_TYPE;
  1427.     return(0); /* good */
  1428. }
  1429.  
  1430. /* figure out what file transfer type to use
  1431.  * and set the next state to set it if it needs
  1432.  * set or else begin the transfer if it's already
  1433.  * set correctly
  1434.  */
  1435. PRIVATE int
  1436. net_get_ftp_file_type(ActiveEntry *ce)
  1437. {
  1438.     FTPConData * cd = (FTPConData *)ce->con_data;
  1439.  
  1440.     NET_cinfo * cinfo_struct;
  1441.  
  1442.     /* figure out if we are puttting a file
  1443.      * and if we are figure out which
  1444.      * file we are doing now 
  1445.      */
  1446.     if(ce->URL_s->files_to_post &&
  1447.         ce->URL_s->files_to_post[0] != 0)
  1448.       {
  1449.         /* files_to_post is an array of filename pointers
  1450.          * find the last one in the list and
  1451.          * remove it once done.
  1452.          */
  1453.         char **array = ce->URL_s->files_to_post;
  1454.         int i;
  1455.         
  1456.         if(!cd->total_size_of_files_to_post)
  1457.           {
  1458.               /* If we don't know the total size
  1459.              * of all the files we are posting
  1460.              * figure it out now
  1461.              */
  1462.             XP_StatStruct stat_entry; 
  1463.             
  1464.             for(i=0; array[i]; i++)
  1465.               {
  1466.                 if(XP_Stat(array[i], 
  1467.                            &stat_entry, xpFileToPost) != -1)
  1468.                     cd->total_size_of_files_to_post += stat_entry.st_size;
  1469.               }
  1470.  
  1471.             /* start the graph progress now since we know the size
  1472.               */
  1473.             ce->URL_s->content_length = cd->total_size_of_files_to_post; 
  1474.             FE_GraphProgressInit(ce->window_id, ce->URL_s, ce->URL_s->content_length);
  1475.             cd->destroy_graph_progress = TRUE;
  1476.             cd->original_content_length = ce->URL_s->content_length;
  1477.  
  1478. #ifdef EDITOR
  1479.             /* Don't show the dialog if the data is being delivered to a plug-in */
  1480.             if (CLEAR_CACHE_BIT(ce->format_out) != FO_PLUGIN)
  1481.             {
  1482.                 FE_SaveDialogCreate(ce->window_id, i, ED_SAVE_DLG_PUBLISH );
  1483.                 cd->destroy_file_upload_progress_dialog = TRUE;
  1484.             }
  1485. #endif /* EDITOR */
  1486.           }  
  1487.                          
  1488.         /* posting files is started as a POST METHOD
  1489.          * eventually though, after posting the files
  1490.          * we end up with a directory listing which
  1491.          * should be treated as a GET METHOD.  Change
  1492.          * the method now so that the cache doesn't
  1493.          * get confused about the method as it caches
  1494.          * the directory listing at the end
  1495.          */
  1496.         if(ce->URL_s->method == URL_POST_METHOD)
  1497.             ce->URL_s->method = URL_GET_METHOD;
  1498.  
  1499.         for(i=0; array[i] != 0; i++)
  1500.             ; /* null body */
  1501.         
  1502.         if(i < 1)
  1503.           {
  1504.             /* no files left to post
  1505.              * quit
  1506.              */
  1507.             cd->next_state = FTP_DONE;
  1508.             return(0);
  1509.           }
  1510.  
  1511.         FREEIF(cd->file_to_post);
  1512.         cd->file_to_post = array[i-1];
  1513.         array[i-1] = 0;
  1514.  
  1515.     /* Use post_to data if specified in the URL struct. */
  1516.     if (ce->URL_s->post_to && ce->URL_s->post_to[i-1]) {
  1517.       FREEIF(cd->post_file_to);
  1518.       cd->post_file_to = ce->URL_s->post_to[i-1];
  1519.       ce->URL_s->post_to[i-1] = 0;
  1520.     }
  1521.  
  1522.  
  1523.         cinfo_struct = NET_cinfo_find_type(cd->file_to_post);
  1524.  
  1525.         /* use binary mode always except for "text" types
  1526.          * don't use ASCII for default types
  1527.          */
  1528.         if(!cinfo_struct->is_default 
  1529.             && !strncasecomp(cinfo_struct->type, "text",4))
  1530.           {
  1531.             if(cd->cc->cur_mode != FTP_MODE_ASCII)
  1532.                 cd->next_state = FTP_SEND_ASCII;
  1533.             else
  1534.                 cd->next_state = FTP_SEND_CWD;
  1535.           }
  1536.         else
  1537.           {
  1538.             if(cd->cc->cur_mode != FTP_MODE_BINARY)
  1539.                 cd->next_state = FTP_SEND_BIN;
  1540.             else
  1541.                 cd->next_state = FTP_SEND_CWD;
  1542.           }
  1543.  
  1544.         return(0);  /* continue */
  1545.       }
  1546.     else if(ce->URL_s->method == URL_PUT_METHOD)
  1547.       {
  1548.         cd->next_state = FTP_SEND_BIN;
  1549.         return(0);
  1550.       }
  1551.  
  1552.     if(cd->type == FTP_MODE_UNKNOWN)
  1553.       {  /* use extensions to determine the type */
  1554.  
  1555.         NET_cinfo * enc = NET_cinfo_find_enc(cd->filename);
  1556.         cinfo_struct = NET_cinfo_find_type(cd->filename);
  1557.  
  1558.         TRACEMSG(("%s", cinfo_struct->is_default ?
  1559.                             "Default cinfo type found" : ""));
  1560.  
  1561.         TRACEMSG(("Current FTP transfer mode is: %d", cd->cc->cur_mode));
  1562.  
  1563.         /* use binary mode always except for "text" types
  1564.          * don't use ASCII for default types
  1565.          *
  1566.          * if it has an encoding value, transfer as binary
  1567.          */
  1568.         if(!cinfo_struct->is_default 
  1569.             && !strncasecomp(cinfo_struct->type, "text",4)
  1570.             && (!enc || !enc->encoding))
  1571.           {
  1572.             if(cd->cc->cur_mode != FTP_MODE_ASCII)
  1573.                 cd->next_state = FTP_SEND_ASCII;
  1574.             else
  1575.                 cd->next_state = FTP_GET_FILE_SIZE;
  1576.           }
  1577.         else
  1578.           {
  1579.             if(cd->cc->cur_mode != FTP_MODE_BINARY)
  1580.                 cd->next_state = FTP_SEND_BIN;
  1581.             else
  1582.                 cd->next_state = FTP_GET_FILE_SIZE;
  1583.           }
  1584.       }
  1585.     else
  1586.       {  /* use the specified type */
  1587.         if(cd->cc->cur_mode == cd->type)
  1588.           {
  1589.             cd->next_state = FTP_GET_FILE_SIZE;
  1590.           }
  1591.         else
  1592.           {
  1593.             if(cd->type == FTP_MODE_ASCII)
  1594.                 cd->next_state = FTP_SEND_ASCII;
  1595.             else
  1596.                 cd->next_state = FTP_SEND_BIN;
  1597.           }
  1598.       }
  1599.   
  1600.  
  1601.     return(0);  /* continue */
  1602. }
  1603.  
  1604. PRIVATE int
  1605. net_send_bin(FTPConData * cd)
  1606. {
  1607.  
  1608.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "TYPE I%c%c", CR, LF);
  1609.     cd->cc->cur_mode = FTP_MODE_BINARY;
  1610.  
  1611.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1612.     cd->next_state_after_response = FTP_SEND_BIN_RESPONSE;
  1613.     cd->pause_for_read = TRUE;
  1614.  
  1615.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  1616.  
  1617.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1618.                              cd->output_buffer, 
  1619.                              XP_STRLEN(cd->output_buffer)));
  1620. }
  1621.  
  1622.  
  1623. PRIVATE int
  1624. net_send_ascii(FTPConData * cd)
  1625. {
  1626.  
  1627.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "TYPE A%c%c", CR, LF);
  1628.     cd->cc->cur_mode = FTP_MODE_ASCII;
  1629.  
  1630.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1631.     cd->next_state_after_response = FTP_SEND_ASCII_RESPONSE;
  1632.     cd->pause_for_read = TRUE;
  1633.  
  1634.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1635.  
  1636.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1637.                              cd->output_buffer, 
  1638.                              XP_STRLEN(cd->output_buffer)));
  1639. }
  1640.  
  1641. PRIVATE int
  1642. net_send_bin_or_ascii_response(ActiveEntry * ce)
  1643. {
  1644.     FTPConData * cd = (FTPConData *) ce->con_data;
  1645.  
  1646.     if (cd->response_code != 2)
  1647.       {
  1648.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CHANGE_FTP_MODE);
  1649.         return MK_UNABLE_TO_CHANGE_FTP_MODE;
  1650.       }
  1651.  
  1652.     /* else */
  1653.     if(cd->file_to_post || ce->URL_s->method == URL_PUT_METHOD)
  1654.         cd->next_state = FTP_SEND_CWD;
  1655.     else
  1656.         cd->next_state = FTP_GET_FILE_SIZE;
  1657.  
  1658.     return(0); /* good */
  1659. }
  1660.  
  1661. PRIVATE int
  1662. net_get_ftp_file_size(ActiveEntry * ce)
  1663. {
  1664.     FTPConData * cd = (FTPConData *) ce->con_data;
  1665.  
  1666.     if (cd->cc->server_type == FTP_VMS_TYPE) 
  1667.       {
  1668.         /* skip this since I'm not sure how it should work on VMS
  1669.          */
  1670.         cd->next_state = FTP_GET_FILE; /* we can skip the mdtm request, too */
  1671.         return(0);
  1672.       }
  1673.  
  1674.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "SIZE %.1024s%c%c", cd->path, CR, LF);
  1675.  
  1676.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1677.     cd->next_state_after_response = FTP_GET_FILE_SIZE_RESPONSE;
  1678.     cd->pause_for_read = TRUE;
  1679.  
  1680.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1681.  
  1682.     return((int) NET_BlockingWrite(cd->cc->csock,
  1683.                              cd->output_buffer,
  1684.                              XP_STRLEN(cd->output_buffer)));
  1685. }
  1686.  
  1687. PRIVATE int
  1688. net_get_ftp_file_size_response(ActiveEntry * ce)
  1689. {
  1690.     FTPConData * cd = (FTPConData *) ce->con_data;
  1691.  
  1692.     if(cd->response_code == 2 && cd->return_msg)
  1693.       {
  1694.           int32 cl = atol(cd->return_msg);
  1695.         /* The response looks like  "160452"
  1696.          * where "160452" is the size in bytes.
  1697.          */
  1698.  
  1699.           if( cd->restart )
  1700.           {
  1701.               TRACEMSG(("Verifying content length: was %ld, is %ld", ce->URL_s->real_content_length, cl));
  1702.               if( ce->URL_s->real_content_length != cl )
  1703.               {
  1704.                   TRACEMSG(("Doesn't match!  Clearing restart flag."));
  1705.                   cd->restart = FALSE;
  1706.                   ce->URL_s->content_length = cl;
  1707.                   ce->URL_s->real_content_length = cl;
  1708.               }
  1709.               else
  1710.               {
  1711.                   TRACEMSG(("Length didn't change."));
  1712.               }
  1713.           }
  1714.           else
  1715.           {
  1716.               ce->URL_s->content_length = cl;
  1717.               ce->URL_s->real_content_length = cl;
  1718.               TRACEMSG(("Set content length: %ld",  ce->URL_s->content_length));
  1719.           }
  1720.       }
  1721.     else
  1722.       {
  1723.         /* unknown length */
  1724.           ce->URL_s->content_length = 0;
  1725.         ce->URL_s->real_content_length = 0;
  1726.         cd->restart = FALSE;
  1727.       }
  1728.  
  1729.     cd->next_state = FTP_GET_FILE_MDTM;
  1730.     
  1731.     return(0);
  1732. }
  1733.  
  1734. PRIVATE int
  1735. net_get_ftp_file_mdtm(ActiveEntry * ce)
  1736. {
  1737.     FTPConData * cd = (FTPConData *) ce->con_data;
  1738.  
  1739.     if (cd->cc->server_type == FTP_VMS_TYPE) 
  1740.       {
  1741.         /* skip this since I'm not sure how it should work on VMS
  1742.          */
  1743.         cd->next_state = FTP_GET_FILE;
  1744.         return(0);
  1745.       }
  1746.  
  1747.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "MDTM %.1024s%c%c", cd->path, CR, LF);
  1748.  
  1749.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1750.     cd->next_state_after_response = FTP_GET_FILE_MDTM_RESPONSE;
  1751.     cd->pause_for_read = TRUE;
  1752.  
  1753.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  1754.  
  1755.     return((int) NET_BlockingWrite(cd->cc->csock,
  1756.                              cd->output_buffer,
  1757.                              XP_STRLEN(cd->output_buffer)));
  1758. }
  1759.  
  1760. PRIVATE int
  1761. net_get_ftp_file_mdtm_response(ActiveEntry * ce)
  1762. {
  1763.     FTPConData * cd = (FTPConData *) ce->con_data;
  1764.  
  1765.     if(cd->response_code == 2 && cd->return_msg)
  1766.     {
  1767.         /* 
  1768.          * The time is returned in ISO 3307 "Representation
  1769.          * of the Time of Day" format. This format is YYYYMMDDHHmmSS or
  1770.          * YYYYMMDDHHmmSS.xxx, where
  1771.          *         YYYY    is the year
  1772.          *         MM      is the month (01-12)
  1773.          *         DD      is the day of the month (01-31)
  1774.          *         HH      is the hour of the day (00-23)
  1775.          *         mm      is the minute of the hour (00-59)
  1776.          *         SS      is the second of the hour (00-59)
  1777.          *         xxx     if present, is a fractional second and may be any length
  1778.          * Time is expressed in UTC (GMT), not local time.
  1779.          */
  1780. #ifndef NSPR20
  1781.         PRTime ts;
  1782.         int64 t, s2us, st;
  1783. #else
  1784.         PRExplodedTime ts;
  1785.         int64 s2us, st;
  1786.         PRTime t;
  1787. #endif /* NSPR20 */
  1788.         time_t tt;
  1789.  
  1790.         TRACEMSG(("Parsing MDTM \"%.1024s\"", cd->return_msg, CR, LF));
  1791.  
  1792.         ts.tm_year = (cd->return_msg[0] - '0') * 1000 +
  1793.                      (cd->return_msg[1] - '0') *  100 +
  1794.                      (cd->return_msg[2] - '0') *   10 +
  1795.                      (cd->return_msg[3] - '0');
  1796. #ifndef NSPR20
  1797.         ts.tm_mon = ((cd->return_msg[4] - '0') * 10 + (cd->return_msg[5] - '0')) - 1;
  1798. #else
  1799.         ts.tm_month = ((cd->return_msg[4] - '0') * 10 + (cd->return_msg[5] - '0')) - 1;
  1800. #endif /* NSPR20 */
  1801.         ts.tm_mday = (cd->return_msg[6] - '0') * 10 + (cd->return_msg[7] - '0');
  1802.         ts.tm_hour = (cd->return_msg[8] - '0') * 10 + (cd->return_msg[9] - '0');
  1803.         ts.tm_min  = (cd->return_msg[10] - '0') * 10 + (cd->return_msg[11] - '0');
  1804.         ts.tm_sec  = (cd->return_msg[12] - '0') * 10 + (cd->return_msg[13] - '0');
  1805.         ts.tm_usec = 0;
  1806.  
  1807.         /* 
  1808.          * if( '.' == cd->return_msg[14] )
  1809.          * {
  1810.          *     long int i = 1000000;
  1811.          *     char *p = &cd->return_msg[15];
  1812.          *     while( isdigit(*p) )
  1813.          *     {
  1814.          *         ts.tm_usec += (*p - '0') * i;
  1815.          *         i /= 10;
  1816.          *         p++;
  1817.          *     }
  1818.          * }
  1819.          */
  1820.  
  1821. #ifndef NSPR20
  1822.         t = PR_ComputeTime(&ts);
  1823. #else
  1824.         t = PR_ImplodeTime(&ts);
  1825. #endif /* NSPR20 */
  1826.         LL_I2L(s2us, PR_USEC_PER_SEC);
  1827.         LL_DIV(st, t, s2us);
  1828.         LL_L2I(tt, st);
  1829.  
  1830. #ifdef DEBUG
  1831.         {
  1832.             char line[256];
  1833. #ifndef NSPR20
  1834.             PRTime et;
  1835.             int64 t2;
  1836. #else
  1837.             PRExplodedTime et;
  1838.             PRTime t2;
  1839. #endif /* NSPR20 */
  1840.             LL_MUL(t2, st, s2us);
  1841. #ifndef NSPR20
  1842.             PR_ExplodeTime(&et, t2);
  1843. #else
  1844.             PR_ExplodeTime(t2, PR_GMTParameters, &et);
  1845. #endif /* NSPR20 */
  1846.             PR_FormatTimeUSEnglish(line, sizeof(line), "%Y%m%d%H%M%S", &et);
  1847.             TRACEMSG(("Parse check: mdtm is \"%s\"", line));
  1848.         }
  1849. #endif
  1850.  
  1851.         if( cd->restart && (ce->URL_s->last_modified != 0) )
  1852.         {
  1853.             TRACEMSG(("Verifying last modified date: was %ld, is %ld", 
  1854.                       ce->URL_s->last_modified, tt));
  1855.             if( ce->URL_s->last_modified != tt )
  1856.             {
  1857.                 TRACEMSG(("Doesn't match!  Clearing restart flag."));
  1858.                 cd->restart = FALSE;
  1859.                 ce->URL_s->last_modified = tt;
  1860.             }
  1861.             else
  1862.             {
  1863.                 TRACEMSG(("Time of last modification didn't change."));
  1864.             }
  1865.         }
  1866.         else
  1867.         {
  1868.             ce->URL_s->last_modified = tt;
  1869.             TRACEMSG(("set last modified: %ld", ce->URL_s->last_modified));
  1870.         }
  1871.     }
  1872.  
  1873.     cd->next_state = FTP_GET_FILE;
  1874.     
  1875.     return(0);
  1876. }
  1877.  
  1878. PRIVATE int
  1879. net_get_ftp_file(FTPConData * cd)
  1880. {
  1881.     
  1882.     /* if this is a VMS server then we need to 
  1883.      * do some special things to map a UNIX syntax
  1884.      * ftp url to a VMS type file syntax
  1885.      */
  1886.     if (cd->cc->server_type == FTP_VMS_TYPE) 
  1887.       {
  1888.           cd->restart = FALSE;
  1889.         /* If we want the VMS account's top directory speed to it 
  1890.          */
  1891.         if (cd->path[0] == '\0' || (cd->path[0] == '/' && cd->path[1]== '\0')) 
  1892.           {
  1893.             cd->is_directory = YES;
  1894.             cd->next_state = FTP_SEND_LIST_OR_NLST;
  1895.           }
  1896.         else if(!XP_STRCHR(cd->path, '/'))
  1897.           {
  1898.             /* if we want a file out of the top directory skip the PWD
  1899.              */
  1900.             cd->next_state = FTP_SEND_RETR;
  1901.           }
  1902.         else
  1903.           {
  1904.             cd->next_state = FTP_SEND_CWD;
  1905.           }
  1906.     
  1907.       }
  1908.     else
  1909.       {  /* non VMS */
  1910.         /* if we have already done the RETR or
  1911.          * if the path ends with a slash
  1912.          */
  1913.         if(cd->retr_already_failed ||
  1914.             '/' == cd->path[XP_STRLEN(cd->path)-1])
  1915.             cd->next_state = FTP_SEND_CWD;
  1916.         else
  1917.         {
  1918.             if( cd->restart ) cd->next_state = FTP_SEND_REST;
  1919.             else              cd->next_state = FTP_SEND_RETR;
  1920.         }
  1921.       }
  1922.  
  1923.     return(0); /* continue */
  1924. }
  1925.  
  1926. PRIVATE int
  1927. net_send_cwd(FTPConData * cd)
  1928. {
  1929.  
  1930.     if (cd->cc->server_type == FTP_VMS_TYPE) 
  1931.       {
  1932.         char *cp, *cp1;
  1933.         /** Try to go to the appropriate directory and doctor filename **/
  1934.         if ((cp=XP_STRRCHR(cd->path, '/'))!=NULL) 
  1935.           {
  1936.             *cp = '\0';
  1937.             PR_snprintf(cd->output_buffer, 
  1938.                        OUTPUT_BUFFER_SIZE, 
  1939.                        "CWD [%.1024s]" CRLF, 
  1940.                        cd->path);
  1941.             *cp = '/';  /* set it back so it is left unmodified */
  1942.  
  1943.             /* turn slashes into '.' */
  1944.             while ((cp1=XP_STRCHR(cd->output_buffer, '/')) != NULL)
  1945.                 *cp1 = '.';
  1946.           }
  1947.         else
  1948.           {
  1949.             PR_snprintf(cd->output_buffer, 
  1950.                        OUTPUT_BUFFER_SIZE, 
  1951.                        "CWD [.%.1024s]" CRLF, 
  1952.                        cd->path);
  1953.           }
  1954.       } /* if FTP_VMS_TYPE */
  1955.     else
  1956.       {  /* non VMS server */
  1957.         PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "CWD %.1024s" CRLF, cd->path);
  1958.       }
  1959.  
  1960.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  1961.     cd->next_state_after_response = FTP_SEND_CWD_RESPONSE;
  1962.     cd->pause_for_read = TRUE;
  1963.  
  1964.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  1965.  
  1966.     return((int) NET_BlockingWrite(cd->cc->csock, 
  1967.                              cd->output_buffer, 
  1968.                              XP_STRLEN(cd->output_buffer)));
  1969. }
  1970.  
  1971. PRIVATE int
  1972. net_send_cwd_response(ActiveEntry * ce)
  1973. {
  1974.     FTPConData * cd = (FTPConData *) ce->con_data;
  1975.  
  1976.     if (cd->response_code == 2) /* Successed : let's NAME LIST it */
  1977.       {  
  1978.  
  1979.         if(cd->file_to_post)
  1980.           {
  1981.             cd->next_state = FTP_BEGIN_PUT;
  1982.             return(0); /* good */
  1983.           }
  1984.         else if(ce->URL_s->method == URL_PUT_METHOD)
  1985.           {
  1986.             cd->next_state = FTP_BEGIN_PUT;
  1987.             return(0); /* good */
  1988.           }
  1989.  
  1990.         ce->URL_s->is_directory = TRUE;
  1991.         cd->restart = FALSE;
  1992.  
  1993.         if (cd->cc->server_type == FTP_VMS_TYPE)
  1994.           {
  1995.             if(*cd->filename)
  1996.                 cd->next_state = FTP_SEND_RETR;
  1997.             else
  1998.                  cd->next_state = FTP_SEND_LIST_OR_NLST;
  1999.             return(0); /* good */
  2000.           }
  2001.  
  2002.         /* non VMS server */
  2003.         /* we are in the correct directory and we
  2004.          * already failed on the RETR so lets 
  2005.          * LIST or NLST it
  2006.          */
  2007.         cd->next_state = FTP_SEND_LIST_OR_NLST;
  2008.         return(0); /* good */
  2009.       }
  2010.  
  2011.     /* else */
  2012.     ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_FILE, 
  2013.                                                     *cd->path ? cd->path : "/");
  2014.  
  2015.     return MK_UNABLE_TO_LOCATE_FILE;  /* error */
  2016. }
  2017.  
  2018. PRIVATE int
  2019. net_begin_put(ActiveEntry * ce)
  2020. {
  2021.     FTPConData * cd = (FTPConData *) ce->con_data;
  2022.     char *path, *filename;
  2023.  
  2024.     if(cd->file_to_post)
  2025.       {
  2026.        /* Use post_file_to as destination filename, if supplied. */
  2027.        /* All path info in post_file_to is thrown away.  Assumed to
  2028.           be in same directory. */
  2029.        if (cd->post_file_to) {
  2030.             path = XP_STRDUP(cd->post_file_to);
  2031.                if(!path)
  2032.                   return(MK_OUT_OF_MEMORY);
  2033.              filename = XP_STRRCHR(path, '/');
  2034.        }
  2035.        else {
  2036.             path = XP_STRDUP(cd->file_to_post);
  2037.             if(!path)
  2038.                 return(MK_OUT_OF_MEMORY);
  2039. #if defined(XP_WIN) || defined(XP_OS2)               /* IBM - SAH */
  2040.             filename = XP_STRRCHR(path, '\\');
  2041. #else
  2042.             filename = XP_STRRCHR(path, '/');
  2043. #endif
  2044.        }
  2045.       }
  2046.     else
  2047.       {
  2048.         path = NET_ParseURL(ce->URL_s->address, GET_PATH_PART);
  2049.         if(!path)
  2050.             return(MK_OUT_OF_MEMORY);
  2051.         filename = XP_STRRCHR(path, '/');
  2052.       }
  2053.  
  2054.     if(!filename)
  2055.         filename = path;
  2056.     else
  2057.         filename++; /* go past delimiter */
  2058.  
  2059.     PR_snprintf(cd->output_buffer, 
  2060.                OUTPUT_BUFFER_SIZE, 
  2061.                "STOR %.1024s" CRLF, 
  2062.                filename);
  2063.  
  2064. #ifdef EDITOR
  2065.     if(cd->destroy_file_upload_progress_dialog)
  2066.         FE_SaveDialogSetFilename(ce->window_id, filename);
  2067. #endif /* EDITOR */
  2068.  
  2069.     FREE(path);
  2070.  
  2071.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2072.     cd->next_state_after_response = FTP_BEGIN_PUT_RESPONSE;
  2073.     cd->pause_for_read = TRUE;
  2074.  
  2075.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  2076.  
  2077.     return((int) NET_BlockingWrite(cd->cc->csock, 
  2078.                              cd->output_buffer, 
  2079.                              XP_STRLEN(cd->output_buffer)));
  2080. }
  2081.  
  2082.  
  2083. PRIVATE int
  2084. net_begin_put_response(ActiveEntry * ce)
  2085. {
  2086.     FTPConData * cd = (FTPConData *) ce->con_data;
  2087.  
  2088.     if (cd->response_code != 1) 
  2089.       {
  2090.         /* failed */
  2091.         ce->URL_s->error_msg = NET_ExplainErrorDetails(
  2092.                                 MK_COULD_NOT_PUT_FILE, 
  2093.                                 cd->file_to_post ? 
  2094.                                         cd->file_to_post : ce->URL_s->address,
  2095.                                 cd->return_msg ? cd->return_msg : "");
  2096.         return(MK_COULD_NOT_PUT_FILE);
  2097.       }
  2098.  
  2099.     NET_ClearReadSelect(ce->window_id, ce->socket);
  2100.     ce->socket = cd->dsock;
  2101.     ce->con_sock = cd->dsock;
  2102.  
  2103. #if defined(XP_WIN16) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK)) /* stuff not for windows since setsockopt doesnt work */
  2104.     {
  2105.         PRSocketOptionData opt;
  2106.           /* turn the linger off so that we don't
  2107.            * wait but we don't abort either
  2108.            * since aborting will lose quede data
  2109.            */
  2110.         opt.option = PR_SockOpt_Linger;
  2111.         opt.value.linger.polarity = PR_FALSE;
  2112.         opt.value.linger.linger = PR_INTERVAL_NO_WAIT;
  2113.         PR_SetSocketOption(cd->listen_sock, &opt);
  2114.     }
  2115. #endif /* XP_WIN16 */
  2116.  
  2117.     /* set connect select because we are writting
  2118.      * not reading
  2119.      */
  2120.     if(cd->pasv_mode)
  2121.         NET_SetConnectSelect(ce->window_id, ce->socket);
  2122. #ifdef XP_WIN
  2123.     cd->calling_netlib_all_the_time = TRUE;
  2124.     NET_SetCallNetlibAllTheTime(ce->window_id, "mkftp");
  2125. #endif
  2126.  
  2127.  
  2128.   /* Maybe should change string if cd->post_file_to */
  2129.     PR_snprintf(cd->output_buffer,
  2130.                OUTPUT_BUFFER_SIZE,
  2131.                 XP_GetString( XP_POSTING_FILE ),
  2132.                cd->file_to_post);
  2133.  
  2134.     NET_Progress(ce->window_id, cd->output_buffer);
  2135.         
  2136.     if(cd->pasv_mode)
  2137.       {
  2138.         cd->next_state = FTP_DO_PUT;
  2139.       }
  2140.     else
  2141.       { /* non PASV */
  2142.         cd->next_state = FTP_WAIT_FOR_ACCEPT;
  2143.       }
  2144.  
  2145.     return(0);
  2146.  
  2147. }
  2148.  
  2149. /* finally send the file
  2150.  */
  2151. PRIVATE int
  2152. net_do_put(ActiveEntry * ce)
  2153. {
  2154.     FTPConData * cd = (FTPConData *) ce->con_data;
  2155.  
  2156.     /* Default to adding crlf if ftp ASCII mode. */
  2157.     XP_Bool add_crlf = (cd->cc->cur_mode == FTP_MODE_ASCII);
  2158.  
  2159.  
  2160.     if(!cd->write_post_data_data)
  2161.       {
  2162.         /* first time through */
  2163.  
  2164.         /* if file_to_post is filled in then
  2165.          * throw the name into the post data
  2166.          * so that we can use the WritePostData
  2167.          * function
  2168.          */
  2169.         if(cd->file_to_post)
  2170.           {
  2171.             ce->URL_s->post_data = cd->file_to_post;
  2172.             ce->URL_s->post_data_is_file = TRUE;
  2173.           }
  2174.       }
  2175.  
  2176.     /* If the add_crlf field is set in the URL struct, find the
  2177.        entry corresponding to the current file being posted. */
  2178.     if (ce->URL_s->files_to_post && ce->URL_s->add_crlf) {
  2179.       int n = 0;
  2180.       while (ce->URL_s->files_to_post[n]) {
  2181.         n++;
  2182.       }
  2183.       /* n should now point after the last entry in files_to_post,
  2184.          which is the current file being uploaded. */      
  2185.       add_crlf = ce->URL_s->add_crlf[n];
  2186.     }
  2187.  
  2188.     /* returns 0 on done and negative on error
  2189.      * positive if it needs to continue.
  2190.      */
  2191.     ce->status = NET_WritePostData(ce->window_id, ce->URL_s,
  2192.                                   ce->socket,
  2193.                                   &cd->write_post_data_data,
  2194.                                                   add_crlf);
  2195.  
  2196.     cd->pause_for_read = TRUE;
  2197.  
  2198.     if(ce->status == 0)
  2199.       {
  2200.         /* normal done
  2201.          */
  2202.            NET_ClearConnectSelect(ce->window_id, ce->socket);
  2203. #ifdef XP_WIN
  2204.         if(cd->calling_netlib_all_the_time)
  2205.         {
  2206.             cd->calling_netlib_all_the_time = FALSE;
  2207.             NET_ClearCallNetlibAllTheTime(ce->window_id, "mkftp");
  2208.         }
  2209. #endif
  2210.            ce->socket = cd->cc->csock;
  2211.            ce->con_sock = NULL;
  2212.            NET_SetReadSelect(ce->window_id, ce->socket);
  2213.  
  2214.         PR_Close(cd->dsock);
  2215.         cd->dsock = NULL;
  2216.  
  2217.         if(cd->destroy_graph_progress)
  2218.           {
  2219.             FE_GraphProgressDestroy(ce->window_id,     
  2220.                                     ce->URL_s,     
  2221.                                     cd->original_content_length,
  2222.                                     ce->bytes_received);
  2223.             cd->destroy_graph_progress = FALSE;
  2224.           }
  2225.  
  2226.            /* go read the response since we need to
  2227.             * get the "226 sucessful transfer" message
  2228.             * out of the read queue in order to cache
  2229.             * connections properly
  2230.             */
  2231.            cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2232.  
  2233.         if(cd->file_to_post)
  2234.           {
  2235.             ce->URL_s->post_data = 0;
  2236.             ce->URL_s->post_data_is_file = FALSE;
  2237.             FREE_AND_CLEAR(cd->file_to_post);
  2238.           }
  2239.  
  2240.     if (cd->post_file_to)
  2241.       {
  2242.       FREE_AND_CLEAR(cd->post_file_to);
  2243.       }
  2244.  
  2245.         /* after getting the server response start
  2246.          * over again if there are more
  2247.          * files to send or to do a new directory
  2248.          * listing...
  2249.          */
  2250.           cd->next_state_after_response = FTP_DONE;
  2251.  
  2252.  
  2253.         /* if this was the last file, check the history
  2254.          * to see if the window is currently displaying an ftp
  2255.          * listing.  If it is update the listing.  If it isnt
  2256.          * quit.
  2257.          */
  2258.         if(ce->URL_s->files_to_post)
  2259.           {
  2260.             if(ce->URL_s->files_to_post[0])
  2261.               {
  2262.                 /* more files, keep going */
  2263.                 cd->next_state_after_response = FTP_FIGURE_OUT_WHAT_TO_DO;
  2264.               }
  2265.       /* Don't want to show a directory when remote editing a FTP page, bug 50942 */
  2266.             else if (!EDT_IS_EDITOR(ce->window_id))
  2267.                 {
  2268.                 /* no more files, figure out if we should
  2269.                  * show an index
  2270.                  */
  2271. #ifdef MOZILLA_CLIENT
  2272.                   History_entry * his = SHIST_GetCurrent(&ce->window_id->hist);
  2273. #else
  2274.                 History_entry * his = NULL;
  2275. #endif /* MOZILLA_CLIENT */
  2276.  
  2277.                 if(his && !strncasecomp(his->address, "ftp:", 4))
  2278.                     {
  2279.                     /* go get the index of the directory */
  2280.                     cd->next_state_after_response = FTP_FIGURE_OUT_WHAT_TO_DO;
  2281.                     }
  2282.               }
  2283.           }
  2284.       }
  2285.     else if(ce->status > 0)
  2286.       {
  2287.         ce->bytes_received += ce->status;
  2288.         FE_GraphProgress(ce->window_id,
  2289.                          ce->URL_s,
  2290.                          ce->bytes_received,
  2291.                          ce->status,
  2292.                          ce->URL_s->content_length);
  2293.         FE_SetProgressBarPercent(ce->window_id,
  2294.                                  (long)(((double)ce->bytes_received*100) / (double)(uint32)ce->URL_s->content_length) );
  2295.       }
  2296.  
  2297.     return(ce->status);
  2298. }
  2299.  
  2300. PRIVATE int
  2301. net_send_rest(ActiveEntry *ce)
  2302. {
  2303.     FTPConData * cd = (FTPConData *) ce->con_data;
  2304.  
  2305.     PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "REST %ld%c%c", 
  2306.                 ce->URL_s->content_length, CR, LF);
  2307.  
  2308.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2309.     cd->next_state_after_response = FTP_SEND_REST_RESPONSE;
  2310.     cd->pause_for_read = TRUE;
  2311.  
  2312.     TRACEMSG(("FTP Rx: %s", cd->output_buffer));
  2313.  
  2314.     return((int) NET_BlockingWrite(cd->cc->csock,
  2315.                              cd->output_buffer,
  2316.                              XP_STRLEN(cd->output_buffer)));
  2317. }
  2318.  
  2319. PRIVATE int
  2320. net_send_rest_response(ActiveEntry *ce)
  2321. {
  2322.     FTPConData * cd = (FTPConData *) ce->con_data;
  2323.  
  2324.     if(cd->response_code == 3 && cd->return_msg)
  2325.     {
  2326.         TRACEMSG(("Restarting!  Yay!"));
  2327.     }
  2328.     else
  2329.     {
  2330.         TRACEMSG(("Didn't understand the REST command."));
  2331.         cd->cc->no_restart = TRUE;
  2332.         cd->restart = FALSE;
  2333.     }
  2334.  
  2335.     cd->next_state = FTP_SEND_RETR;
  2336.     return(0);
  2337. }
  2338.  
  2339.  
  2340. PRIVATE int
  2341. net_send_retr(FTPConData * cd)
  2342. {
  2343.  
  2344.     if(cd->cc->server_type == FTP_VMS_TYPE)
  2345.         PR_snprintf(cd->output_buffer, 
  2346.                    OUTPUT_BUFFER_SIZE,
  2347.                    "RETR %.1024s" CRLF, 
  2348.                    cd->filename);
  2349.     else
  2350.         PR_snprintf(cd->output_buffer, 
  2351.                     OUTPUT_BUFFER_SIZE, 
  2352.                     "RETR %.1024s" CRLF, 
  2353.                     cd->path);
  2354.  
  2355.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2356.     cd->next_state_after_response = FTP_SEND_RETR_RESPONSE;
  2357.     cd->pause_for_read = TRUE;
  2358.  
  2359.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  2360.  
  2361.     return((int) NET_BlockingWrite(cd->cc->csock, 
  2362.                              cd->output_buffer, 
  2363.                              XP_STRLEN(cd->output_buffer)));
  2364. }
  2365.  
  2366.  
  2367. PRIVATE int
  2368. net_send_retr_response(ActiveEntry * ce)
  2369. {
  2370.     FTPConData * cd = (FTPConData *) ce->con_data;
  2371.  
  2372.     if(cd->response_code != 1) 
  2373.       {
  2374.         if(cd->cc->server_type == FTP_VMS_TYPE) 
  2375.           {
  2376.             ce->URL_s->error_msg = NET_ExplainErrorDetails(
  2377.                                                   MK_UNABLE_TO_LOCATE_FILE, 
  2378.                                                   *cd->path ? cd->path : "/");
  2379.             return(MK_UNABLE_TO_LOCATE_FILE);
  2380.           }
  2381.         else
  2382.           { /* non VMS */
  2383.  
  2384. #ifdef DOUBLE_PASV 
  2385.             /* this will use a new PASV connection
  2386.              * for each command even if the one only
  2387.              * failed and didn't transmit
  2388.              */
  2389.             /* close the old pasv connection */
  2390.             NETCLOSE(cd->dsock);
  2391.             /* invalidate the old pasv connection */
  2392.             cd->dsock = NULL;
  2393.  
  2394.             cd->next_state = FTP_SEND_PASV; /* send it again */
  2395.             cd->retr_already_failed = TRUE;
  2396. #else
  2397.             /* we need to use this to make the mac FTPD work
  2398.              */
  2399.             /* old way */
  2400.             cd->next_state = FTP_SEND_CWD; 
  2401. #endif /* DOUBLE_PASV */
  2402.           }
  2403.       }
  2404.     else
  2405.       {   /* successful RETR */
  2406.         cd->next_state = FTP_SETUP_STREAM;
  2407.       }
  2408.  
  2409.     return(0); /* continue */
  2410. }
  2411.  
  2412. PRIVATE int
  2413. net_send_list_or_nlst(ActiveEntry * ce)
  2414. {
  2415.     FTPConData * cd = (FTPConData *)ce->con_data;
  2416.  
  2417.     if(cd->cc->use_list)
  2418.         XP_STRCPY(cd->output_buffer, "LIST" CRLF);
  2419.     else
  2420.         XP_STRCPY(cd->output_buffer, "NLST" CRLF);
  2421.  
  2422.     cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2423.     cd->next_state_after_response = FTP_SEND_LIST_OR_NLST_RESPONSE;
  2424.     cd->pause_for_read = TRUE;
  2425.  
  2426.     TRACEMSG(("FTP Tx: %s", cd->output_buffer));
  2427.  
  2428.     return((int) NET_BlockingWrite(cd->cc->csock, 
  2429.                              cd->output_buffer, 
  2430.                              XP_STRLEN(cd->output_buffer)));
  2431. }
  2432.  
  2433. PRIVATE int
  2434. net_send_list_or_nlst_response(ActiveEntry * ce)
  2435. {
  2436.     FTPConData * cd = (FTPConData *)ce->con_data;
  2437.  
  2438.     if(cd->response_code == 1)
  2439.       {  /* succesful list or nlst */
  2440.         cd->is_directory = TRUE;
  2441.         cd->restart = FALSE;
  2442.         cd->next_state = FTP_SETUP_STREAM;
  2443.  
  2444.         /* add a slash to the end of the uRL if it doesnt' have one now
  2445.          */
  2446.         if(ce->URL_s->address[XP_STRLEN(ce->URL_s->address)-1] != '/')
  2447.           {
  2448.             /* update the global history before modification
  2449.              */
  2450. #ifdef MOZILLA_CLIENT
  2451.             GH_UpdateGlobalHistory(ce->URL_s);
  2452. #endif /* MOZILLA_CLIENT */
  2453.             StrAllocCat(ce->URL_s->address, "/");
  2454.             ce->URL_s->address_modified = TRUE;
  2455.           }
  2456.  
  2457.         return(0); /* good */
  2458.       }
  2459.   
  2460.     ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_FILE, 
  2461.                                                   *cd->path ? cd->path : "/");
  2462.     return(MK_UNABLE_TO_LOCATE_FILE);
  2463. }
  2464.  
  2465.     
  2466. PRIVATE int
  2467. net_setup_ftp_stream(ActiveEntry * ce)
  2468. {
  2469.     FTPConData * cd = (FTPConData *)ce->con_data;
  2470.  
  2471.     /* set up the data stream now
  2472.      */
  2473.     if(cd->is_directory)
  2474.       {
  2475.          char * pUrl ;
  2476.         int status;
  2477.  
  2478.          status = PREF_CopyCharPref("protocol.mimefor.ftp",&pUrl);
  2479.  
  2480.          if (status >= 0 && pUrl) {
  2481.              StrAllocCopy(ce->URL_s->content_type, pUrl);
  2482.              XP_FREE(pUrl);
  2483.          } else {
  2484.               StrAllocCopy(ce->URL_s->content_type, APPLICATION_HTTP_INDEX);
  2485.          }
  2486.  
  2487.         cd->stream = net_ftp_make_stream(ce->format_out, ce->URL_s, ce->window_id);
  2488.       }
  2489.     else
  2490.       {
  2491.         if(!ce->URL_s->preset_content_type || !ce->URL_s->content_type )
  2492.           {
  2493.             NET_cinfo * cinfo_struct = NET_cinfo_find_type(cd->filename);
  2494.              StrAllocCopy(ce->URL_s->content_type, cinfo_struct->type);
  2495.  
  2496.             cinfo_struct = NET_cinfo_find_enc(cd->filename);
  2497.              StrAllocCopy(ce->URL_s->content_encoding, cinfo_struct->encoding);
  2498.           }
  2499.  
  2500.         cd->stream = net_ftp_make_stream(ce->format_out, ce->URL_s, ce->window_id);
  2501.       }
  2502.  
  2503.     if (!cd->stream)
  2504.       {
  2505.         cd->next_state = FTP_ERROR_DONE;
  2506.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT); 
  2507.         return(MK_UNABLE_TO_CONVERT);
  2508.       }
  2509.  
  2510. HG69136
  2511.  
  2512.     if(cd->pasv_mode)
  2513.       {
  2514.         if(cd->is_directory)
  2515.            cd->next_state = FTP_START_READ_DIRECTORY;
  2516.         else
  2517.            cd->next_state = FTP_START_READ_FILE;
  2518.       }
  2519.     else
  2520.       { /* non PASV */
  2521.         cd->next_state = FTP_WAIT_FOR_ACCEPT;
  2522.       }
  2523.  
  2524.     /* start the graph progress indicator
  2525.      */
  2526.     FE_GraphProgressInit(ce->window_id, ce->URL_s, ce->URL_s->real_content_length);
  2527.     cd->destroy_graph_progress = TRUE;
  2528.     cd->original_content_length = ce->URL_s->real_content_length;
  2529.  
  2530.     return(0); /* continue */
  2531. }
  2532.     
  2533. PRIVATE int
  2534. net_wait_for_ftp_accept(ActiveEntry * ce)
  2535. {
  2536.     FTPConData * cd = (FTPConData *)ce->con_data;
  2537.     PRNetAddr pr_addr;
  2538.  
  2539. /* make this non-blocking sometime */
  2540.  
  2541.     /* Wait for the connection
  2542.      */
  2543. #define ACCEPT_TIMEOUT 20
  2544.     cd->dsock = PR_Accept(cd->listen_sock, &pr_addr, ACCEPT_TIMEOUT);
  2545.    
  2546.     if (cd->dsock == NULL)
  2547.       {
  2548.         if(PR_GetError() == PR_WOULD_BLOCK_ERROR)
  2549.           {
  2550.             cd->pause_for_read = TRUE; /* pause */
  2551.             NET_ClearReadSelect(ce->window_id, ce->socket);
  2552.             ce->socket = cd->listen_sock;
  2553.             NET_SetReadSelect(ce->window_id, ce->socket);
  2554.             return(0);
  2555.           }
  2556.         else
  2557.           {
  2558.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_ACCEPT_SOCKET); 
  2559.             return MK_UNABLE_TO_ACCEPT_SOCKET;
  2560.           }
  2561.       }
  2562.  
  2563.     TRACEMSG(("FTP: Accepted new socket %d\n", cd->dsock));
  2564.  
  2565.     if(cd->file_to_post || ce->URL_s->method == URL_POST_METHOD)
  2566.       {
  2567.         ce->socket = cd->dsock;
  2568.         NET_SetConnectSelect(ce->window_id, ce->socket);
  2569.         cd->next_state = FTP_DO_PUT;
  2570.       }
  2571.     else if (cd->is_directory)
  2572.       {
  2573.         cd->next_state = FTP_START_READ_DIRECTORY;
  2574.       }
  2575.     else
  2576.       {
  2577.         cd->next_state = FTP_START_READ_FILE;
  2578.       }
  2579.  
  2580.     return(0);
  2581. }
  2582.  
  2583. PRIVATE int
  2584. net_ftp_push_partial_cache_file(ActiveEntry * ce)
  2585. {
  2586.     FTPConData * cd = (FTPConData *)ce->con_data;
  2587.  
  2588.     int32 write_ready, status;
  2589.  
  2590.     write_ready = (*cd->stream->is_write_ready)(cd->stream);
  2591.  
  2592.     if( 0 == write_ready )
  2593.       {
  2594.         cd->pause_for_read = TRUE;
  2595.         return(0);  /* wait until we are ready to write */
  2596.       }
  2597.  
  2598.     write_ready = MIN(write_ready, NET_Socket_Buffer_Size);
  2599.  
  2600.     status = XP_FileRead(NET_Socket_Buffer, write_ready, cd->partial_cache_fp);
  2601.  
  2602.     if( status < 0 )
  2603.     {
  2604.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);
  2605.         return MK_TCP_READ_ERROR;
  2606.     }
  2607.     else if( status == 0 )
  2608.     {
  2609.         /* All done, switch over to incoming data */
  2610.         TRACEMSG(("Closing FTP cache file"));
  2611.         NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_FILEDONE));
  2612.         NET_ClearFileReadSelect(ce->window_id, XP_Fileno(cd->partial_cache_fp));
  2613.         XP_FileClose(cd->partial_cache_fp);
  2614.         cd->partial_cache_fp = NULL;
  2615.         ce->socket = cd->dsock;
  2616.         NET_SetReadSelect(ce->window_id, ce->socket);
  2617.         ce->local_file = FALSE;
  2618.         cd->next_state = FTP_READ_FILE;
  2619.         NET_Progress (ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_FTPFILE));
  2620.         return 0;
  2621.     }
  2622.     else
  2623.     {
  2624.         /* write the data */
  2625.         TRACEMSG(("Transferring %ld bytes from cache file.", status));
  2626.         ce->bytes_received += status;
  2627.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->real_content_length);
  2628.         FE_SetProgressBarPercent(ce->window_id, (long)(((double)ce->bytes_received*100) / (double)(uint32)ce->URL_s->real_content_length) );
  2629.         status = (*cd->stream->put_block)(cd->stream, NET_Socket_Buffer, status);
  2630.         cd->pause_for_read = TRUE;
  2631.         return status;
  2632.     }
  2633. }
  2634.  
  2635. PRIVATE int
  2636. net_start_ftp_read_file(ActiveEntry * ce)
  2637. {
  2638.     FTPConData * cd = (FTPConData *)ce->con_data;
  2639.  
  2640.     if( cd->restart )
  2641.     {
  2642.         char *cache_file = ce->URL_s->cache_file;
  2643.         XP_File fp;
  2644.  
  2645.         TRACEMSG(("Opening FTP cache file"));
  2646.  
  2647.         if( (NULL == cache_file) || (NULL == (fp = XP_FileOpen(cache_file, xpCache, XP_FILE_READ_BIN))) )
  2648.         {
  2649.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_OPEN_FILE, cache_file);
  2650.             return MK_UNABLE_TO_OPEN_FILE;
  2651.         }
  2652.  
  2653.         NET_ClearReadSelect(ce->window_id, ce->socket);
  2654.         /* ce->socket = XP_Fileno(fp); */
  2655.         ce->socket = NULL;
  2656.         NET_SetFileReadSelect(ce->window_id, XP_Fileno(fp));
  2657.         ce->local_file = TRUE;
  2658.         cd->next_state = FTP_READ_CACHE;
  2659.         cd->partial_cache_fp = fp;
  2660.         NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_READFILE));
  2661.         return net_ftp_push_partial_cache_file(ce);
  2662.     }
  2663.  
  2664.     /* nothing much to be done */
  2665.     cd->next_state = FTP_READ_FILE;
  2666.     cd->pause_for_read = TRUE;
  2667.  
  2668.     NET_ClearReadSelect(ce->window_id, ce->socket);
  2669.     ce->socket = cd->dsock;
  2670.     NET_SetReadSelect(ce->window_id, ce->socket);
  2671.  
  2672.     NET_Progress (ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_FTPFILE));
  2673.  
  2674.     return(0); /* continue */
  2675. }
  2676.  
  2677. PRIVATE int
  2678. net_ftp_read_file(ActiveEntry * ce) 
  2679.     FTPConData * cd = (FTPConData *)ce->con_data; 
  2680.     int status; 
  2681.     unsigned int write_ready, read_size;
  2682.  
  2683.     /* check to see if the stream is ready for writing
  2684.      */
  2685.     write_ready = (*cd->stream->is_write_ready)(cd->stream);
  2686.  
  2687.     if(!write_ready)
  2688.       {
  2689.         cd->pause_for_read = TRUE;
  2690.         return(0);  /* wait until we are ready to write */
  2691.       }
  2692.     else if(write_ready < (unsigned int) NET_Socket_Buffer_Size)
  2693.       {
  2694.         read_size = write_ready;
  2695.       }
  2696.     else
  2697.       {
  2698.         read_size = NET_Socket_Buffer_Size;
  2699.       }
  2700.  
  2701.     status = PR_Read(cd->dsock, NET_Socket_Buffer, read_size); 
  2702.  
  2703.     if(status > 0) 
  2704.       { 
  2705.         ce->bytes_received += status; 
  2706.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, status, ce->URL_s->real_content_length);
  2707.         if (ce->URL_s->real_content_length == 0)
  2708.             FE_SetProgressBarPercent(ce->window_id, 0);
  2709.         else
  2710.             FE_SetProgressBarPercent(ce->window_id, (long)(((double)ce->bytes_received*100) / (double)(uint32)ce->URL_s->real_content_length) );
  2711.         status = PUTBLOCK(NET_Socket_Buffer, status);
  2712.         cd->pause_for_read = TRUE;
  2713.       }
  2714.     else if(status == 0)
  2715.       {
  2716.         /* go read the response since we need to
  2717.          * get the "226 sucessful transfer" message
  2718.          * out of the read queue in order to cache
  2719.          * connections properly
  2720.          */
  2721.         cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2722.         cd->next_state_after_response = FTP_DONE;
  2723.  
  2724.         if(cd->dsock != NULL)
  2725.           {
  2726.             NET_ClearReadSelect(ce->window_id, cd->dsock);
  2727.             PR_Close(cd->dsock);
  2728.             cd->dsock = NULL;
  2729.           }
  2730.  
  2731.         /* set select on the control socket */
  2732.         ce->socket = cd->cc->csock;
  2733.         NET_SetReadSelect(ce->window_id, ce->socket);
  2734.         /* try and read the response immediately,
  2735.          * so don't pause for read
  2736.          *
  2737.          * cd->pause_for_read = TRUE;
  2738.          */
  2739.  
  2740.         return(MK_DATA_LOADED);
  2741.       }
  2742.     else
  2743.       {
  2744.         /* status less than zero
  2745.          */
  2746.           int rv = PR_GetError();
  2747.  
  2748.         if (rv == PR_WOULD_BLOCK_ERROR)
  2749.           {
  2750.             cd->pause_for_read = TRUE;
  2751.             return 0;
  2752.           }
  2753.           status = (rv < 0) ? rv : (-rv);
  2754.       }
  2755.  
  2756.    return(status);
  2757. }
  2758.  
  2759. PRIVATE int
  2760. net_start_ftp_read_dir(ActiveEntry * ce)
  2761. {
  2762.     FTPConData * cd = (FTPConData *)ce->con_data;
  2763.     char buf[512];
  2764.     char *line;
  2765.  
  2766.     cd->sort_base = NET_SortInit();
  2767.  
  2768.     NET_Progress (ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_FTPDIR));
  2769.  
  2770.     if (*cd->path != '\0')  /* Not Empty path: */
  2771.       {
  2772.         int end;
  2773.  
  2774.         end = XP_STRLEN(cd->path)-1;
  2775.         /* if the path ends with a slash kill it.
  2776.          * that includes the path "/"
  2777.          */
  2778.         if(cd->path[end] == '/')
  2779.           {
  2780.              cd->path[end] = 0; /* kill the last slash */
  2781.           }
  2782.       }
  2783.  
  2784.     if(cd->login_message_buffer) /* Don't check !(*cd->path) here or this
  2785.                                     message never shows up.  It's supposed to
  2786.                                     be shown the first time you connect to
  2787.                                     this server, whether you started at the
  2788.                                     root or not. */
  2789.       {
  2790.     
  2791.         /* print it out like so:  101: message line 1
  2792.          *                        101: message line 2, etc...
  2793.           */
  2794.         line = XP_STRTOK(cd->login_message_buffer, "\n");
  2795.         while(ce->status >= 0 && line)
  2796.         {
  2797.             XP_STRCPY(buf, "101: ");
  2798.             XP_STRNCAT_SAFE (buf, line, sizeof(buf)-8);
  2799.             XP_STRCAT (buf, CRLF);
  2800.  
  2801.             ce->status = PUTSTRING(buf);
  2802.  
  2803.             line = XP_STRTOK(NULL, "\n");
  2804.         }
  2805.  
  2806.         /* if there is also a cwd message add a blank line */
  2807.         if(ce->status >= 0 && cd->cwd_message_buffer)
  2808.         {
  2809.             XP_STRCPY(buf, "101: "CRLF);
  2810.             ce->status = PUTSTRING(buf);
  2811.         }
  2812.     }
  2813.  
  2814.     if(cd->cwd_message_buffer)
  2815.     {
  2816.         /* print it out like so:  101: message line 1
  2817.          *                        101: message line 2, etc...
  2818.           */
  2819.         char *end_line = XP_STRCHR(cd->cwd_message_buffer, '\n');
  2820.         if(end_line)
  2821.             *end_line = '\0';
  2822.         line = cd->cwd_message_buffer;
  2823.  
  2824.         while(ce->status >= 0 && line)
  2825.         {
  2826.             XP_STRCPY(buf, "101: ");
  2827.             XP_STRNCAT_SAFE (buf, line, sizeof(buf)-8);
  2828.             XP_STRCAT (buf, CRLF);
  2829.  
  2830.             ce->status = PUTSTRING(buf);
  2831.  
  2832.             if(end_line)
  2833.             {
  2834.                 line = end_line+1;
  2835.                 end_line = XP_STRCHR(line, '\n');
  2836.                 if(end_line)
  2837.                     *end_line = '\0';
  2838.             }
  2839.             else
  2840.             {
  2841.                 line = NULL;
  2842.             }
  2843.         } 
  2844.     }
  2845.  
  2846.     /* clear the buffer so we can use it on the data sock */
  2847.     FREE_AND_CLEAR(cd->data_buf);
  2848.     cd->data_buf_size = 0;
  2849.  
  2850.     cd->next_state = FTP_READ_DIRECTORY;
  2851.  
  2852.     /* clear the select on the control socket
  2853.      */
  2854.     NET_ClearReadSelect(ce->window_id, ce->socket);
  2855.     ce->socket = cd->dsock;
  2856.     NET_SetReadSelect(ce->window_id, ce->socket);
  2857.  
  2858.     return(0); /* continue */
  2859. }
  2860.  
  2861. PRIVATE int
  2862. net_ftp_read_dir(ActiveEntry * ce)
  2863. {
  2864.    
  2865.     char * line;
  2866.     NET_FileEntryInfo * entry_info;
  2867.     FTPConData * cd = (FTPConData *)ce->con_data;
  2868.  
  2869.     ce->status = NET_BufferedReadLine(cd->dsock, &line, &cd->data_buf, 
  2870.                     &cd->data_buf_size, &cd->pause_for_read);
  2871.  
  2872.     if(ce->status == 0)
  2873.       {
  2874.         cd->next_state = FTP_PRINT_DIR;
  2875.         return(ce->status);
  2876.       }
  2877.     else if(ce->status < 0)
  2878.       {
  2879.         if(cd->stream && (*cd->stream->is_write_ready)(cd->stream))
  2880.             NET_PrintDirectory(&cd->sort_base, cd->stream, cd->path, ce->URL_s);
  2881.  
  2882.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, ce->status);
  2883.  
  2884.         /* return TCP error
  2885.          */
  2886.         return MK_TCP_READ_ERROR;
  2887.       }
  2888.  
  2889.     /* not exactly right but really close
  2890.      */
  2891.     if(ce->status > 1)
  2892.       {
  2893.         ce->bytes_received += ce->status;
  2894.         FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
  2895.       }
  2896.  
  2897.     if(!line)
  2898.         return(0); /* no line ready */
  2899.  
  2900.     TRACEMSG(("MKFTP: Line in %s is %s\n", cd->path, line));
  2901.  
  2902.     entry_info = net_parse_dir_entry(line, cd->cc->server_type);
  2903.  
  2904.     if(!entry_info)
  2905.       {
  2906.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  2907.         return(MK_OUT_OF_MEMORY);
  2908.       }
  2909.  
  2910.     if(entry_info->display)
  2911.       {
  2912.         TRACEMSG(("Adding file to sort list: %s\n", entry_info->filename));
  2913.         NET_SortAdd(cd->sort_base, (void *)entry_info);
  2914.       }
  2915.     else
  2916.       {
  2917.         NET_FreeEntryInfoStruct(entry_info);
  2918.       }
  2919.  
  2920.     return(ce->status);
  2921. }
  2922.  
  2923. PRIVATE int
  2924. net_ftp_print_dir(ActiveEntry * ce)
  2925. {
  2926.     FTPConData * cd = (FTPConData *)ce->con_data;
  2927.  
  2928.     if(!cd->calling_netlib_all_the_time)
  2929.     {
  2930.  
  2931.         cd->calling_netlib_all_the_time = TRUE;
  2932.         NET_SetCallNetlibAllTheTime(ce->window_id, "mkftp");
  2933.     }
  2934.  
  2935.     if(cd->stream 
  2936.        && (ce->status = (*cd->stream->is_write_ready)(cd->stream)) != 0)
  2937.     {
  2938.         ce->status = NET_PrintDirectory(&cd->sort_base, cd->stream, cd->path, ce->URL_s);
  2939.  
  2940.         /* go read the response since we need to
  2941.          * get the "226 sucessful transfer" message
  2942.          * out of the read queue in order to cache
  2943.          * connections properly
  2944.          */
  2945.         cd->next_state = FTP_WAIT_FOR_RESPONSE;
  2946.         cd->next_state_after_response = FTP_DONE;
  2947.           
  2948.         /* clear select on the data sock and set select on the control sock
  2949.          */
  2950.         if(cd->dsock != NULL)
  2951.           {
  2952.             NET_ClearReadSelect(ce->window_id, cd->dsock);
  2953.             PR_Close(cd->dsock);
  2954.             cd->dsock = NULL;
  2955.           }
  2956.         
  2957.         ce->socket = cd->cc->csock;
  2958.         NET_SetReadSelect(ce->window_id, cd->cc->csock);
  2959.  
  2960.         return(ce->status);
  2961.       }
  2962.     else
  2963.       {
  2964.         /* come back into this state and try again later */
  2965.         /* @@@ busy wait */
  2966.         cd->pause_for_read = TRUE;
  2967.         return(0);
  2968.       }
  2969. }
  2970.  
  2971. /*
  2972.  * is_ls_date() --
  2973.  *      Return 1 if the ptr points to a string of the form:
  2974.  *              "Sep  1  1990 " or
  2975.  *              "Sep 11 11:59 " or
  2976.  *              "Dec 12 1989  " or
  2977.  *              "FCv 23 1990  " ...
  2978.  */
  2979. PRIVATE Bool 
  2980. net_is_ls_date (char *s)
  2981. {
  2982.     /* must start with three alpha characters */
  2983.     if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
  2984.         return FALSE;
  2985.  
  2986.     /* space */
  2987.     if (*s++ != ' ')
  2988.         return FALSE;
  2989.  
  2990.     /* space or digit */
  2991.     if ((*s != ' ') && !isdigit(*s))
  2992.         return FALSE;
  2993.     s++;
  2994.  
  2995.     /* digit */
  2996.     if (!isdigit(*s++))
  2997.         return FALSE;
  2998.  
  2999.     /* space */
  3000.     if (*s++ != ' ')
  3001.         return FALSE;
  3002.  
  3003.     /* space or digit */
  3004.     if ((*s != ' ') && !isdigit(*s))
  3005.         return FALSE;
  3006.     s++;
  3007.  
  3008.     /* digit */
  3009.     if (!isdigit(*s++))
  3010.         return FALSE;
  3011.  
  3012.     /* colon or digit */
  3013.     if ((*s != ':') && !isdigit(*s))
  3014.         return FALSE;
  3015.     s++;
  3016.  
  3017.     /* digit */
  3018.     if (!isdigit(*s++))
  3019.         return FALSE;
  3020.  
  3021.     /* space or digit */
  3022.     if ((*s != ' ') && !isdigit(*s))
  3023.         return FALSE;
  3024.     s++;
  3025.  
  3026.     /* space */
  3027.     if (*s++ != ' ')
  3028.         return FALSE;
  3029.  
  3030.     return TRUE;
  3031. } /* is_ls_date() */
  3032.  
  3033. /*       
  3034.  *  Converts a date string from 'ls -l' to a time_t number
  3035.  *
  3036.  *              "Sep  1  1990 " or
  3037.  *              "Sep 11 11:59 " or
  3038.  *              "Dec 12 1989  " or
  3039.  *              "FCv 23 1990  " ...
  3040.  *
  3041.  *  Returns 0 on error.
  3042.  */
  3043. PRIVATE time_t net_convert_unix_date (char * datestr)
  3044. {
  3045.     struct tm *time_info;         /* Points to static tm structure */
  3046.     char *bcol = datestr;         /* Column begin */
  3047.     char *ecol;                   /* Column end */
  3048.     long tval;
  3049.     int cnt;
  3050.     time_t curtime = time(NULL);
  3051.  
  3052.     if ((time_info = gmtime(&curtime)) == NULL) 
  3053.       {
  3054.         return (time_t) 0;
  3055.       }
  3056.  
  3057.     time_info->tm_isdst = -1;                 /* Disable summer time */
  3058.     for (cnt=0; cnt<3; cnt++)                 /* Month */
  3059.         *bcol++ = toupper(*bcol);
  3060.  
  3061.     if ((time_info->tm_mon = NET_MonthNo(datestr)) < 0)
  3062.         return (time_t) 0;
  3063.  
  3064.     ecol = bcol;                        /* Day */
  3065.     while (*ecol++ == ' ') ;            /* Spool to other side of day */
  3066.     while (*ecol++ != ' ') ;
  3067.     *--ecol = '\0';
  3068.  
  3069.     time_info->tm_mday = atoi(bcol);
  3070.     time_info->tm_wday = 0;
  3071.     time_info->tm_yday = 0;
  3072.     bcol = ++ecol;                                   /* Year */
  3073.     if ((ecol = XP_STRCHR(bcol, ':')) == NULL) 
  3074.       {
  3075.         time_info->tm_year = atoi(bcol)-1900;
  3076.         time_info->tm_sec = 0;
  3077.         time_info->tm_min = 0;
  3078.         time_info->tm_hour = 0;
  3079.       } 
  3080.     else 
  3081.       {                                 /* Time */
  3082.         /* If the time is given as hh:mm, then the file is less than 1 year
  3083.          *    old, but we might shift calandar year. This is avoided by checking
  3084.          *    if the date parsed is future or not. 
  3085.          */
  3086.         *ecol = '\0';
  3087.         time_info->tm_sec = 0;
  3088.         time_info->tm_min = atoi(++ecol);           /* Right side of ':' */
  3089.         time_info->tm_hour = atoi(bcol);         /* Left side of ':' */
  3090.         if (mktime(time_info) > curtime)
  3091.             --time_info->tm_year;
  3092.       }
  3093.     return ((tval = mktime(time_info)) == -1 ? (time_t) 0 : tval);
  3094. }
  3095.  
  3096. /* a dos date/time string looks like this
  3097.  * 04-06-95  02:03PM 
  3098.  * 07-13-95  11:39AM 
  3099.  */
  3100. PRIVATE time_t 
  3101. net_parse_dos_date_time (char * datestr)
  3102. {
  3103.     struct tm *time_info;         /* Points to static tm structure */
  3104.     long tval;
  3105.     time_t curtime = time(NULL);
  3106.  
  3107.     if ((time_info = gmtime(&curtime)) == NULL) 
  3108.       {
  3109.         return (time_t) 0;
  3110.       }
  3111.  
  3112.     time_info->tm_isdst = -1;                 /* Disable summer time */
  3113.  
  3114.     time_info->tm_mon = (datestr[1]-'0')-1;
  3115.  
  3116.     time_info->tm_mday = (((datestr[3]-'0')*10) + datestr[4]-'0');
  3117.     time_info->tm_year = (((datestr[6]-'0')*10) + datestr[7]-'0');
  3118.     time_info->tm_hour = (((datestr[10]-'0')*10) + datestr[11]-'0');
  3119.     if(datestr[15] == 'P')
  3120.         time_info->tm_hour += 12;
  3121.     time_info->tm_min = (((datestr[13]-'0')*10) + datestr[14]-'0');
  3122.  
  3123.     time_info->tm_wday = 0;
  3124.     time_info->tm_yday = 0;
  3125.     time_info->tm_sec = 0;
  3126.  
  3127.     return ((tval = mktime(time_info)) == -1 ? (time_t) 0 : tval);
  3128. }
  3129.  
  3130. /*         
  3131.  *  Converts a date string from vms to a time_t number
  3132.  *  This is needed in order to put out the date using the same format
  3133.  *  for all directory listings.
  3134.  *
  3135.  *  Returns 0 on error
  3136.  */
  3137. PRIVATE time_t net_convert_vms_date (char * datestr)
  3138. {
  3139.     struct tm *time_info;           /* Points to static tm structure */
  3140.     char *col;
  3141.     long tval;
  3142.     time_t curtime = time(NULL);
  3143.  
  3144.     if ((time_info = gmtime(&curtime)) == NULL)
  3145.         return (time_t) 0;
  3146.  
  3147.     time_info->tm_isdst = -1;                 /* Disable summer time */
  3148.  
  3149.     if ((col = XP_STRTOK(datestr, "-")) == NULL)
  3150.         return (time_t) 0;
  3151.  
  3152.     time_info->tm_mday = atoi(col);                   /* Day */
  3153.     time_info->tm_wday = 0;
  3154.     time_info->tm_yday = 0;
  3155.  
  3156.     if ((col = XP_STRTOK(NULL, "-")) == NULL || (time_info->tm_mon = NET_MonthNo(col)) < 0)
  3157.         return (time_t) 0;
  3158.  
  3159.     if ((col = XP_STRTOK(NULL, " ")) == NULL)               /* Year */
  3160.         return (time_t) 0;
  3161.  
  3162.     time_info->tm_year = atoi(col)-1900;
  3163.  
  3164.     if ((col = XP_STRTOK(NULL, ":")) == NULL)               /* Hour */
  3165.         return (time_t) 0;
  3166.  
  3167.     time_info->tm_hour = atoi(col);
  3168.  
  3169.     if ((col = XP_STRTOK(NULL, " ")) == NULL)               /* Mins */
  3170.         return (time_t) 0;
  3171.  
  3172.     time_info->tm_min = atoi(col);
  3173.     time_info->tm_sec = 0;
  3174.  
  3175.     return ((tval = mktime(time_info)) < 0 ? (time_t) 0 : tval);
  3176. }
  3177.  
  3178.  
  3179. /*
  3180.  * parse_ls_line() --
  3181.  *      Extract the name, size, and date from an ls -l line.
  3182.  *
  3183.  * ls -l listing
  3184.  *
  3185.  * drwxr-xr-x    2 montulli eng          512 Nov  8 23:23 CVS
  3186.  * -rw-r--r--    1 montulli eng         2244 Nov  8 23:23 Imakefile
  3187.  * -rw-r--r--    1 montulli eng        14615 Nov  9 17:03 Makefile
  3188.  *
  3189.  * or a dl listing
  3190.  *
  3191.  * cs.news/              =  ICSI Computing Systems Newsletter
  3192.  * dash/                 -  DASH group documents and software
  3193.  * elisp/                -  Emacs lisp code
  3194.  * 
  3195.  */
  3196. PRIVATE void 
  3197. net_parse_ls_line (char *line, NET_FileEntryInfo *entry_info)
  3198. {
  3199.     int32   base=1;
  3200.     int32   size_num=0;
  3201.     char    save_char;
  3202.     char   *ptr;
  3203.  
  3204.     for (ptr = &line[XP_STRLEN(line) - 1];
  3205.             (ptr > line+13) && (!XP_IS_SPACE(*ptr) || !net_is_ls_date(ptr-12)); ptr--)
  3206.                 ; /* null body */
  3207.     save_char = *ptr;
  3208.     *ptr = '\0';
  3209.     if (ptr > line+13) 
  3210.       {
  3211.         entry_info->date = net_convert_unix_date(ptr-12);
  3212.       }
  3213.     else
  3214.       {
  3215.         /* must be a dl listing
  3216.          */
  3217.         /* unterminate the line */
  3218.         *ptr = save_char;
  3219.         /* find the first whitespace and  terminate
  3220.          */
  3221.         for(ptr=line; *ptr != '\0'; ptr++)
  3222.             if(XP_IS_SPACE(*ptr))
  3223.               {
  3224.                 *ptr = '\0';
  3225.                 break;
  3226.               }
  3227.         entry_info->filename = NET_Escape(line, URL_PATH);
  3228.     
  3229.         return;
  3230.       }
  3231.  
  3232.     /* escape and copy
  3233.      */
  3234.     entry_info->filename = NET_Escape(ptr+1, URL_PATH);
  3235.  
  3236.     /* parse size
  3237.      */
  3238.     if(ptr > line+15)
  3239.       {
  3240.         ptr -= 14;
  3241.         while (isdigit(*ptr))
  3242.           {
  3243.             size_num += ((int32) (*ptr - '0')) * base;
  3244.             base *= 10;
  3245.             ptr--;
  3246.           }
  3247.     
  3248.         entry_info->size = size_num;
  3249.       }
  3250.     
  3251. } /* parse_ls_line() */
  3252.  
  3253. /*
  3254.  * net_parse_vms_dir_entry()
  3255.  *      Format the name, date, and size from a VMS LIST line
  3256.  *      into the EntryInfo structure
  3257.  */
  3258. PRIVATE void 
  3259. net_parse_vms_dir_entry (char *line, NET_FileEntryInfo *entry_info)
  3260. {
  3261.         int i, j;
  3262.         int32 ialloc;
  3263.         char *cp, *cpd, *cps, date[64], *sp = " ";
  3264.         time_t NowTime;
  3265.         static char ThisYear[32];
  3266.         static Bool HaveYear = FALSE; 
  3267.  
  3268.         /**  Get rid of blank lines, and information lines.  **/
  3269.         /**  Valid lines have the ';' version number token.  **/
  3270.         if (!XP_STRLEN(line) || (cp=XP_STRCHR(line, ';')) == NULL) 
  3271.           {
  3272.             entry_info->display = FALSE;
  3273.             return;
  3274.           }
  3275.  
  3276.         /** Cut out file or directory name at VMS version number. **/
  3277.         *cp++ ='\0';
  3278.         /* escape and copy
  3279.           */
  3280.         entry_info->filename = NET_Escape(line, URL_PATH);
  3281.  
  3282.         /** Cast VMS file and directory names to lowercase. **/
  3283.         for (i=0; entry_info->filename[i]; i++)
  3284.             entry_info->filename[i] = tolower(entry_info->filename[i]);
  3285.  
  3286.         /** Uppercase terminal .z's or _z's. **/
  3287.         if ((--i > 2) && entry_info->filename[i] == 'z' &&
  3288.                  (entry_info->filename[i-1] == '.' || entry_info->filename[i-1] == '_'))
  3289.             entry_info->filename[i] = 'Z';
  3290.  
  3291.         /** Convert any tabs in rest of line to spaces. **/
  3292.         cps = cp-1;
  3293.         while ((cps=XP_STRCHR(cps+1, '\t')) != NULL)
  3294.             *cps = ' ';
  3295.  
  3296.         /** Collapse serial spaces. **/
  3297.         i = 0; j = 1;
  3298.         cps = cp;
  3299.         while (cps[j] != '\0') 
  3300.           {
  3301.             if (cps[i] == ' ' && cps[j] == ' ')
  3302.                 j++;
  3303.             else
  3304.                 cps[++i] = cps[j++];
  3305.           }
  3306.         cps[++i] = '\0';
  3307.  
  3308.         /* Save the current year.       
  3309.          * It could be wrong on New Year's Eve.
  3310.          */
  3311.         if (!HaveYear) 
  3312.            {
  3313.             NowTime = time(NULL);
  3314.             XP_STRCPY(ThisYear, (char *)ctime(&NowTime)+20);
  3315.             ThisYear[4] = '\0';
  3316.             HaveYear = TRUE;
  3317.           }
  3318.  
  3319.         /* get the date. 
  3320.          */
  3321.         if ((cpd=XP_STRCHR(cp, '-')) != NULL &&
  3322.                 XP_STRLEN(cpd) > 9 && isdigit(*(cpd-1)) &&
  3323.                 isalpha(*(cpd+1)) && *(cpd+4) == '-') 
  3324.           {
  3325.  
  3326.             /* Month 
  3327.              */
  3328.             *(cpd+4) = '\0';
  3329.             *(cpd+2) = tolower(*(cpd+2));
  3330.             *(cpd+3) = tolower(*(cpd+3));
  3331.             XP_SPRINTF(date, "%.32s ", cpd+1);
  3332.             *(cpd+4) = '-';
  3333.  
  3334.             /** Day **/
  3335.             *cpd = '\0';
  3336.             if (isdigit(*(cpd-2)))
  3337.                 XP_SPRINTF(date+4, "%.32s ", cpd-2);
  3338.             else
  3339.                 XP_SPRINTF(date+4, " %.32s ", cpd-1);
  3340.             *cpd = '-';
  3341.  
  3342.             /** Time or Year **/
  3343.             if (!XP_STRNCMP(ThisYear, cpd+5, 4) && XP_STRLEN(cpd) > 15 && *(cpd+12) == ':')
  3344.               {
  3345.                 *(cpd+15) = '\0';
  3346.                 XP_SPRINTF(date+7, "%.32s", cpd+10);
  3347.                 *(cpd+15) = ' ';
  3348.               } 
  3349.             else 
  3350.               {
  3351.                 *(cpd+9) = '\0';
  3352.                 XP_SPRINTF(date+7, " %.32s", cpd+5);
  3353.                 *(cpd+9) = ' ';
  3354.               }
  3355.  
  3356.             entry_info->date = net_convert_vms_date(date);
  3357.           }
  3358.  
  3359.         /* get the size 
  3360.          */
  3361.         if ((cpd=XP_STRCHR(cp, '/')) != NULL) 
  3362.           {
  3363.             /* Appears be in used/allocated format */
  3364.             cps = cpd;
  3365.             while (isdigit(*(cps-1)))
  3366.                 cps--;
  3367.             if (cps < cpd)
  3368.                 *cpd = '\0';
  3369.             entry_info->size = atol(cps);
  3370.             cps = cpd+1;
  3371.             while (isdigit(*cps))
  3372.                 cps++;
  3373.             *cps = '\0';
  3374.             ialloc = atoi(cpd+1);
  3375.             /* Check if used is in blocks or bytes */
  3376.             if (entry_info->size <= ialloc)
  3377.                 entry_info->size *= 512;
  3378.           }
  3379.         else if ((cps=XP_STRTOK(cp, sp)) != NULL) 
  3380.           {
  3381.             /* We just initialized on the version number 
  3382.              * Now let's find a lone, size number   
  3383.              */
  3384.             while ((cps=XP_STRTOK(NULL, sp)) != NULL) 
  3385.               {
  3386.                  cpd = cps;
  3387.                  while (isdigit(*cpd))
  3388.                      cpd++;
  3389.                  if (*cpd == '\0') 
  3390.                    {
  3391.                      /* Assume it's blocks */
  3392.                      entry_info->size = atol(cps) * 512;
  3393.                      break;
  3394.                    }
  3395.                }
  3396.           }
  3397.  
  3398.         /** Wrap it up **/
  3399.         TRACEMSG(("MKFTP: VMS filename: %s  date: %d  size: %ld\n",
  3400.                          entry_info->filename, entry_info->date, entry_info->size));
  3401.         return;
  3402.  
  3403. } /* net_parse_vms_dir_entry() */
  3404.  
  3405. /*
  3406.  *     parse_dir_entry() 
  3407.  *      Given a line of LIST/NLST output in entry, return results 
  3408.  *      and a file/dir name in entry_info struct
  3409.  *
  3410.  */
  3411. PRIVATE NET_FileEntryInfo * 
  3412. net_parse_dir_entry (char *entry, int server_type)
  3413. {
  3414.         NET_FileEntryInfo *entry_info;
  3415.         int  i;
  3416.         int  len;
  3417.         Bool remove_size=FALSE;
  3418.  
  3419.         entry_info = NET_CreateFileEntryInfoStruct();
  3420.         if(!entry_info)
  3421.             return(NULL);
  3422.  
  3423.         entry_info->display = TRUE;
  3424.  
  3425.         /* do special case for ambiguous NT servers
  3426.          *
  3427.          * ftp.microsoft.com is an NT server with UNIX ls -l
  3428.          * syntax.  But most NT servers use the DOS dir syntax.
  3429.          * If there is a space at position 8 then it's a DOS dir syntax
  3430.          */
  3431.         if(server_type == FTP_NT_TYPE && !XP_IS_SPACE(entry[8]))
  3432.             server_type = FTP_UNIX_TYPE;
  3433.  
  3434.         switch (server_type)
  3435.         {
  3436.             case FTP_UNIX_TYPE:
  3437.             case FTP_PETER_LEWIS_TYPE:
  3438.             case FTP_MACHTEN_TYPE:
  3439.                 /* interpret and edit LIST output from Unix server */
  3440.  
  3441.                 if(!XP_STRNCMP(entry, "total ", 6)  
  3442.                         || (XP_STRSTR(entry, "Permission denied") != NULL)
  3443.                             ||     (XP_STRSTR(entry, "not available") != NULL))
  3444.                   {
  3445.                     entry_info->display=FALSE;
  3446.                     return(entry_info);
  3447.                   }
  3448.  
  3449.                 len = XP_STRLEN(entry);
  3450.  
  3451.                 if( '+' == entry[0] )
  3452.                 {
  3453.                     /*
  3454.                      * EPLF: see http://pobox.com/~djb/proto/eplf.txt
  3455.                      * "+i8388621.29609,m824255902,/,\tdev"
  3456.                      * "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF"
  3457.                      *
  3458.                      * A plus, a bunch of comma-separated facts, a tab, 
  3459.                      * and then the name.  Facts include m for mdtm (as
  3460.                      * seconds since the Unix epoch), s for size, r for
  3461.                      * (readable) file, and / for (listable) directory.
  3462.                      */
  3463.                     char *tab = XP_STRCHR(entry, '\t');
  3464.                     char *begin, *end;
  3465.                     remove_size = TRUE; /* It'll be put back if we have data */
  3466.                     if( (char *)0 == tab ) break;
  3467.                     for( begin = &entry[1]; begin < tab; begin = &end[1])
  3468.                     {
  3469.                         end = XP_STRCHR(begin, ',');
  3470.                         if( (char *)0 == end ) break;
  3471.                         switch( *begin )
  3472.                         {
  3473.                             case 'm':
  3474.                                 *end = '\0';
  3475.                                 entry_info->date = XP_ATOI(&begin[1]);
  3476.                                 *end = ',';
  3477.                                 break;
  3478.                             case 's':
  3479.                                 *end = '\0';
  3480.                                 entry_info->size = XP_ATOI(&begin[1]);
  3481.                                 *end = ',';
  3482.                                 remove_size = FALSE;
  3483.                                 break;
  3484.                             case 'r':
  3485.                                 entry_info->special_type = NET_FILE_TYPE;
  3486.                                 break;
  3487.                             case '/':
  3488.                                 entry_info->special_type = NET_DIRECTORY;
  3489.                                 break;
  3490.                             default:
  3491.                                 break;
  3492.                         }
  3493.                     }
  3494.  
  3495.                     entry_info->filename = NET_Escape(&tab[1], URL_PATH);
  3496.                     break;
  3497.                 }
  3498.                 else 
  3499.                 /* check first character of ls -l output */
  3500.                 if (toupper(entry[0]) == 'D') 
  3501.                   {
  3502.                     /* it's a directory */
  3503.                     entry_info->special_type = NET_DIRECTORY;
  3504.                     remove_size=TRUE; /* size is not useful */
  3505.                   }
  3506.                 else if (entry[0] == 'l')
  3507.                   {
  3508.                     /* it's a symbolic link, does the user care about
  3509.                      * knowing if it is symbolic?  I think so since
  3510.                      * it might be a directory
  3511.                      */
  3512.                     entry_info->special_type = NET_SYM_LINK;
  3513.                     remove_size=TRUE; /* size is not useful */
  3514.  
  3515.                     /* strip off " -> pathname" */
  3516.                     for (i = len - 1; (i > 3) && (!XP_IS_SPACE(entry[i])
  3517.                     || (entry[i-1] != '>') 
  3518.                     || (entry[i-2] != '-')
  3519.                     || (entry[i-3] != ' ')); i--)
  3520.                              ; /* null body */
  3521.                     if (i > 3)
  3522.                       {
  3523.                         entry[i-3] = '\0';
  3524.                         len = i - 3;
  3525.                       }
  3526.                   } /* link */
  3527.  
  3528.                 net_parse_ls_line(entry, entry_info); 
  3529.  
  3530.                 if(!XP_STRCMP(entry_info->filename,"..") || !XP_STRCMP(entry_info->filename,"."))
  3531.                     entry_info->display=FALSE;
  3532.  
  3533.                 /* add a trailing slash to all directories */
  3534.                 if(entry_info->special_type == NET_DIRECTORY)
  3535.                     StrAllocCat(entry_info->filename, "/");
  3536.  
  3537.                 /* goto the bottom and get real type */
  3538.                 break;
  3539.  
  3540.             case FTP_VMS_TYPE:
  3541.                 /* Interpret and edit LIST output from VMS server */
  3542.                 /* and convert information lines to zero length.  */
  3543.                 net_parse_vms_dir_entry(entry, entry_info);
  3544.  
  3545.                 /* Get rid of any junk lines */
  3546.                 if(!entry_info->display)
  3547.                     return(entry_info);
  3548.  
  3549.                 /** Trim off VMS directory extensions **/
  3550.                 len = XP_STRLEN(entry_info->filename);
  3551.                 if ((len > 4) && !XP_STRCMP(&entry_info->filename[len-4], ".dir"))
  3552.                   {
  3553.                     entry_info->filename[len-4] = '\0';
  3554.                     entry_info->special_type = NET_DIRECTORY;
  3555.                     remove_size=TRUE; /* size is not useful */
  3556.                     /* add trailing slash to directories
  3557.                      */
  3558.                     StrAllocCat(entry_info->filename, "/");
  3559.                   }
  3560.                 /* goto the bottom and get real type */
  3561.                 break;
  3562.  
  3563.             case FTP_CMS_TYPE:
  3564.                 /* can't be directory... */
  3565.                 /*
  3566.                  * "entry" already equals the correct filename
  3567.                  */
  3568.                 /* escape and copy
  3569.                   */
  3570.                 entry_info->filename = NET_Escape(entry, URL_PATH);
  3571.                 /* goto the bottom and get real type */
  3572.                 break;
  3573.  
  3574.             case FTP_NCSA_TYPE:
  3575.             case FTP_TCPC_TYPE:
  3576.                 /* directories identified by trailing "/" characters */
  3577.                 /* escape and copy
  3578.                   */
  3579.                 entry_info->filename = NET_Escape(entry, URL_PATH);
  3580.                 len = XP_STRLEN(entry);
  3581.                 if (entry[len-1] == '/')
  3582.                   {
  3583.                     entry_info->special_type = NET_DIRECTORY;
  3584.                     remove_size=TRUE; /* size is not useful */
  3585.                   }
  3586.                   /* goto the bottom and get real type */
  3587.                 break;
  3588.  
  3589.             case FTP_NT_TYPE:
  3590.                 /* windows NT DOS dir syntax.
  3591.                  * looks like:  
  3592.                  *            1         2         3         4         5
  3593.                  *  012345678901234567890123456789012345678901234567890
  3594.                  *  06-29-95  03:05PM       <DIR>          muntemp
  3595.                  *  05-02-95  10:03AM               961590 naxp11e.zip
  3596.                  *
  3597.                  *  The date time directory indicator and filename
  3598.                  *  are always in a fixed position.  The file
  3599.                   *  size always ends at position 37.
  3600.                  */
  3601.                   {
  3602.                     char *date, *size_s, *name;
  3603.         
  3604.                     if(XP_STRLEN(entry) > 37)
  3605.                       {
  3606.                         date = entry;
  3607.                         entry[17] = '\0';
  3608.                         size_s = &entry[18];
  3609.                         entry[38] = '\0';
  3610.                         name = &entry[39];
  3611.     
  3612.                         if(XP_STRSTR(size_s, "<DIR>"))
  3613.                             entry_info->special_type = NET_DIRECTORY;
  3614.                         else
  3615.                             entry_info->size = atol(XP_StripLine(size_s));
  3616.     
  3617.                         entry_info->date = net_parse_dos_date_time(date);
  3618.  
  3619.                         StrAllocCopy(entry_info->filename, name);
  3620.                       }
  3621.                     else
  3622.                       {
  3623.                         StrAllocCopy(entry_info->filename, entry);
  3624.                       }
  3625.                   }
  3626.                 break;
  3627.     
  3628.             default:
  3629.                 /* we cant tell if it is a directory since we only
  3630.                  * did an NLST   
  3631.                  */
  3632.                 /* escape and copy
  3633.                   */
  3634.                 entry_info->filename = NET_Escape(entry, URL_PATH);
  3635.                 return(entry_info); /* mostly empty info */
  3636.                 /* break; Not needed */
  3637.  
  3638.         } /* switch (server_type) */
  3639.  
  3640.  
  3641.     if(remove_size && entry_info->size)
  3642.       {
  3643.         entry_info->size = 0;
  3644.       }
  3645.  
  3646.     /* get real types eventually */
  3647.     if(!entry_info->special_type) 
  3648.       {
  3649.         entry_info->cinfo = NET_cinfo_find_type(entry_info->filename);
  3650.       }
  3651.  
  3652.     return(entry_info);
  3653.  
  3654. } /* net_parse_dir_entry */
  3655.  
  3656. PRIVATE int
  3657. net_get_ftp_password(ActiveEntry *ce)
  3658. {
  3659.     FTPConData *cd = (FTPConData *) ce->con_data;
  3660.  
  3661.     if(!cd->password)
  3662.       {
  3663.         char * host_string = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  3664.  
  3665. #ifndef MCC_PROXY
  3666.         if(ftp_last_password 
  3667.                 && ftp_last_password_host 
  3668.                     && !XP_STRCMP(ftp_last_password_host, host_string)) 
  3669.           {
  3670.             StrAllocCopy(cd->password, ftp_last_password);
  3671.           }
  3672.         else
  3673. #endif
  3674.           {
  3675.             /* check for cached password */
  3676.             char *key = gen_ftp_password_key(ce->URL_s->address, cd->username);
  3677.  
  3678.             if(key)
  3679.             {
  3680.                 PCNameValueArray * array = PC_CheckForStoredPasswordArray(PC_FTP_MODULE_KEY, key);
  3681.  
  3682.                 if(array)
  3683.                     cd->password = PC_FindInNameValueArray(array, PC_FTP_PASSWORD_KEY);
  3684.  
  3685.                 PC_FreeNameValueArray(array);
  3686.  
  3687.                 FREE(key);
  3688.             }
  3689.  
  3690.             if(!cd->password)
  3691.             {
  3692.                 PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE,
  3693.                             XP_GetString(XP_PROMPT_ENTER_PASSWORD),
  3694.                             host_string);
  3695.                 cd->password = (char *)PC_PromptPassword(ce->window_id,
  3696.                                                      cd->output_buffer,
  3697.                                                      &cd->store_password,
  3698.                                                      FALSE /* not secure */);
  3699.  
  3700.                 if(!cd->password)
  3701.                   {
  3702.                     FREE(host_string);
  3703.                     return(MK_INTERRUPTED);  /* user canceled */
  3704.                   }
  3705.             }
  3706.  
  3707.             StrAllocCopy(ftp_last_password_host, host_string);
  3708.             StrAllocCopy(ftp_last_password, cd->password);
  3709.           }
  3710.  
  3711.         FREE(host_string);
  3712.       }
  3713.  
  3714.     cd->next_state = FTP_SEND_PASSWORD;
  3715.  
  3716.     return 0;
  3717. }
  3718.  
  3719. PRIVATE int
  3720. net_get_ftp_password_response(FTPConData *cd)
  3721. {
  3722.     /* not used yet
  3723.      */
  3724.     return 0; /* #### what should return value be? */
  3725. }
  3726.  
  3727. /* the load initializeation routine for FTP.
  3728.  *
  3729.  * this sets up all the data structures and begin's the
  3730.  * connection by calling processFTP
  3731.  */
  3732. PRIVATE int32
  3733. net_FTPLoad (ActiveEntry * ce)
  3734. {
  3735.     char * host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  3736.     char * unamePwd = NET_ParseURL(ce->URL_s->address, GET_USERNAME_PART | GET_PASSWORD_PART);
  3737.     char *username,*colon,*password=NULL;
  3738.     char * filename;
  3739.     char * semi_colon;
  3740.     FTPConData * cd = XP_NEW(FTPConData);
  3741.  
  3742.     if(!cd)
  3743.       {
  3744.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  3745.         ce->status = MK_OUT_OF_MEMORY;
  3746.         return(MK_OUT_OF_MEMORY);  
  3747.       }
  3748.  
  3749.     /* get the username & password out of the combo string */
  3750.     if( (colon = XP_STRCHR(unamePwd, ':')) != NULL ) {
  3751.         *colon='\0';
  3752.         username=XP_STRDUP(unamePwd);
  3753.         password=XP_STRDUP(colon+1);
  3754.         *colon=':';
  3755.         XP_FREE(unamePwd);
  3756.     } else {
  3757.         username=unamePwd;
  3758.     }
  3759.     
  3760.     ce->con_data = (FTPConData *)cd;
  3761.  
  3762.     /* init the connection data struct 
  3763.      */
  3764.     XP_MEMSET(cd, 0, sizeof(FTPConData));
  3765.     cd->dsock         = NULL;
  3766.     cd->listen_sock   = NULL;
  3767.     cd->pasv_mode     = TRUE;
  3768.     cd->is_directory  = FALSE;
  3769.     cd->next_state_after_response = FTP_ERROR_DONE;
  3770.     cd->cont_response     = -1;
  3771.  
  3772.     /* @@@@ hope we never need a buffer larger than this 
  3773.      */
  3774.     cd->output_buffer = (char *) XP_ALLOC(OUTPUT_BUFFER_SIZE);
  3775.     if(!cd->output_buffer)
  3776.       {
  3777.         FREE(cd);
  3778.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  3779.         ce->status = MK_OUT_OF_MEMORY;
  3780.         return(MK_OUT_OF_MEMORY);  
  3781.       }
  3782.  
  3783.     if (username && *username) {
  3784.         StrAllocCopy(cd->username, username);
  3785.         if (password && *password)
  3786.             StrAllocCopy(cd->password, password);
  3787.     } else {
  3788.         const char * user = NULL;
  3789.         XP_FREEIF(username);
  3790.         username = NULL;
  3791.         XP_FREEIF(password);
  3792.         password = NULL;
  3793.         
  3794. #ifdef MOZ_MAIL_NEWS
  3795. #ifdef MOZILLA_CLIENT
  3796.         if(net_get_send_email_address_as_password())
  3797.             user = FE_UsersMailAddress();
  3798. #endif
  3799. #endif /* MOZ_MAIL_NEWS */
  3800.  
  3801.         if(user && *user)
  3802.           {
  3803.             StrAllocCopy(cd->password, user);
  3804.  
  3805.             /* make sure it has an @ sign in it or else the ftp
  3806.              * server won't like it
  3807.              */
  3808.             if(!XP_STRCHR(cd->password, '@'))
  3809.                 StrAllocCat(cd->password, "@");
  3810.           }
  3811.         else
  3812.           {
  3813.             StrAllocCopy(cd->password, "mozilla@");
  3814.           }
  3815.     }
  3816.  
  3817.     /* get the path */
  3818.     cd->path = NET_ParseURL(ce->URL_s->address, GET_PATH_PART);
  3819.  
  3820.     if(!cd->path)
  3821.       {
  3822.         FREE(ce->con_data);
  3823.         FREE(cd->output_buffer);
  3824.         ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  3825.         ce->status = MK_OUT_OF_MEMORY;
  3826.         return(MK_OUT_OF_MEMORY);
  3827.       }
  3828.  
  3829.     NET_UnEscape(cd->path);
  3830.  
  3831.     if(*cd->path == '\0')
  3832.       {
  3833.         TRACEMSG(("Found ftp url with NO PATH"));
  3834.         
  3835.         cd->use_default_path = TRUE;
  3836.       }
  3837.     else
  3838.       {
  3839.         /* terminate at an "\r" or "\n" in the string
  3840.          * this prevents URL's of the form "foo\nDELE foo"
  3841.          */
  3842.         XP_STRTOK(cd->path, "\r");
  3843.         XP_STRTOK(cd->path, "\n");
  3844.       }
  3845.  
  3846.     /* if the path begins with /./ 
  3847.      * use pwd to get the sub path
  3848.      * and append the end to it
  3849.      */
  3850.     if(!XP_STRNCMP(cd->path, "/./", 3) || !XP_STRCMP(cd->path, "/."))
  3851.       {
  3852.         char * tmp = cd->path;
  3853.  
  3854.         cd->use_default_path = TRUE;
  3855.  
  3856.         /* skip the "/." and leave a slash at the beginning */
  3857.         cd->path = XP_STRDUP(cd->path+2);
  3858.         XP_FREE(tmp);
  3859.  
  3860.         if(!cd->path)
  3861.           {
  3862.             FREE(ce->con_data);
  3863.             FREE(cd->output_buffer);
  3864.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  3865.             ce->status = MK_OUT_OF_MEMORY;
  3866.             return(MK_OUT_OF_MEMORY);
  3867.           }
  3868.       }
  3869.  
  3870.     /* set the default type for use later
  3871.      */
  3872.     cd->type = FTP_MODE_UNKNOWN;
  3873.  
  3874.     TRACEMSG(("content length: %d, real content length: %d",
  3875.            ce->URL_s->content_length, ce->URL_s->real_content_length));
  3876.     if( (ce->URL_s->real_content_length > ce->URL_s->content_length) &&
  3877.         (NULL != ce->URL_s->cache_file) )
  3878.     {
  3879.         cd->restart = TRUE;
  3880.         TRACEMSG(("Considering a restart."));
  3881.     }
  3882.  
  3883.     /* look for the extra type information at the end per
  3884.      * the RFC 1630.  It will be delimited by a ';'
  3885.      */
  3886.     if((semi_colon = XP_STRRCHR(cd->path, ';')) != NULL)
  3887.       {
  3888.         /* just switch on the character at the end of this whole
  3889.          * thing since it must be the type to use
  3890.          */
  3891.         switch(semi_colon[XP_STRLEN(semi_colon)-1])
  3892.           {
  3893.             case 'a':
  3894.             case 'A':
  3895.                 cd->type = FTP_MODE_ASCII;
  3896.                 TRACEMSG(("Explicitly setting type ASCII"));
  3897.                 break;
  3898.  
  3899.             case 'i':
  3900.             case 'I':
  3901.                 cd->type = FTP_MODE_BINARY;
  3902.                 TRACEMSG(("Explicitly setting type BINARY"));
  3903.                 break;
  3904.           }
  3905.  
  3906.         /* chop off the bits after the semi colon
  3907.          */
  3908.         *semi_colon = '\0';
  3909.  
  3910.         /* also chop of the semi colon from the real URL */
  3911.         XP_STRTOK(ce->URL_s->address, ";");
  3912.       }
  3913.  
  3914.     /* find the filename in the path */
  3915.     filename = XP_STRRCHR(cd->path, '/');
  3916.     if(!filename)
  3917.         filename = cd->path;
  3918.     else
  3919.         filename++;  /* go past slash */
  3920.  
  3921.     cd->filename = 0;
  3922.     StrAllocCopy(cd->filename, filename);
  3923.  
  3924.     /* Try and find an open connection to this 
  3925.      * server that is not currently being used
  3926.      */
  3927.     if(connection_list)
  3928.       {
  3929.         FTPConnection * tmp_con;
  3930.         XP_List * list_entry = connection_list;
  3931.  
  3932.         while((tmp_con = (FTPConnection *)XP_ListNextObject(list_entry)) != NULL)
  3933.           {
  3934.             /* if the hostnames match up exactly and the connection
  3935.              * is not busy at the moment then reuse this connection.
  3936.              */
  3937.             if(!tmp_con->busy && !XP_STRCMP(tmp_con->hostname, host))
  3938.               {
  3939.                 cd->cc = tmp_con;
  3940.                 ce->socket = cd->cc->csock;  /* set select on the control socket */
  3941.                 NET_SetReadSelect(ce->window_id, cd->cc->csock);
  3942.                 cd->cc->prev_cache = TRUE;  /* this was from the cache */
  3943.                 break;
  3944.               }
  3945.           }
  3946.       }
  3947.     else
  3948.       {
  3949.     /* initialize the connection list 
  3950.      */
  3951.         connection_list = XP_ListNew();
  3952.       }
  3953.     
  3954.     if(!cd->cc)
  3955.       {
  3956.  
  3957.         TRACEMSG(("cached connection not found making new FTP connection"));
  3958.  
  3959.         /* build a control connection structure so we
  3960.          * can store the data as we go along
  3961.          */
  3962.         cd->cc = XP_NEW(FTPConnection);
  3963.         if(!cd->cc)
  3964.           {
  3965.             XP_FREE(host);  /* free from way up above */
  3966.             XP_FREEIF(username);
  3967.             XP_FREEIF(password);
  3968.             ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); 
  3969.             return(MK_OUT_OF_MEMORY);
  3970.           }
  3971.  
  3972.         cd->cc->hostname = 0;
  3973.         StrAllocCopy(cd->cc->hostname, host);
  3974.         /* set the mode of the control connection unknown
  3975.          */
  3976.         cd->cc->cur_mode = FTP_MODE_UNKNOWN;
  3977.         cd->cc->csock = NULL;
  3978.         cd->cc->server_type = FTP_GENERIC_TYPE;
  3979.         cd->cc->use_list = FALSE;
  3980.         cd->cc->no_pasv  = FALSE;
  3981.  
  3982.         cd->cc->prev_cache = FALSE;  /* this wasn't from the cache */
  3983.         cd->cc->no_restart = FALSE; /* as far as we know so far */
  3984.  
  3985.         /* add this structure to the connection list even
  3986.          * though it's not really valid yet.
  3987.          * we will fill it in as we go and if
  3988.          * an error occurs will will remove it from the
  3989.          * list.  No one else will be able to use it since
  3990.          * we will mark it busy.
  3991.          */
  3992.         XP_ListAddObjectToEnd(connection_list, cd->cc);
  3993.  
  3994.         /* set this connection busy so no one else can use it
  3995.          */
  3996.         cd->cc->busy = TRUE;
  3997.  
  3998.         /* gate the maximum number of cached connections
  3999.          * to one
  4000.          */
  4001.         if(XP_ListCount(connection_list) > 1)
  4002.           {
  4003.             XP_List * list_ptr = connection_list->next;
  4004.             FTPConnection * con;
  4005.  
  4006.             while(list_ptr)
  4007.               {
  4008.                 con = (FTPConnection *) list_ptr->object;
  4009.                 list_ptr = list_ptr->next;
  4010.                 if(!con->busy)
  4011.                   {
  4012.                     XP_ListRemoveObject(connection_list, con);
  4013.                     PR_Close(con->csock);
  4014.                     /* dont reduce the number of
  4015.                      * open connections since cached ones
  4016.                      * dont count as active
  4017.                      */
  4018.                     FREEIF(con->hostname);
  4019.                     XP_FREE(con);
  4020.                     break;
  4021.                   }
  4022.               }
  4023.           }
  4024.  
  4025.         cd->next_state = FTP_CONTROL_CONNECT;
  4026.       }
  4027.     else
  4028.       {
  4029.         TRACEMSG(("Found cached FTP connection! YES!!! "));
  4030.  
  4031.         /* don't send PASV if the server cant do it */
  4032.         if(cd->cc->no_pasv || !net_use_pasv)
  4033.             cd->next_state = FTP_SEND_PORT;
  4034.         else
  4035.             cd->next_state = FTP_SEND_PASV;
  4036.  
  4037.         if( cd->cc->no_restart )
  4038.             ce->URL_s->server_can_do_restart = FALSE;
  4039.         else
  4040.             ce->URL_s->server_can_do_restart = TRUE;
  4041.  
  4042.         /* set this connection busy so no one else can use it
  4043.          */
  4044.         cd->cc->busy = TRUE;
  4045.         /* add this connection to the number that is open
  4046.          * since it is now active
  4047.          */
  4048.         NET_TotalNumberOfOpenConnections++;
  4049.       }
  4050.  
  4051.     XP_FREE(host);  /* free from way up above */
  4052.     XP_FREEIF(username);
  4053.     XP_FREEIF(password);
  4054.     TRACEMSG(("Server can do restarts: %s", ce->URL_s->server_can_do_restart ? 
  4055.               "TRUE" : "FALSE"));
  4056.     return(net_ProcessFTP(ce));
  4057.  
  4058.  
  4059. /* the main state machine control routine.  Calls
  4060.  * the individual state processors
  4061.  * 
  4062.  * returns -1 when all done or 0 or positive all other times
  4063.  */
  4064. PRIVATE int32
  4065. net_ProcessFTP(ActiveEntry * ce)
  4066. {
  4067.     FTPConData * cd = (FTPConData *)ce->con_data;
  4068.  
  4069.     cd->pause_for_read = FALSE;
  4070.  
  4071.     while(!cd->pause_for_read)
  4072.       {
  4073.         TRACEMSG(("In ProcessFTP switch loop with state: %d \n", cd->next_state));
  4074.  
  4075.         switch(cd->next_state) {
  4076.  
  4077.           case FTP_WAIT_FOR_RESPONSE:
  4078.             ce->status = net_ftp_response(ce);
  4079.             break;
  4080.     
  4081.           /* begin login states */
  4082.           case FTP_CONTROL_CONNECT:
  4083.             ce->status = NET_BeginConnect(ce->URL_s->address,
  4084.                                           ce->URL_s->IPAddressString,
  4085.                                          "FTP",
  4086.                                             FTP_PORT,
  4087.                                          &cd->cc->csock, 
  4088.                                          FALSE, 
  4089.                                          &cd->tcp_con_data, 
  4090.                                          ce->window_id,
  4091.                                          &ce->URL_s->error_msg,
  4092.                                           ce->socks_host,
  4093.                                           ce->socks_port);
  4094.             ce->socket = cd->cc->csock;
  4095.  
  4096.             if(cd->cc->csock != NULL)
  4097.                 NET_TotalNumberOfOpenConnections++;
  4098.     
  4099.             cd->pause_for_read = TRUE;
  4100.     
  4101.             if(ce->status == MK_CONNECTED)
  4102.               {
  4103.                 cd->next_state = FTP_WAIT_FOR_RESPONSE;
  4104.                 cd->next_state_after_response = FTP_SEND_USERNAME;
  4105.                 NET_SetReadSelect(ce->window_id, cd->cc->csock);
  4106.               }
  4107.             else if(ce->status > -1)
  4108.               {
  4109.                 cd->next_state = FTP_CONTROL_CONNECT_WAIT;
  4110.                 ce->con_sock = cd->cc->csock;  /* set con sock so we can select on it */
  4111.                 NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4112.     
  4113.               }
  4114.             break;
  4115.     
  4116.           case FTP_CONTROL_CONNECT_WAIT:
  4117.             ce->status = NET_FinishConnect(ce->URL_s->address,
  4118.                                           "FTP",
  4119.                                             FTP_PORT,
  4120.                                           &cd->cc->csock, 
  4121.                                           &cd->tcp_con_data, 
  4122.                                           ce->window_id,
  4123.                                           &ce->URL_s->error_msg);
  4124.     
  4125.             cd->pause_for_read = TRUE;
  4126.     
  4127.             if(ce->status == MK_CONNECTED)
  4128.               {
  4129.                 cd->next_state = FTP_WAIT_FOR_RESPONSE;
  4130.                 cd->next_state_after_response = FTP_SEND_USERNAME;
  4131.                 NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4132.                 ce->con_sock = NULL;  /* reset con sock so we don't select on it */
  4133.                 NET_SetReadSelect(ce->window_id, ce->socket);
  4134.               }
  4135.             else
  4136.               {
  4137.                 /* the not yet connected state */
  4138.  
  4139.                 /* unregister the old CE_SOCK from the select list
  4140.                   * and register the new value in the case that it changes
  4141.                   */
  4142.                 if(ce->con_sock != cd->cc->csock)
  4143.                   {
  4144.                     NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4145.                     ce->con_sock = cd->cc->csock;
  4146.                     NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4147.                   }
  4148.               }
  4149.             break;
  4150.  
  4151.           case FTP_SEND_USERNAME:
  4152.             ce->status = net_send_username((FTPConData *)ce->con_data);
  4153.             break;
  4154.     
  4155.           case FTP_SEND_USERNAME_RESPONSE:
  4156.             ce->status = net_send_username_response(ce);
  4157.             break;
  4158.     
  4159.           case FTP_GET_PASSWORD:
  4160.             ce->status = net_get_ftp_password(ce);
  4161.             break;
  4162.  
  4163.           case FTP_GET_PASSWORD_RESPONSE:
  4164.             /* currently skipped 
  4165.              */
  4166.             ce->status = net_get_ftp_password_response((FTPConData *)ce->con_data);
  4167.             break;
  4168.     
  4169.           case FTP_SEND_PASSWORD:
  4170.             ce->status = net_send_password((FTPConData *)ce->con_data);
  4171.             break;
  4172.     
  4173.           case FTP_SEND_PASSWORD_RESPONSE:
  4174.             ce->status = net_send_password_response(ce);
  4175.             break;
  4176.         
  4177.           case FTP_SEND_ACCT:
  4178.             ce->status = net_send_acct((FTPConData *)ce->con_data);
  4179.             break;
  4180.     
  4181.           case FTP_SEND_ACCT_RESPONSE:
  4182.             ce->status = net_send_acct_response(ce);
  4183.             break;
  4184.     
  4185.           case FTP_SEND_REST_ZERO:
  4186.             ce->status = net_send_rest_zero(ce);
  4187.             break;
  4188.     
  4189.           case FTP_SEND_REST_ZERO_RESPONSE:
  4190.             ce->status = net_send_rest_zero_response(ce);
  4191.             break;
  4192.     
  4193.           case FTP_SEND_SYST:
  4194.             ce->status = net_send_syst((FTPConData *)ce->con_data);
  4195.             break;
  4196.     
  4197.           case FTP_SEND_SYST_RESPONSE:
  4198.             ce->status = net_send_syst_response((FTPConData *)ce->con_data);
  4199.             break;
  4200.     
  4201.           case FTP_SEND_MAC_BIN:
  4202.             ce->status = net_send_mac_bin((FTPConData *)ce->con_data);
  4203.             break;
  4204.     
  4205.           case FTP_SEND_MAC_BIN_RESPONSE:
  4206.             ce->status = net_send_mac_bin_response((FTPConData *)ce->con_data);
  4207.             break;
  4208.     
  4209.           case FTP_SEND_PWD:
  4210.             ce->status = net_send_pwd((FTPConData *)ce->con_data);
  4211.             break;
  4212.     
  4213.           case FTP_SEND_PWD_RESPONSE:
  4214.             ce->status = net_send_pwd_response(ce);
  4215.             break;
  4216.         /* end login states */
  4217.  
  4218.           case FTP_FIGURE_OUT_WHAT_TO_DO:
  4219.             ce->status = net_figure_out_what_to_do(ce);
  4220.             break;
  4221.  
  4222.           case FTP_SEND_MKDIR:
  4223.             ce->status = net_send_mkdir(ce);
  4224.             break;
  4225.  
  4226.           case FTP_SEND_MKDIR_RESPONSE:
  4227.             ce->status = net_send_mkdir_response(ce);
  4228.             break;
  4229.     
  4230.           case FTP_SEND_DELETE_FILE:
  4231.             ce->status = net_send_delete_file(ce);
  4232.             break;
  4233.  
  4234.           case FTP_SEND_DELETE_FILE_RESPONSE:
  4235.             ce->status = net_send_delete_file_response(ce);
  4236.             break;
  4237.     
  4238.           case FTP_SEND_DELETE_DIR:
  4239.             ce->status = net_send_delete_dir(ce);
  4240.             break;
  4241.  
  4242.           case FTP_SEND_DELETE_DIR_RESPONSE:
  4243.             ce->status = net_send_delete_dir_response(ce);
  4244.             break;
  4245.  
  4246.           case FTP_SEND_PASV:
  4247.             ce->status = net_send_pasv((FTPConData *)ce->con_data);
  4248.             break;
  4249.     
  4250.           case FTP_SEND_PASV_RESPONSE:
  4251.             ce->status = net_send_pasv_response((FTPConData *)ce->con_data);
  4252.             break;
  4253.     
  4254.           case FTP_PASV_DATA_CONNECT:
  4255.             ce->status = NET_BeginConnect(cd->data_con_address, 
  4256.                                           NULL,
  4257.                                          "FTP Data Connection",
  4258.                                            FTP_PORT, 
  4259.                                          &cd->dsock, 
  4260.                                          FALSE, 
  4261.                                          &cd->tcp_con_data, 
  4262.                                          ce->window_id,
  4263.                                          &ce->URL_s->error_msg,
  4264.                                           ce->socks_host,
  4265.                                           ce->socks_port);
  4266.     
  4267.             if(ce->status == MK_CONNECTED)
  4268.               {
  4269.                 cd->next_state = FTP_GET_FILE_TYPE;
  4270.               }
  4271.             else if(ce->status > -1)
  4272.               {
  4273.                 cd->pause_for_read = TRUE;
  4274.                 cd->next_state = FTP_PASV_DATA_CONNECT_WAIT;
  4275.                 ce->con_sock = cd->dsock;  /* set con sock so we can select on it */
  4276.                 NET_SetConnectSelect(ce->window_id, ce->con_sock);
  4277.               }
  4278.             break;
  4279.  
  4280.           case FTP_PASV_DATA_CONNECT_WAIT:
  4281.                 ce->status = NET_FinishConnect(cd->data_con_address, 
  4282.                                               "FTP Data Connection",
  4283.                                               FTP_PORT,
  4284.                                               &cd->dsock,
  4285.                                               &cd->tcp_con_data,
  4286.                                               ce->window_id,
  4287.                                               &ce->URL_s->error_msg);
  4288.                 TRACEMSG(("FTP: got pasv data connection on port #%d\n",cd->cc->csock));
  4289.     
  4290.                 if(ce->status == MK_CONNECTED)
  4291.                   {
  4292.                     cd->next_state = FTP_GET_FILE_TYPE;
  4293.                     NET_ClearConnectSelect(ce->window_id, ce->con_sock);
  4294.                     ce->con_sock = NULL;  /* reset con sock so we don't select on it */
  4295.                   }
  4296.                 else
  4297.                   {
  4298.                     ce->con_sock = cd->dsock; /* it might change */
  4299.                     cd->pause_for_read = TRUE;
  4300.                   }
  4301.             break;
  4302.     
  4303.           case FTP_SEND_PORT:
  4304.             ce->status = net_send_port(ce);
  4305.             break;
  4306.     
  4307.           case FTP_SEND_PORT_RESPONSE:
  4308.             ce->status = net_send_port_response(ce);
  4309.             break;
  4310.     
  4311.           /* This is the state that gets called after
  4312.            * login and port/pasv are done
  4313.            */
  4314.           case FTP_GET_FILE_TYPE:
  4315.             ce->status = net_get_ftp_file_type(ce);
  4316.             break;
  4317.     
  4318.           case FTP_SEND_BIN:
  4319.             ce->status = net_send_bin((FTPConData *)ce->con_data);
  4320.             break;
  4321.     
  4322.           case FTP_SEND_ASCII:
  4323.             ce->status = net_send_ascii((FTPConData *)ce->con_data);
  4324.             break;
  4325.     
  4326.           case FTP_SEND_BIN_RESPONSE:
  4327.           case FTP_SEND_ASCII_RESPONSE:
  4328.             ce->status = net_send_bin_or_ascii_response(ce);
  4329.             break;
  4330.  
  4331.           case FTP_GET_FILE_SIZE:
  4332.             ce->status = net_get_ftp_file_size(ce);
  4333.             break;
  4334.  
  4335.           case FTP_GET_FILE_SIZE_RESPONSE:
  4336.             ce->status = net_get_ftp_file_size_response(ce);
  4337.             break;
  4338.     
  4339.           case FTP_GET_FILE_MDTM:
  4340.             ce->status = net_get_ftp_file_mdtm(ce);
  4341.             break;
  4342.  
  4343.           case FTP_GET_FILE_MDTM_RESPONSE:
  4344.             ce->status = net_get_ftp_file_mdtm_response(ce);
  4345.             break;
  4346.     
  4347.           case FTP_GET_FILE:
  4348.             ce->status = net_get_ftp_file((FTPConData *)ce->con_data);
  4349.             break;
  4350.     
  4351.           case FTP_SEND_REST:
  4352.             ce->status = net_send_rest(ce);
  4353.             break;
  4354.  
  4355.           case FTP_SEND_REST_RESPONSE:
  4356.             ce->status = net_send_rest_response(ce);
  4357.             break;
  4358.  
  4359.           case FTP_SEND_RETR:
  4360.             ce->status = net_send_retr((FTPConData *)ce->con_data);
  4361.             break;
  4362.     
  4363.           case FTP_SEND_RETR_RESPONSE:
  4364.             ce->status = net_send_retr_response(ce);
  4365.             break;
  4366.     
  4367.           case FTP_SEND_CWD:
  4368.             ce->status = net_send_cwd((FTPConData *)ce->con_data);
  4369.             break;
  4370.     
  4371.           case FTP_SEND_CWD_RESPONSE:
  4372.             ce->status = net_send_cwd_response(ce);
  4373.             break;
  4374.     
  4375.           case FTP_BEGIN_PUT:
  4376.             ce->status = net_begin_put(ce);
  4377.             break;
  4378.     
  4379.           case FTP_BEGIN_PUT_RESPONSE:
  4380.             ce->status = net_begin_put_response(ce);
  4381.             break;
  4382.     
  4383.           case FTP_DO_PUT:
  4384.             ce->status = net_do_put(ce);
  4385.             break;
  4386.     
  4387.           case FTP_SEND_LIST_OR_NLST:
  4388.             ce->status = net_send_list_or_nlst(ce);
  4389.             break;
  4390.     
  4391.           case FTP_SEND_LIST_OR_NLST_RESPONSE:
  4392.             ce->status = net_send_list_or_nlst_response(ce);
  4393.             break;
  4394.  
  4395.           case FTP_SETUP_STREAM:
  4396.             ce->status = net_setup_ftp_stream(ce);
  4397.             break;
  4398.     
  4399.           case FTP_WAIT_FOR_ACCEPT:
  4400.             ce->status = net_wait_for_ftp_accept(ce);
  4401.             break;
  4402.     
  4403.           case FTP_READ_CACHE:
  4404.             ce->status = net_ftp_push_partial_cache_file(ce);
  4405.             break;
  4406.  
  4407.           case FTP_START_READ_FILE:
  4408.             ce->status = net_start_ftp_read_file(ce);
  4409.             break;
  4410.     
  4411.           case FTP_READ_FILE:
  4412.             ce->status = net_ftp_read_file(ce);
  4413.             break;
  4414.     
  4415.           case FTP_START_READ_DIRECTORY:
  4416.             ce->status = net_start_ftp_read_dir(ce);
  4417.             break;
  4418.     
  4419.           case FTP_READ_DIRECTORY:
  4420.     
  4421.             ce->status = net_ftp_read_dir(ce);
  4422.             break;
  4423.  
  4424.           case FTP_PRINT_DIR:
  4425.             ce->status = net_ftp_print_dir(ce);
  4426.             break;
  4427.  
  4428.           case FTP_DONE:
  4429.             if(cd->stream)
  4430.                 COMPLETE_STREAM;
  4431.  
  4432.             /* Make sure that any previous cache entry is not locked anymore */
  4433.             NET_ChangeCacheFileLock(ce->URL_s, FALSE);
  4434.  
  4435.             /* don't close the control sock since we need 
  4436.              * it for connection caching
  4437.              */
  4438.             if(cd->dsock != NULL)
  4439.               {
  4440.                 NET_ClearReadSelect(ce->window_id, cd->dsock);
  4441.                 TRACEMSG(("Closing and clearing sock cd->dsock: %d", cd->dsock));
  4442.                 PR_Close(cd->dsock);
  4443.                 cd->dsock = NULL;
  4444.               }
  4445.  
  4446.             if(cd->listen_sock != NULL)
  4447.               {
  4448.                 TRACEMSG(("Closing and clearing sock cd->listen_sock: %d", cd->listen_sock));
  4449.                 NET_ClearReadSelect(ce->window_id, cd->listen_sock);
  4450.                 PR_Close(cd->listen_sock);
  4451.                 /* not counted as a connection */
  4452.               }
  4453.                 
  4454.             cd->next_state = FTP_FREE_DATA;
  4455.     
  4456.             cd->cc->busy = FALSE;  /* we are no longer using the connection */
  4457.             NET_ClearReadSelect(ce->window_id, cd->cc->csock);
  4458.  
  4459.             /* we can't reuse VMS control connections since they are screwed up
  4460.                 */
  4461.             if(cd->cc->server_type == FTP_VMS_TYPE)
  4462.               {
  4463.                 /* remove the connection from the cache list
  4464.                  * and free the data
  4465.                  */
  4466.                 XP_ListRemoveObject(connection_list, cd->cc);
  4467.                 FREEIF(cd->cc->hostname);
  4468.                 if(cd->cc->csock != NULL)
  4469.                   {
  4470.                     TRACEMSG(("Closing and clearing sock cd->cc->csock: %d", cd->cc->csock));
  4471.                     PR_Close(cd->cc->csock);
  4472.                     cd->cc->csock = NULL;
  4473.                   }
  4474.                 XP_FREE(cd->cc);
  4475.                 cd->cc = 0;
  4476.               }
  4477.  
  4478.             /* remove the cached connection from the total number
  4479.              * counted, since it is no longer active
  4480.              */
  4481.             NET_TotalNumberOfOpenConnections--;
  4482.  
  4483.             break;
  4484.     
  4485.           case FTP_ERROR_DONE:
  4486.             if(cd->sort_base)
  4487.               {
  4488.                  /* we have to do this to get it free'd
  4489.                   */
  4490.          if(cd->stream && (*cd->stream->is_write_ready)(cd->stream))
  4491.                      NET_PrintDirectory(&cd->sort_base, cd->stream, cd->path, ce->URL_s);
  4492.               }
  4493.  
  4494.             if(cd->stream && cd->stream->data_object)
  4495.             {
  4496.                 ABORT_STREAM(ce->status);
  4497.                 /* (ABORT_STREAM frees cd->stream->data_object:) */
  4498.                 cd->stream->data_object = NULL;
  4499.             }
  4500.  
  4501.             /* lock the object in cache if we can restart it */
  4502.             /* This way even if it's huge, we can restart. */
  4503.             TRACEMSG(("FTP aborting!  content_length = %ld, real_content_length = %ld\n", 
  4504.                       ce->URL_s->content_length, ce->URL_s->real_content_length));
  4505.             TRACEMSG(("  bytes_received = %ld\n", ce->bytes_received));
  4506.             if(ce->URL_s->server_can_do_restart)
  4507.                 NET_ChangeCacheFileLock(ce->URL_s, TRUE);
  4508.  
  4509.             if(cd->dsock != NULL)
  4510.               {
  4511.                 NET_ClearConnectSelect(ce->window_id, cd->dsock);
  4512.                 NET_ClearReadSelect(ce->window_id, cd->dsock);
  4513.                 NET_ClearDNSSelect(ce->window_id, cd->dsock);
  4514.  
  4515. #ifdef XP_WIN
  4516.                 if(cd->calling_netlib_all_the_time)
  4517.                 {
  4518.                     cd->calling_netlib_all_the_time = FALSE;
  4519.                     NET_ClearCallNetlibAllTheTime(ce->window_id, "mkftp");
  4520.                 }
  4521. #endif /* XP_WIN */
  4522.  
  4523.                 TRACEMSG(("Closing and clearing sock cd->dsock: %d", cd->dsock));
  4524.                 PR_Close(cd->dsock);
  4525.                 cd->dsock = NULL;
  4526.               }
  4527.  
  4528.             if(cd->file_to_post_fp)
  4529.               {
  4530.                 XP_FileClose(cd->file_to_post_fp);
  4531.                 cd->file_to_post_fp = 0;
  4532.               }
  4533.     
  4534.             if(cd->cc->csock != NULL)
  4535.               {
  4536.                 NET_ClearConnectSelect(ce->window_id, cd->cc->csock);
  4537.                 NET_ClearReadSelect(ce->window_id, cd->cc->csock);
  4538.                 NET_ClearDNSSelect(ce->window_id, cd->cc->csock);
  4539.                 TRACEMSG(("Closing and clearing sock cd->cc->csock: %d", cd->cc->csock));
  4540.                 PR_Close(cd->cc->csock);
  4541.                 cd->cc->csock = NULL;
  4542.                 NET_TotalNumberOfOpenConnections--;
  4543.               }
  4544.  
  4545.             if(cd->listen_sock != NULL)
  4546.               {
  4547.                 TRACEMSG(("Closing and clearing sock cd->listen_sock: %d", cd->listen_sock));
  4548.                 NET_ClearReadSelect(ce->window_id, cd->listen_sock);
  4549.                 PR_Close(cd->listen_sock);
  4550.                 /* not counted as a connection */
  4551.               }
  4552.  
  4553.  
  4554.             /* check if this connection came from the cache or if it was
  4555.              * a new connection.  If it was new lets start it over again
  4556.              */
  4557.             if(cd->cc->prev_cache 
  4558.                 && ce->status != MK_INTERRUPTED
  4559.                   && ce->status != MK_UNABLE_TO_CONVERT
  4560.                     && ce->status != MK_OUT_OF_MEMORY
  4561.                     && ce->status != MK_DISK_FULL)
  4562.               {
  4563.                 cd->cc->prev_cache = NO;
  4564.                 cd->next_state = FTP_CONTROL_CONNECT;
  4565.                 cd->pasv_mode = TRUE;  /* until we learn otherwise */
  4566.                 cd->pause_for_read = FALSE;
  4567.                 ce->status = 0;  /* keep going */
  4568.                 cd->cc->cur_mode = FTP_MODE_UNKNOWN;
  4569.               }
  4570.             else
  4571.               {
  4572.                 cd->next_state = FTP_FREE_DATA;
  4573.         
  4574.                 /* remove the connection from the cache list
  4575.                  * and free the data
  4576.                  */
  4577.                 XP_ListRemoveObject(connection_list, cd->cc);
  4578.                 FREEIF(cd->cc->hostname);
  4579.                 XP_FREE(cd->cc);
  4580.               }
  4581.     
  4582.             break;
  4583.     
  4584.           case FTP_FREE_DATA:
  4585.                 if(cd->calling_netlib_all_the_time)
  4586.                   {
  4587.                     NET_ClearCallNetlibAllTheTime(ce->window_id, "mkftp");
  4588.                     cd->calling_netlib_all_the_time = FALSE;
  4589.                   }
  4590.  
  4591.                 if(cd->destroy_graph_progress)
  4592.                     FE_GraphProgressDestroy(ce->window_id,     
  4593.                                             ce->URL_s,     
  4594.                                             cd->original_content_length,
  4595.                                             ce->bytes_received);
  4596. #ifdef EDITOR
  4597.                 if(cd->destroy_file_upload_progress_dialog) {
  4598.                     /* Convert from netlib errors to editor errors. */
  4599.                     ED_FileError error = ED_ERROR_NONE;
  4600.                     if ( ce->status < 0 )
  4601.                         error = ED_ERROR_PUBLISHING;
  4602.                     FE_SaveDialogDestroy(ce->window_id, error, ce->URL_s->post_data);
  4603.                     /*  FE_SaveDialogDestroy(ce->window_id, ce->status, cd->file_to_post); */
  4604.                 }
  4605. #endif /* EDITOR */
  4606.                 FREEIF(cd->cwd_message_buffer);
  4607.                 FREEIF(cd->login_message_buffer);
  4608.                 FREEIF(cd->data_buf)
  4609.                 FREEIF(cd->return_msg)
  4610.                 FREEIF(cd->data_con_address)
  4611.                 FREEIF(cd->filename)
  4612.                 FREEIF(cd->path)
  4613.                 FREEIF(cd->username)
  4614.                 FREEIF(cd->password)
  4615.                 FREEIF(cd->stream);  /* don't forget the stream */
  4616.                 FREEIF(cd->output_buffer);
  4617.                 if(cd->tcp_con_data)
  4618.                     NET_FreeTCPConData(cd->tcp_con_data);
  4619.                 
  4620.  
  4621.                 XP_FREE(cd);
  4622.  
  4623.                 /* gate the maximum number of cached connections
  4624.                   * to one
  4625.                  */
  4626.                 if(XP_ListCount(connection_list) > 1)
  4627.                   {
  4628.                     XP_List * list_ptr = connection_list->next;
  4629.                     FTPConnection * con;
  4630.         
  4631.                     while(list_ptr)
  4632.                       {
  4633.                         con = (FTPConnection *) list_ptr->object;
  4634.                         list_ptr = list_ptr->next;
  4635.                         if(!con->busy)
  4636.                           {
  4637.                             XP_ListRemoveObject(connection_list, con);
  4638.  
  4639.                             if(con->csock != NULL)
  4640.                               {
  4641.                                 PR_Close(con->csock);
  4642.                                 /* dont reduce the number of
  4643.                                  * open connections since cached ones
  4644.                                   * dont count as active
  4645.                                  */
  4646.                               }
  4647.                             FREEIF(con->hostname);
  4648.                             XP_FREE(con);
  4649.                             break;
  4650.                           }
  4651.                       }
  4652.                   }
  4653.  
  4654.                 return(-1);
  4655.     
  4656.               default:
  4657.                 TRACEMSG(("Bad state in ProcessFTP\n"));
  4658.                 return(-1);
  4659.          } /* end switch */
  4660.     
  4661.        if(ce->status < 0 && cd->next_state != FTP_FREE_DATA)
  4662.           {
  4663.             cd->pause_for_read = FALSE;
  4664.             cd->next_state = FTP_ERROR_DONE;
  4665.           }
  4666.     } /* end while */
  4667.  
  4668.     return(ce->status);
  4669. }
  4670.     
  4671. /* abort the connection in progress
  4672.  */
  4673. PRIVATE int32
  4674. net_InterruptFTP(ActiveEntry * ce)
  4675. {
  4676.     FTPConData * cd = (FTPConData *)ce->con_data;
  4677.  
  4678.     cd->next_state = FTP_ERROR_DONE;
  4679.     ce->status = MK_INTERRUPTED;
  4680.     cd->cc->prev_cache = FALSE;  /* to keep it from reconnecting */
  4681.  
  4682.     return(net_ProcessFTP(ce));
  4683. }
  4684.  
  4685. /* Free any connections or memory that are cached
  4686.  */
  4687. PRIVATE void
  4688. net_CleanupFTP(void)
  4689. {
  4690.     if(connection_list)
  4691.       {
  4692.         FTPConnection * tmp_con;
  4693.  
  4694.         while((tmp_con = (FTPConnection *)XP_ListRemoveTopObject(connection_list)) != NULL)
  4695.           {
  4696.             if(!tmp_con->busy)
  4697.               {
  4698.                 FREE(tmp_con->hostname);       /* hostname string */
  4699.                 if(tmp_con->csock != NULL)
  4700.                   {
  4701.                     PR_Close(tmp_con->csock);      /* control socket */
  4702.                     tmp_con->csock = NULL;
  4703.                     /* don't count cached connections as open ones
  4704.                      */
  4705.                   }
  4706.                 FREE(tmp_con);
  4707.               }
  4708.           }
  4709.       }
  4710.  
  4711.     return;
  4712. }
  4713.  
  4714.  
  4715. MODULE_PRIVATE void
  4716. NET_InitFTPProtocol(void)
  4717. {
  4718.     static NET_ProtoImpl ftp_proto_impl;
  4719.  
  4720.     ftp_proto_impl.init = net_FTPLoad;
  4721.     ftp_proto_impl.process = net_ProcessFTP;
  4722.     ftp_proto_impl.interrupt = net_InterruptFTP;
  4723.     ftp_proto_impl.cleanup = net_CleanupFTP;
  4724.  
  4725.     NET_RegisterProtocolImplementation(&ftp_proto_impl, FTP_TYPE_URL);
  4726. }
  4727.