home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / common / ftp.cpp < prev    next >
C/C++ Source or Header  |  2002-04-12  |  26KB  |  930 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        ftp.cpp
  3. // Purpose:     FTP protocol
  4. // Author:      Guilhem Lavaux
  5. // Modified by: Mark Johnson, wxWindows@mj10777.de
  6. //              20000917 : RmDir, GetLastResult, GetList
  7. //              Vadim Zeitlin (numerous fixes and rewrites to all part of the
  8. //              code, support ASCII/Binary modes, better error reporting, more
  9. //              robust Abort(), support for arbitrary FTP commands, ...)
  10. // Created:     07/07/1997
  11. // RCS-ID:      $Id: ftp.cpp,v 1.43 2002/04/12 10:23:55 VZ Exp $
  12. // Copyright:   (c) 1997, 1998 Guilhem Lavaux
  13. // Licence:     wxWindows license
  14. /////////////////////////////////////////////////////////////////////////////
  15.  
  16. // ============================================================================
  17. // declarations
  18. // ============================================================================
  19.  
  20. #ifdef __GNUG__
  21.   #pragma implementation "ftp.h"
  22. #endif
  23.  
  24. // ----------------------------------------------------------------------------
  25. // headers
  26. // ----------------------------------------------------------------------------
  27.  
  28. // For compilers that support precompilation, includes "wx.h".
  29. #include "wx/wxprec.h"
  30.  
  31. #ifdef __BORLANDC__
  32.   #pragma hdrstop
  33. #endif
  34.  
  35. #if wxUSE_PROTOCOL_FTP
  36.  
  37. #ifndef WX_PRECOMP
  38.     #include <stdlib.h>
  39.     #include "wx/string.h"
  40.     #include "wx/utils.h"
  41.     #include "wx/log.h"
  42.     #include "wx/intl.h"
  43. #endif // WX_PRECOMP
  44.  
  45. #include "wx/sckaddr.h"
  46. #include "wx/socket.h"
  47. #include "wx/url.h"
  48. #include "wx/sckstrm.h"
  49. #include "wx/protocol/protocol.h"
  50. #include "wx/protocol/ftp.h"
  51.  
  52. #if defined(__WXMAC__)
  53.     #include "wx/mac/macsock.h"
  54. #endif
  55.  
  56. #ifndef __MWERKS__
  57.     #include <memory.h>
  58. #endif
  59.  
  60. // ----------------------------------------------------------------------------
  61. // constants
  62. // ----------------------------------------------------------------------------
  63.  
  64. // the length of FTP status code (3 digits)
  65. static const size_t LEN_CODE = 3;
  66.  
  67. // ----------------------------------------------------------------------------
  68. // macros
  69. // ----------------------------------------------------------------------------
  70.  
  71. IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
  72. IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), TRUE)
  73.  
  74. // ============================================================================
  75. // implementation
  76. // ============================================================================
  77.  
  78. // ----------------------------------------------------------------------------
  79. // wxFTP constructor and destructor
  80. // ----------------------------------------------------------------------------
  81.  
  82. wxFTP::wxFTP()
  83. {
  84.     m_lastError = wxPROTO_NOERR;
  85.     m_streaming = FALSE;
  86.     m_currentTransfermode = NONE;
  87.  
  88.     m_user = wxT("anonymous");
  89.     m_passwd << wxGetUserId() << wxT('@') << wxGetFullHostName();
  90.  
  91.     SetNotify(0);
  92.     SetFlags(wxSOCKET_NONE);
  93. }
  94.  
  95. wxFTP::~wxFTP()
  96. {
  97.     if ( m_streaming )
  98.     {
  99.         (void)Abort();
  100.     }
  101.  
  102.     Close();
  103. }
  104.  
  105. // ----------------------------------------------------------------------------
  106. // wxFTP connect and login methods
  107. // ----------------------------------------------------------------------------
  108.  
  109. bool wxFTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
  110. {
  111.     if ( !wxProtocol::Connect(addr) )
  112.     {
  113.         m_lastError = wxPROTO_NETERR;
  114.         return FALSE;
  115.     }
  116.  
  117.     if ( !m_user )
  118.     {
  119.         m_lastError = wxPROTO_CONNERR;
  120.         return FALSE;
  121.     }
  122.  
  123.     // we should have 220 welcome message
  124.     if ( !CheckResult('2') )
  125.     {
  126.         Close();
  127.         return FALSE;
  128.     }
  129.  
  130.     wxString command;
  131.     command.Printf(wxT("USER %s"), m_user.c_str());
  132.     char rc = SendCommand(command);
  133.     if ( rc == '2' )
  134.     {
  135.         // 230 return: user accepted without password
  136.         return TRUE;
  137.     }
  138.  
  139.     if ( rc != '3' )
  140.     {
  141.         Close();
  142.         return FALSE;
  143.     }
  144.  
  145.     command.Printf(wxT("PASS %s"), m_passwd.c_str());
  146.     if ( !CheckCommand(command, '2') )
  147.     {
  148.         Close();
  149.         return FALSE;
  150.     }
  151.  
  152.     return TRUE;
  153. }
  154.  
  155. bool wxFTP::Connect(const wxString& host)
  156. {
  157.     wxIPV4address addr;
  158.     addr.Hostname(host);
  159.     addr.Service(wxT("ftp"));
  160.  
  161.     return Connect(addr);
  162. }
  163.  
  164. bool wxFTP::Close()
  165. {
  166.     if ( m_streaming )
  167.     {
  168.         m_lastError = wxPROTO_STREAMING;
  169.         return FALSE;
  170.     }
  171.  
  172.     if ( IsConnected() )
  173.     {
  174.         if ( !CheckCommand(wxT("QUIT"), '2') )
  175.         {
  176.             wxLogDebug(_T("Failed to close connection gracefully."));
  177.         }
  178.     }
  179.  
  180.     return wxSocketClient::Close();
  181. }
  182.  
  183. // ============================================================================
  184. // low level methods
  185. // ============================================================================
  186.  
  187. // ----------------------------------------------------------------------------
  188. // Send command to FTP server
  189. // ----------------------------------------------------------------------------
  190.  
  191. char wxFTP::SendCommand(const wxString& command)
  192. {
  193.     if ( m_streaming )
  194.     {
  195.         m_lastError = wxPROTO_STREAMING;
  196.         return 0;
  197.     }
  198.  
  199.     wxString tmp_str = command + wxT("\r\n");
  200.     const wxWX2MBbuf tmp_buf = tmp_str.mb_str();
  201.     if ( Write(wxMBSTRINGCAST tmp_buf, strlen(tmp_buf)).Error())
  202.     {
  203.         m_lastError = wxPROTO_NETERR;
  204.         return 0;
  205.     }
  206.  
  207. #ifdef __WXDEBUG__
  208.     // don't show the passwords in the logs (even in debug ones)
  209.     wxString cmd, password;
  210.     if ( command.Upper().StartsWith(_T("PASS "), &password) )
  211.     {
  212.         cmd << _T("PASS ") << wxString(_T('*'), password.length());
  213.     }
  214.     else
  215.     {
  216.         cmd = command;
  217.     }
  218.  
  219.     wxLogTrace(FTP_TRACE_MASK, _T("==> %s"), cmd.c_str());
  220. #endif // __WXDEBUG__
  221.  
  222.     return GetResult();
  223. }
  224.  
  225. // ----------------------------------------------------------------------------
  226. // Recieve servers reply
  227. // ----------------------------------------------------------------------------
  228.  
  229. char wxFTP::GetResult()
  230. {
  231.     wxString code;
  232.  
  233.     // m_lastResult will contain the entire server response, possibly on
  234.     // multiple lines
  235.     m_lastResult.clear();
  236.  
  237.     // we handle multiline replies here according to RFC 959: it says that a
  238.     // reply may either be on 1 line of the form "xyz ..." or on several lines
  239.     // in whuch case it looks like
  240.     //      xyz-...
  241.     //      ...
  242.     //      xyz ...
  243.     // and the intermeidate lines may start with xyz or not
  244.     bool badReply = FALSE;
  245.     bool firstLine = TRUE;
  246.     bool endOfReply = FALSE;
  247.     while ( !endOfReply && !badReply )
  248.     {
  249.         wxString line;
  250.         m_lastError = ReadLine(line);
  251.         if ( m_lastError )
  252.             return 0;
  253.  
  254.         if ( !m_lastResult.empty() )
  255.         {
  256.             // separate from last line
  257.             m_lastResult += _T('\n');
  258.         }
  259.  
  260.         m_lastResult += line;
  261.  
  262.         // unless this is an intermediate line of a multiline reply, it must
  263.         // contain the code in the beginning and '-' or ' ' following it
  264.         if ( line.Len() < LEN_CODE + 1 )
  265.         {
  266.             if ( firstLine )
  267.             {
  268.                 badReply = TRUE;
  269.             }
  270.             else
  271.             {
  272.                 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
  273.                            code.c_str(), line.c_str());
  274.             }
  275.         }
  276.         else // line has at least 4 chars
  277.         {
  278.             // this is the char which tells us what we're dealing with
  279.             wxChar chMarker = line.GetChar(LEN_CODE);
  280.  
  281.             if ( firstLine )
  282.             {
  283.                 code = wxString(line, LEN_CODE);
  284.                 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
  285.                            code.c_str(), line.c_str() + LEN_CODE + 1);
  286.  
  287.                 switch ( chMarker )
  288.                 {
  289.                     case _T(' '):
  290.                         endOfReply = TRUE;
  291.                         break;
  292.  
  293.                     case _T('-'):
  294.                         firstLine = FALSE;
  295.                         break;
  296.  
  297.                     default:
  298.                         // unexpected
  299.                         badReply = TRUE;
  300.                 }
  301.             }
  302.             else // subsequent line of multiline reply
  303.             {
  304.                 if ( wxStrncmp(line, code, LEN_CODE) == 0 )
  305.                 {
  306.                     if ( chMarker == _T(' ') )
  307.                     {
  308.                         endOfReply = TRUE;
  309.                     }
  310.  
  311.                     wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
  312.                                code.c_str(), line.c_str() + LEN_CODE + 1);
  313.                 }
  314.                 else
  315.                 {
  316.                     // just part of reply
  317.                     wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
  318.                                code.c_str(), line.c_str());
  319.                 }
  320.             }
  321.         }
  322.     }
  323.  
  324.     if ( badReply )
  325.     {
  326.         wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
  327.                    m_lastResult.c_str());
  328.  
  329.         m_lastError = wxPROTO_PROTERR;
  330.  
  331.         return 0;
  332.     }
  333.  
  334.     // if we got here we must have a non empty code string
  335.     return code[0u];
  336. }
  337.  
  338. // ----------------------------------------------------------------------------
  339. // wxFTP simple commands
  340. // ----------------------------------------------------------------------------
  341.  
  342. bool wxFTP::SetTransferMode(TransferMode transferMode)
  343. {
  344.     if ( transferMode == m_currentTransfermode )
  345.     {
  346.         // nothing to do
  347.         return TRUE;
  348.     }
  349.  
  350.     wxString mode;
  351.     switch ( transferMode )
  352.     {
  353.         default:
  354.             wxFAIL_MSG(_T("unknown FTP transfer mode"));
  355.             // fall through
  356.  
  357.         case BINARY:
  358.             mode = _T('I');
  359.             break;
  360.  
  361.         case ASCII:
  362.             mode = _T('A');
  363.             break;
  364.     }
  365.  
  366.     if ( !DoSimpleCommand(_T("TYPE"), mode) )
  367.     {
  368.         wxLogError(_("Failed to set FTP transfer mode to %s."), (const wxChar*)
  369.                    (transferMode == ASCII ? _("ASCII") : _("binary")));
  370.  
  371.         return FALSE;
  372.     }
  373.  
  374.     // If we get here the operation has been succesfully completed
  375.     // Set the status-member
  376.     m_currentTransfermode = transferMode;
  377.  
  378.     return TRUE;
  379. }
  380.  
  381. bool wxFTP::DoSimpleCommand(const wxChar *command, const wxString& arg)
  382. {
  383.     wxString fullcmd = command;
  384.     if ( !arg.empty() )
  385.     {
  386.         fullcmd << _T(' ') << arg;
  387.     }
  388.  
  389.     if ( !CheckCommand(fullcmd, '2') )
  390.     {
  391.         wxLogDebug(_T("FTP command '%s' failed."), fullcmd.c_str());
  392.  
  393.         return FALSE;
  394.     }
  395.  
  396.     return TRUE;
  397. }
  398.  
  399. bool wxFTP::ChDir(const wxString& dir)
  400. {
  401.     // some servers might not understand ".." if they use different directory
  402.     // tree conventions, but they always understand CDUP - should we use it if
  403.     // dir == ".."? OTOH, do such servers (still) exist?
  404.  
  405.     return DoSimpleCommand(_T("CWD"), dir);
  406. }
  407.  
  408. bool wxFTP::MkDir(const wxString& dir)
  409. {
  410.     return DoSimpleCommand(_T("MKD"), dir);
  411. }
  412.  
  413. bool wxFTP::RmDir(const wxString& dir)
  414. {
  415.     return DoSimpleCommand(_T("RMD"), dir);
  416. }
  417.  
  418. wxString wxFTP::Pwd()
  419. {
  420.     wxString path;
  421.  
  422.     if ( CheckCommand(wxT("PWD"), '2') )
  423.     {
  424.         // the result is at least that long if CheckCommand() succeeded
  425.         const wxChar *p = m_lastResult.c_str() + LEN_CODE + 1;
  426.         if ( *p != _T('"') )
  427.         {
  428.             wxLogDebug(_T("Missing starting quote in reply for PWD: %s"), p);
  429.         }
  430.         else
  431.         {
  432.             for ( p++; *p; p++ )
  433.             {
  434.                 if ( *p == _T('"') )
  435.                 {
  436.                     // check if the quote is doubled
  437.                     p++;
  438.                     if ( !*p || *p != _T('"') )
  439.                     {
  440.                         // no, this is the end
  441.                         break;
  442.                     }
  443.                     //else: yes, it is: this is an embedded quote in the
  444.                     //      filename, treat as normal char
  445.                 }
  446.  
  447.                 path += *p;
  448.             }
  449.  
  450.             if ( !*p )
  451.             {
  452.                 wxLogDebug(_T("Missing ending quote in reply for PWD: %s"),
  453.                            m_lastResult.c_str() + LEN_CODE + 1);
  454.             }
  455.         }
  456.     }
  457.     else
  458.     {
  459.         wxLogDebug(_T("FTP PWD command failed."));
  460.     }
  461.  
  462.     return path;
  463. }
  464.  
  465. bool wxFTP::Rename(const wxString& src, const wxString& dst)
  466. {
  467.     wxString str;
  468.  
  469.     str = wxT("RNFR ") + src;
  470.     if ( !CheckCommand(str, '3') )
  471.         return FALSE;
  472.  
  473.     str = wxT("RNTO ") + dst;
  474.  
  475.     return CheckCommand(str, '2');
  476. }
  477.  
  478. bool wxFTP::RmFile(const wxString& path)
  479. {
  480.     wxString str;
  481.     str = wxT("DELE ") + path;
  482.  
  483.     return CheckCommand(str, '2');
  484. }
  485.  
  486. // ----------------------------------------------------------------------------
  487. // wxFTP download and upload
  488. // ----------------------------------------------------------------------------
  489.  
  490. class wxInputFTPStream : public wxSocketInputStream
  491. {
  492. public:
  493.     wxInputFTPStream(wxFTP *ftp, wxSocketBase *sock)
  494.         : wxSocketInputStream(*sock)
  495.     {
  496.         m_ftp = ftp;
  497.  
  498.         // FIXME make the timeout configurable
  499.  
  500.         // set a shorter than default timeout
  501.         m_i_socket->SetTimeout(60); // 1 minute
  502.     }
  503.  
  504.     size_t GetSize() const { return m_ftpsize; }
  505.  
  506.     virtual ~wxInputFTPStream()
  507.     {
  508.         delete m_i_socket;
  509.  
  510.         if ( LastError() == wxStream_NOERROR )
  511.         {
  512.             // wait for "226 transfer completed"
  513.             m_ftp->CheckResult('2');
  514.  
  515.             m_ftp->m_streaming = FALSE;
  516.         }
  517.         else
  518.         {
  519.             m_ftp->Abort();
  520.         }
  521.  
  522.         // delete m_i_socket; // moved to top of destructor to accomodate wu-FTPd >= 2.6.0
  523.     }
  524.  
  525.     wxFTP *m_ftp;
  526.     size_t m_ftpsize;
  527. };
  528.  
  529. class wxOutputFTPStream : public wxSocketOutputStream
  530. {
  531. public:
  532.     wxOutputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
  533.         : wxSocketOutputStream(*sock), m_ftp(ftp_clt)
  534.     {
  535.     }
  536.  
  537.     virtual ~wxOutputFTPStream(void)
  538.     {
  539.         if ( IsOk() )
  540.         {
  541.             // close data connection first, this will generate "transfer
  542.             // completed" reply
  543.             delete m_o_socket;
  544.  
  545.             // read this reply
  546.             m_ftp->CheckResult('2');
  547.  
  548.             m_ftp->m_streaming = FALSE;
  549.         }
  550.         else
  551.         {
  552.             // abort data connection first
  553.             m_ftp->Abort();
  554.  
  555.             // and close it after
  556.             delete m_o_socket;
  557.         }
  558.     }
  559.  
  560.     wxFTP *m_ftp;
  561. };
  562.  
  563. wxSocketClient *wxFTP::GetPort()
  564. {
  565.     int a[6];
  566.  
  567.     if ( !DoSimpleCommand(_T("PASV")) )
  568.     {
  569.         wxLogError(_("The FTP server doesn't support passive mode."));
  570.  
  571.         return NULL;
  572.     }
  573.  
  574.     const wxChar *addrStart = wxStrchr(m_lastResult, _T('('));
  575.     if ( !addrStart )
  576.     {
  577.         m_lastError = wxPROTO_PROTERR;
  578.  
  579.         return NULL;
  580.     }
  581.  
  582.     const wxChar *addrEnd = wxStrchr(addrStart, _T(')'));
  583.     if ( !addrEnd )
  584.     {
  585.         m_lastError = wxPROTO_PROTERR;
  586.  
  587.         return NULL;
  588.     }
  589.  
  590.     wxString straddr(addrStart + 1, addrEnd);
  591.  
  592.     wxSscanf(straddr, wxT("%d,%d,%d,%d,%d,%d"),
  593.              &a[2],&a[3],&a[4],&a[5],&a[0],&a[1]);
  594.  
  595.     wxUint32 hostaddr = (wxUint16)a[5] << 24 |
  596.                         (wxUint16)a[4] << 16 |
  597.                         (wxUint16)a[3] << 8 |
  598.                         a[2];
  599.     wxUint16 port = (wxUint16)a[0] << 8 | a[1];
  600.  
  601.     wxIPV4address addr;
  602.     addr.Hostname(hostaddr);
  603.     addr.Service(port);
  604.  
  605.     wxSocketClient *client = new wxSocketClient();
  606.     if ( !client->Connect(addr) )
  607.     {
  608.         delete client;
  609.         return NULL;
  610.     }
  611.  
  612.     client->Notify(FALSE);
  613.  
  614.     return client;
  615. }
  616.  
  617. bool wxFTP::Abort()
  618. {
  619.     if ( !m_streaming )
  620.         return TRUE;
  621.  
  622.     m_streaming = FALSE;
  623.     if ( !CheckCommand(wxT("ABOR"), '4') )
  624.         return FALSE;
  625.  
  626.     return CheckResult('2');
  627. }
  628.  
  629. wxInputStream *wxFTP::GetInputStream(const wxString& path)
  630. {
  631.     int pos_size;
  632.     wxInputFTPStream *in_stream;
  633.  
  634.     if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
  635.         return NULL;
  636.  
  637.     wxSocketClient *sock = GetPort();
  638.  
  639.     if ( !sock )
  640.     {
  641.         m_lastError = wxPROTO_NETERR;
  642.         return NULL;
  643.     }
  644.  
  645.     wxString tmp_str = wxT("RETR ") + wxURL::ConvertFromURI(path);
  646.     if ( !CheckCommand(tmp_str, '1') )
  647.         return NULL;
  648.  
  649.     m_streaming = TRUE;
  650.  
  651.     in_stream = new wxInputFTPStream(this, sock);
  652.  
  653.     pos_size = m_lastResult.Index(wxT('('));
  654.     if ( pos_size != wxNOT_FOUND )
  655.     {
  656.         wxString str_size = m_lastResult(pos_size+1, m_lastResult.Index(wxT(')'))-1);
  657.  
  658.         in_stream->m_ftpsize = wxAtoi(WXSTRINGCAST str_size);
  659.     }
  660.  
  661.     sock->SetFlags(wxSOCKET_WAITALL);
  662.  
  663.     return in_stream;
  664. }
  665.  
  666. wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
  667. {
  668.     if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
  669.         return NULL;
  670.  
  671.     wxSocketClient *sock = GetPort();
  672.  
  673.     wxString tmp_str = wxT("STOR ") + path;
  674.     if ( !CheckCommand(tmp_str, '1') )
  675.         return NULL;
  676.  
  677.     m_streaming = TRUE;
  678.  
  679.     return new wxOutputFTPStream(this, sock);
  680. }
  681.  
  682. // ----------------------------------------------------------------------------
  683. // FTP directory listing
  684. // ----------------------------------------------------------------------------
  685.  
  686. bool wxFTP::GetList(wxArrayString& files,
  687.                     const wxString& wildcard,
  688.                     bool details)
  689. {
  690.     wxSocketBase *sock = GetPort();
  691.     if (!sock)
  692.         return FALSE;
  693.  
  694.     // NLST : List of Filenames (including Directory's !)
  695.     // LIST : depending on BS of FTP-Server
  696.     //        - Unix    : result like "ls" command
  697.     //        - Windows : like "dir" command
  698.     //        - others  : ?
  699.     wxString line(details ? _T("LIST") : _T("NLST"));
  700.     if ( !!wildcard )
  701.     {
  702.         line << _T(' ') << wildcard;
  703.     }
  704.  
  705.     if (!CheckCommand(line, '1'))
  706.     {
  707.         return FALSE;
  708.     }
  709.     files.Empty();
  710.     while ( ReadLine(sock, line) == wxPROTO_NOERR )
  711.     {
  712.         files.Add(line);
  713.     }
  714.     delete sock;
  715.  
  716.     // the file list should be terminated by "226 Transfer complete""
  717.     if ( !CheckResult('2') )
  718.         return FALSE;
  719.  
  720.     return TRUE;
  721. }
  722.  
  723. bool wxFTP::FileExists(const wxString& fileName)
  724. {
  725.     // This function checks if the file specified in fileName exists in the
  726.     // current dir. It does so by simply doing an NLST (via GetList).
  727.     // If this succeeds (and the list is not empty) the file exists.
  728.  
  729.     bool retval = FALSE;
  730.     wxArrayString fileList;
  731.  
  732.     if ( GetList(fileList, fileName, FALSE) )
  733.     {
  734.         // Some ftp-servers (Ipswitch WS_FTP Server 1.0.5 does this)
  735.         // displays this behaviour when queried on a non-existing file:
  736.         // NLST this_file_does_not_exist
  737.         // 150 Opening ASCII data connection for directory listing
  738.         // (no data transferred)
  739.         // 226 Transfer complete
  740.         // Here wxFTP::GetList(...) will succeed but it will return an empty
  741.         // list.
  742.         retval = !fileList.IsEmpty();
  743.     }
  744.  
  745.     return retval;
  746. }
  747.  
  748. // ----------------------------------------------------------------------------
  749. // FTP GetSize
  750. // ----------------------------------------------------------------------------
  751.  
  752. int wxFTP::GetFileSize(const wxString& fileName)
  753. {
  754.     // return the filesize of the given file if possible
  755.     // return -1 otherwise (predominantly if file doesn't exist
  756.     // in current dir)
  757.  
  758.     int filesize = -1;
  759.  
  760.     // Check for existance of file via wxFTP::FileExists(...)
  761.     if ( FileExists(fileName) )
  762.     {
  763.         wxString command;
  764.  
  765.         // First try "SIZE" command using BINARY(IMAGE) transfermode
  766.         // Especially UNIX ftp-servers distinguish between the different
  767.         // transfermodes and reports different filesizes accordingly.
  768.         // The BINARY size is the interesting one: How much memory
  769.         // will we need to hold this file?
  770.         TransferMode oldTransfermode = m_currentTransfermode;
  771.         SetTransferMode(BINARY);
  772.         command << _T("SIZE ") << fileName;
  773.  
  774.         bool ok = CheckCommand(command, '2');
  775.  
  776.         if ( ok )
  777.         {
  778.             // The answer should be one line: "213 <filesize>\n"
  779.             // 213 is File Status (STD9)
  780.             // "SIZE" is not described anywhere..? It works on most servers
  781.             int statuscode;
  782.             if ( wxSscanf(GetLastResult().c_str(), _T("%i %i"),
  783.                           &statuscode, &filesize) == 2 )
  784.             {
  785.                 // We've gotten a good reply.
  786.                 ok = TRUE;
  787.             }
  788.             else
  789.             {
  790.                 // Something bad happened.. A "2yz" reply with no size
  791.                 // Fallback
  792.                 ok = FALSE;
  793.             }
  794.         }
  795.  
  796.         // Set transfermode back to the original. Only the "SIZE"-command
  797.         // is dependant on transfermode
  798.         if ( oldTransfermode != NONE )
  799.         {
  800.             SetTransferMode(oldTransfermode);
  801.         }
  802.  
  803.         if ( !ok ) // this is not a direct else clause.. The size command might return an invalid "2yz" reply
  804.         {
  805.             // The server didn't understand the "SIZE"-command or it
  806.             // returned an invalid reply.
  807.             // We now try to get details for the file with a "LIST"-command
  808.             // and then parse the output from there..
  809.             wxArrayString fileList;
  810.             if ( GetList(fileList, fileName, TRUE) )
  811.             {
  812.                 if ( !fileList.IsEmpty() )
  813.                 {
  814.                     // We _should_ only get one line in return, but just to be
  815.                     // safe we run through the line(s) returned and look for a
  816.                     // substring containing the name we are looking for. We
  817.                     // stop the iteration at the first occurrence of the
  818.                     // filename. The search is not case-sensitive.
  819.                     bool foundIt = FALSE;
  820.  
  821.                     size_t i;
  822.                     for ( i = 0; !foundIt && i < fileList.Count(); i++ )
  823.                     {
  824.                         foundIt = fileList[i].Upper().Contains(fileName.Upper());
  825.                     }
  826.  
  827.                     if ( foundIt )
  828.                     {
  829.                         // The index i points to the first occurrence of
  830.                         // fileName in the array Now we have to find out what
  831.                         // format the LIST has returned. There are two
  832.                         // "schools": Unix-like
  833.                         //
  834.                         // '-rw-rw-rw- owner group size month day time filename'
  835.                         //
  836.                         // or Windows-like
  837.                         //
  838.                         // 'date size filename'
  839.  
  840.                         // check if the first character is '-'. This would
  841.                         // indicate Unix-style (this also limits this function
  842.                         // to searching for files, not directories)
  843.                         if ( fileList[i].Mid(0, 1) == _T("-") )
  844.                         {
  845.  
  846.                             if ( wxSscanf(fileList[i].c_str(),
  847.                                           _T("%*s %*s %*s %*s %i %*s %*s %*s %*s"),
  848.                                           &filesize) == 9 )
  849.                             {
  850.                                 // We've gotten a good response
  851.                                 ok = TRUE;
  852.                             }
  853.                             else
  854.                             {
  855.                                 // Hmm... Invalid response
  856.                                 wxLogTrace(FTP_TRACE_MASK,
  857.                                            _T("Invalid LIST response"));
  858.                             }
  859.                         }
  860.                         else // Windows-style response (?)
  861.                         {
  862.                             if ( wxSscanf(fileList[i].c_str(),
  863.                                           _T("%*s %*s %i %*s"),
  864.                                           &filesize) == 4 )
  865.                             {
  866.                                 // valid response
  867.                                 ok = TRUE;
  868.                             }
  869.                             else
  870.                             {
  871.                                 // something bad happened..?
  872.                                 wxLogTrace(FTP_TRACE_MASK,
  873.                                            _T("Invalid or unknown LIST response"));
  874.                             }
  875.                         }
  876.                     }
  877.                 }
  878.             }
  879.         }
  880.     }
  881.  
  882.     // filesize might still be -1 when exiting
  883.     return filesize;
  884. }
  885.  
  886.  
  887. #if WXWIN_COMPATIBILITY_2
  888. // deprecated
  889. wxList *wxFTP::GetList(const wxString& wildcard, bool details)
  890. {
  891.  wxSocketBase *sock = GetPort();
  892.  if (!sock)
  893.   return FALSE;
  894.  wxList *file_list = new wxList;
  895.  wxString line;
  896.  // NLST : List of Filenames (including Directory's !)
  897.  // LIST : depending on BS of FTP-Server
  898.  //        - Unix    : result like "ls" command
  899.  //        - Windows : like "dir" command
  900.  //        - others  : ?
  901.  if (!details)
  902.   line = _T("NLST");   // Default
  903.  else
  904.   line = _T("LIST");
  905.  if (!wildcard.IsNull())
  906.   line += wildcard;
  907.  if (!CheckCommand(line, '1'))
  908.  {
  909.   delete sock;
  910.   delete file_list;
  911.   return NULL;
  912.  }
  913.  while (GetLine(sock, line) == wxPROTO_NOERR)
  914.  {
  915.   file_list->Append((wxObject *)(new wxString(line)));
  916.  }
  917.  if (!CheckResult('2'))
  918.  {
  919.   delete sock;
  920.   file_list->DeleteContents(TRUE);
  921.   delete file_list;
  922.   return NULL;
  923.  }
  924.  return file_list;
  925. }
  926. #endif // WXWIN_COMPATIBILITY_2
  927.  
  928. #endif // wxUSE_PROTOCOL_FTP
  929.  
  930.