]> git.saurik.com Git - wxWidgets.git/commitdiff
1. wxFTP works (somehow)
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 15 Mar 2000 17:16:35 +0000 (17:16 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 15 Mar 2000 17:16:35 +0000 (17:16 +0000)
2. wxSocket/GSocket wxBase support for Unix

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@6738 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
docs/latex/wx/ftp.tex
include/wx/protocol/ftp.h
include/wx/protocol/protocol.h
samples/console/console.cpp
src/common/ftp.cpp
src/common/protocol.cpp
src/files.lst
src/unix/gsocket.c

index 75b98fdd272c579ea227299e6cdc8b76d440399c..2409dab2be0cfcfbaac5f376d87cb39cd637de10 100644 (file)
@@ -15,6 +15,7 @@ File hierarchy:
 
 wxBase:
 
+- wxSocket support
 - wxDateTime replaces and extends old wxDate and wxTime classes (still
   available but strongly deprecated) with many new features
 - wxLongLong class provides support for (signed) 64 bit integers
index 07d711056ee837adb9d41620227126d473f05e0a..f49796142e46b68ad32f095c9e787438dfa4a7ae 100644 (file)
@@ -121,11 +121,11 @@ to specify a user and a password.
 % ----------------------------------------------------------------------------
 \membersection{wxFTP::GetList}
 
-\func{wxList *}{GetList}{\param{const wxString\&}{ wildcard}}
+\func{bool}{GetList}{\param{wxArrayString\& }{files}, \param{const wxString\&}{ wildcard = ""}}
 
 The GetList function is quite low-level. It returns the list of the files in
 the current directory. The list can be filtered using the {\it wildcard} string.
-If {\it wildcard} is a NULL string, it will return all files in directory.
+If {\it wildcard} is empty (default), it will return all files in directory.
 
 The form of the list can change from one peer system to another. For example,
 for a UNIX peer system, it will look like this:
@@ -146,7 +146,8 @@ winamp~1 exe    520196 02-25-1999  19:28  winamp204.exe
         1 file(s)           520 196 bytes
 \end{verbatim}
 
-The list is a string list and one node corresponds to a line sent by the peer.
+Return value: TRUE if the file list was successfully retrieved, FALSE
+otherwise.
 
 % ----------------------------------------------------------------------------
 
index 9f71992f2b727531e6f945b703334c0b0a4f3288..459f2bfd6cd353278e745f17f2e1a48921bbcd0b 100644 (file)
@@ -8,6 +8,7 @@
 // Copyright:   (c) 1997, 1998 Guilhem Lavaux
 // Licence:     wxWindows license
 /////////////////////////////////////////////////////////////////////////////
+
 #ifndef __WX_FTP_H__
 #define __WX_FTP_H__
 
 #include "wx/protocol/protocol.h"
 #include "wx/url.h"
 
-class WXDLLEXPORT wxFTP : public wxProtocol {
-  DECLARE_DYNAMIC_CLASS(wxFTP)
-  DECLARE_PROTOCOL(wxFTP)
+class WXDLLEXPORT wxFTP : public wxProtocol
+{
 public:
   typedef enum { ASCII, BINARY } wxFTPmode;
 
   wxFTP();
-  ~wxFTP();
+  virtual ~wxFTP();
 
-  bool Close();
   bool Connect(wxSockAddress& addr, bool wait = TRUE);
   bool Connect(const wxString& host);
 
+  // [forcibly] close the connection
+  bool Close(bool force = FALSE);
+
   void SetUser(const wxString& user) { m_user = user; }
   void SetPassword(const wxString& passwd) { m_passwd = passwd; }
 
-  // Low-level methods
-  bool SendCommand(const wxString& command, char exp_ret);
-  inline virtual wxProtocolError GetError()
-      { return m_lastError; }
-  const wxString& GetLastResult();             // Get the complete return 
-
   // Filesystem commands
   bool ChDir(const wxString& dir);
   bool MkDir(const wxString& dir);
@@ -56,6 +52,14 @@ public:
   wxOutputStream *GetOutputStream(const wxString& path);
 
   // List method
+  bool GetList(wxArrayString& files, const wxString& wildcard = wxEmptyString);
+
+  // Low-level methods
+  bool SendCommand(const wxString& command, char exp_ret);
+  virtual wxProtocolError GetError() { return m_lastError; }
+  const wxString& GetLastResult();             // Get the complete return 
+
+  // deprecated
   wxList *GetList(const wxString& wildcard);
 
 protected:
@@ -69,6 +73,9 @@ protected:
 
   wxSocketClient *GetPort();
   bool GetResult(char exp);
+
+  DECLARE_DYNAMIC_CLASS(wxFTP)
+  DECLARE_PROTOCOL(wxFTP)
 };
 
-#endif
+#endif // __WX_FTP_H__
index 48bf1996af17d66332b2cb5a8d0189875b62eefc..1ac98294c83e7acac907a589e926bcb53cec41c8 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        protocol.h
+// Name:        wx/protocol/protocol.h
 // Purpose:     Protocol base class
 // Author:      Guilhem Lavaux
 // Modified by:
@@ -8,6 +8,7 @@
 // Copyright:   (c) 1997, 1998 Guilhem Lavaux
 // Licence:     wxWindows license
 /////////////////////////////////////////////////////////////////////////////
+
 #ifndef _WX_PROTOCOL_PROTOCOL_H
 #define _WX_PROTOCOL_PROTOCOL_H
 
 
 #include "wx/defs.h"
 
-
 #include "wx/object.h"
 #include "wx/string.h"
 #include "wx/stream.h"
 
 #if wxUSE_SOCKETS
-#include "wx/socket.h"
+    #include "wx/socket.h"
 #endif
 
-typedef enum {
- wxPROTO_NOERR = 0,
- wxPROTO_NETERR,
- wxPROTO_PROTERR,
- wxPROTO_CONNERR,
- wxPROTO_INVVAL,
- wxPROTO_NOHNDLR,
- wxPROTO_NOFILE,
- wxPROTO_ABRT,
- wxPROTO_RCNCT,
- wxPROTO_STREAMING
-} wxProtocolError;
-
-// For protocols
-#define DECLARE_PROTOCOL(class) \
-public: \
-  static wxProtoInfo g_proto_##class;
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
 
-#define IMPLEMENT_PROTOCOL(class, name, serv, host) \
-wxProtoInfo class::g_proto_##class(name, serv, host, CLASSINFO(class));
+typedef enum
+{
+    wxPROTO_NOERR = 0,
+    wxPROTO_NETERR,
+    wxPROTO_PROTERR,
+    wxPROTO_CONNERR,
+    wxPROTO_INVVAL,
+    wxPROTO_NOHNDLR,
+    wxPROTO_NOFILE,
+    wxPROTO_ABRT,
+    wxPROTO_RCNCT,
+    wxPROTO_STREAMING
+} wxProtocolError;
 
-class WXDLLEXPORT wxProtoInfo : public wxObject {
-  DECLARE_DYNAMIC_CLASS(wxProtoInfo)
-protected:
-  wxProtoInfo *next;
-  wxString m_protoname;
-  wxString prefix;
-  wxString m_servname;
-  wxClassInfo *m_cinfo;
-  bool m_needhost;
-
-  friend class wxURL;
-public:
-  wxProtoInfo(const wxChar *name, const wxChar *serv_name, const bool need_host1,
-              wxClassInfo *info);
-};
+// ----------------------------------------------------------------------------
+// wxProtocol: abstract base class for all protocols
+// ----------------------------------------------------------------------------
 
 class WXDLLEXPORT wxProtocol
 #if wxUSE_SOCKETS
- : public wxSocketClient {
+ : public wxSocketClient
 #else
- : public wxObject {
+ : public wxObject
 #endif
-  DECLARE_ABSTRACT_CLASS(wxProtocol)
+{
 public:
-  wxProtocol();
+    wxProtocol();
 
 #if wxUSE_SOCKETS
-  bool Reconnect();
-  virtual bool Connect( const wxString& WXUNUSED(host) ) { return FALSE; }
-  virtual bool Connect( wxSockAddress& addr, bool WXUNUSED(wait) = TRUE) { return wxSocketClient::Connect(addr); }
-#endif
+    bool Reconnect();
+    virtual bool Connect( const wxString& WXUNUSED(host) ) { return FALSE; }
+    virtual bool Connect( wxSockAddress& addr, bool WXUNUSED(wait) = TRUE) { return wxSocketClient::Connect(addr); }
+
+    // read a '\r\n' terminated line from the given socket and put it in
+    // result (without the terminators)
+    static wxProtocolError ReadLine(wxSocketBase *socket, wxString& result);
+
+    // read a line from this socket - this one can be overridden in the
+    // derived classes if different line termination convention is to be used
+    virtual wxProtocolError ReadLine(wxString& result);
+#endif // wxUSE_SOCKETS
 
-  virtual bool Abort() = 0;
-  virtual wxInputStream *GetInputStream(const wxString& path) = 0;
-  virtual wxProtocolError GetError() = 0;
-  virtual wxString GetContentType() { return wxEmptyString; }
-  virtual void SetUser(const wxString& WXUNUSED(user)) {}
-  virtual void SetPassword(const wxString& WXUNUSED(passwd) ) {}
+    virtual bool Abort() = 0;
+    virtual wxInputStream *GetInputStream(const wxString& path) = 0;
+    virtual wxProtocolError GetError() = 0;
+    virtual wxString GetContentType() { return wxEmptyString; }
+    virtual void SetUser(const wxString& WXUNUSED(user)) {}
+    virtual void SetPassword(const wxString& WXUNUSED(passwd) ) {}
+
+private:
+    DECLARE_ABSTRACT_CLASS(wxProtocol)
 };
 
 #if wxUSE_SOCKETS
 wxProtocolError WXDLLEXPORT GetLine(wxSocketBase *sock, wxString& result);
 #endif
 
+// ----------------------------------------------------------------------------
+// macros for protocol classes
+// ----------------------------------------------------------------------------
+
+#define DECLARE_PROTOCOL(class) \
+public: \
+  static wxProtoInfo g_proto_##class;
+
+#define IMPLEMENT_PROTOCOL(class, name, serv, host) \
+wxProtoInfo class::g_proto_##class(name, serv, host, CLASSINFO(class));
+
+class WXDLLEXPORT wxProtoInfo : public wxObject
+{
+public:
+    wxProtoInfo(const wxChar *name,
+                const wxChar *serv_name,
+                const bool need_host1,
+                wxClassInfo *info);
+
+protected:
+    wxProtoInfo *next;
+    wxString m_protoname;
+    wxString prefix;
+    wxString m_servname;
+    wxClassInfo *m_cinfo;
+    bool m_needhost;
+
+    friend class wxURL;
+
+    DECLARE_DYNAMIC_CLASS(wxProtoInfo)
+};
+
 #endif // _WX_PROTOCOL_PROTOCOL_H
index 37042997953f80cbb3726bf690c044eeae79c3e6..88533852c7026734a7791104024f49173b5aec99 100644 (file)
 //#define TEST_LOG
 //#define TEST_LONGLONG
 //#define TEST_MIME
-//#define TEST_SOCKETS
+#define TEST_SOCKETS
 //#define TEST_STRINGS
 //#define TEST_THREADS
-#define TEST_TIMER
+//#define TEST_TIMER
 
 // ============================================================================
 // implementation
 // ============================================================================
 
+// ----------------------------------------------------------------------------
+// helper functions
+// ----------------------------------------------------------------------------
+
+#if defined(TEST_STRINGS) || defined(TEST_SOCKETS)
+
+// replace TABs with \t and CRs with \n
+static wxString MakePrintable(const wxChar *s)
+{
+    wxString str(s);
+    (void)str.Replace(_T("\t"), _T("\\t"));
+    (void)str.Replace(_T("\n"), _T("\\n"));
+    (void)str.Replace(_T("\r"), _T("\\r"));
+
+    return str;
+}
+
+#endif // MakePrintable() is used
+
 // ----------------------------------------------------------------------------
 // wxCmdLineParser
 // ----------------------------------------------------------------------------
@@ -696,27 +715,129 @@ static void TestBitOperations()
 #ifdef TEST_SOCKETS
 
 #include <wx/socket.h>
+#include <wx/protocol/protocol.h>
+#include <wx/protocol/ftp.h>
+#include <wx/protocol/http.h>
+
+static void TestSocketServer()
+{
+    puts("*** Testing wxSocketServer ***\n");
+
+    // we want to launch a server
+    wxIPV4address addr;
+    addr.Service(3000);
+
+    wxSocketServer *server = new wxSocketServer(addr);
+    if ( !server->Ok() )
+    {
+        puts("ERROR: failed to bind");
+    }
+}
 
 static void TestSocketClient()
 {
     puts("*** Testing wxSocketClient ***\n");
 
-    wxIPV4address addrDst;
-    addrDst.Hostname("www.wxwindows.org");
-    addrDst.Service(80);
+    static const char *hostname = "www.wxwindows.org";
+
+    wxIPV4address addr;
+    addr.Hostname(hostname);
+    addr.Service(80);
+
+    printf("--- Attempting to connect to %s:80...\n", hostname);
 
     wxSocketClient client;
-    if ( !client.Connect(addrDst) )
+    if ( !client.Connect(addr) )
     {
-        printf("ERROR: failed to connect to %s\n", addrDst.Hostname().c_str());
+        printf("ERROR: failed to connect to %s\n", hostname);
     }
     else
     {
+        printf("--- Connected to %s:%u...\n",
+               addr.Hostname().c_str(), addr.Service());
+
         char buf[8192];
 
-        client.Write("get /front.htm\n", 17);
+        // could use simply "GET" here I suppose
+        wxString cmdGet =
+            wxString::Format("GET http://%s/\r\n", hostname);
+        client.Write(cmdGet, cmdGet.length());
+        printf("--- Sent command '%s' to the server\n",
+               MakePrintable(cmdGet).c_str());
         client.Read(buf, WXSIZEOF(buf));
-        printf("Server replied:\n%s", buf);
+        printf("--- Server replied:\n%s", buf);
+    }
+}
+
+static void TestProtocolFtp()
+{
+    puts("*** Testing wxFTP ***\n");
+
+    wxLog::AddTraceMask(_T("ftp"));
+
+    static const char *hostname = "ftp.wxwindows.org";
+
+    printf("--- Attempting to connect to %s:21...\n", hostname);
+
+    wxFTP ftp;
+    if ( !ftp.Connect(hostname) )
+    {
+        printf("ERROR: failed to connect to %s\n", hostname);
+    }
+    else
+    {
+        printf("--- Connected to %s, current directory is '%s'\n",
+               hostname, ftp.Pwd().c_str());
+        if ( !ftp.ChDir(_T("pub")) )
+        {
+            puts("ERROR: failed to cd to pub");
+        }
+
+        wxArrayString files;
+        if ( !ftp.GetList(files) )
+        {
+            puts("ERROR: failed to get list of files");
+        }
+        else
+        {
+            printf("List of files under '%s':\n", ftp.Pwd().c_str());
+            size_t count = files.GetCount();
+            for ( size_t n = 0; n < count; n++ )
+            {
+                printf("\t%s\n", files[n].c_str());
+            }
+            puts("End of the file list");
+        }
+
+        if ( !ftp.ChDir(_T("..")) )
+        {
+            puts("ERROR: failed to cd to ..");
+        }
+
+        static const char *filename = "welcome.msg";
+        wxInputStream *in = ftp.GetInputStream(filename);
+        if ( !in )
+        {
+            puts("ERROR: couldn't get input stream");
+        }
+        else
+        {
+            size_t size = in->StreamSize();
+            printf("Reading file %s (%u bytes)...", filename, size);
+
+            char *data = new char[size];
+            if ( !in->Read(data, size) )
+            {
+                puts("ERROR: read error");
+            }
+            else
+            {
+                printf("\nContents of %s:\n%s\n", filename, data);
+            }
+
+            delete [] data;
+            delete in;
+        }
     }
 }
 
@@ -2139,17 +2260,6 @@ static void TestStringFind()
     puts("");
 }
 
-// replace TABs with \t and CRs with \n
-static wxString MakePrintable(const wxChar *s)
-{
-    wxString str(s);
-    (void)str.Replace(_T("\t"), _T("\\t"));
-    (void)str.Replace(_T("\n"), _T("\\n"));
-    (void)str.Replace(_T("\r"), _T("\\r"));
-
-    return str;
-}
-
 static void TestStringTokenizer()
 {
     puts("*** Testing wxStringTokenizer ***");
@@ -2443,7 +2553,12 @@ int main(int argc, char **argv)
 #endif // TEST_MIME
 
 #ifdef TEST_SOCKETS
-    TestSocketClient();
+    if ( 0 )
+    {
+        TestSocketServer();
+        TestSocketClient();
+    }
+        TestProtocolFtp();
 #endif // TEST_SOCKETS
 
 #ifdef TEST_TIMER
index 829d4bf212fd9974ad66f6874613542983e9acf5..80c0e1a057c0513b1f4c0224dcc89b63ac1c4e2a 100644 (file)
 #include "wx/sckstrm.h"
 #include "wx/protocol/protocol.h"
 #include "wx/protocol/ftp.h"
+#include "wx/log.h"
 
 #ifdef __BORLANDC__
 #pragma hdrstop
 #endif
 
+// the length of FTP status code (3 digits)
+static const size_t LEN_CODE = 3;
+
 #define FTP_BSIZE 1024
 
 IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
@@ -53,23 +57,21 @@ IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), TRUE)
 ////////////////////////////////////////////////////////////////
 
 wxFTP::wxFTP()
-  : wxProtocol()
+     : wxProtocol()
 {
-  m_lastError = wxPROTO_NOERR;
-  m_streaming = FALSE;
+    m_lastError = wxPROTO_NOERR;
+    m_streaming = FALSE;
 
-  m_user = wxT("anonymous");
-  m_passwd = wxGetUserId();
-  m_passwd += wxT('@');
-  m_passwd += wxGetHostName();
+    m_user = wxT("anonymous");
+    m_passwd << wxGetUserId() << wxT('@') << wxGetFullHostName();
 
-  SetNotify(0);
-  SetFlags(wxSOCKET_NONE);
+    SetNotify(0);
+    SetFlags(wxSOCKET_NONE);
 }
 
 wxFTP::~wxFTP()
 {
-  SendCommand("QUIT", '2');
+    Close();
 }
 
 ////////////////////////////////////////////////////////////////
@@ -120,60 +122,160 @@ bool wxFTP::Connect(const wxString& host)
   return Connect(addr);
 }
 
-bool wxFTP::Close()
+bool wxFTP::Close(bool force)
 {
-  if (m_streaming) {
-    m_lastError = wxPROTO_STREAMING;
-    return FALSE;
-  }
-  if (IsConnected())
-    SendCommand(wxString(wxT("QUIT")), '2');
+    if ( m_streaming )
+    {
+        if ( !force )
+        {
+            m_lastError = wxPROTO_STREAMING;
+            return FALSE;
+        }
+
+        (void)Abort();
+    }
+
+    if ( IsConnected() )
+        SendCommand(wxT("QUIT"), '2');
 
-  return wxSocketClient::Close();
+    return wxSocketClient::Close();
 }
 
-////////////////////////////////////////////////////////////////
-////// wxFTP low-level methods /////////////////////////////////
-////////////////////////////////////////////////////////////////
+// ============================================================================
+// low level methods
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// Send command to FTP server
+// ----------------------------------------------------------------------------
+
 bool wxFTP::SendCommand(const wxString& command, char exp_ret)
 {
-  wxString tmp_str;
+    wxString tmp_str;
 
-  if (m_streaming) {
-    m_lastError = wxPROTO_STREAMING;
-    return FALSE;
-  }
-  tmp_str = command + wxT("\r\n");
-  const wxWX2MBbuf tmp_buf = tmp_str.mb_str();
-  if (Write(wxMBSTRINGCAST tmp_buf, strlen(tmp_buf)).Error()) {
-    m_lastError = wxPROTO_NETERR;
-    return FALSE;
-  }
-  return GetResult(exp_ret);
+    if (m_streaming)
+    {
+        m_lastError = wxPROTO_STREAMING;
+        return FALSE;
+    }
+
+    tmp_str = command + wxT("\r\n");
+    const wxWX2MBbuf tmp_buf = tmp_str.mb_str();
+    if ( Write(wxMBSTRINGCAST tmp_buf, strlen(tmp_buf)).Error())
+    {
+        m_lastError = wxPROTO_NETERR;
+        return FALSE;
+    }
+
+    wxLogTrace(_T("ftp"), _T("==> %s"), command.c_str());
+
+    return GetResult(exp_ret);
 }
 
+// ----------------------------------------------------------------------------
+// Recieve servers reply
+// ----------------------------------------------------------------------------
+
 bool wxFTP::GetResult(char exp)
 {
-  m_lastError = GetLine(this, m_lastResult);
-  if ( m_lastError )
-    return FALSE;
-  if (m_lastResult.GetChar(0) != exp) {
-    m_lastError = wxPROTO_PROTERR;
-    return FALSE;
-  }
+    wxString code;
+
+    // we handle multiline replies here according to RFC 959: it says that a
+    // reply may either be on 1 line of the form "xyz ..." or on several lines
+    // in whuch case it looks like
+    //      xyz-...
+    //      ...
+    //      xyz ...
+    // and the intermeidate lines may start with xyz or not
+    bool badReply = FALSE;
+    bool firstLine = TRUE;
+    bool endOfReply = FALSE;
+    while ( !endOfReply && !badReply )
+    {
+        m_lastError = ReadLine(m_lastResult);
+        if ( m_lastError )
+            return FALSE;
+
+        // unless this is an intermediate line of a multiline reply, it must
+        // contain the code in the beginning and '-' or ' ' following it
+        if ( m_lastResult.Len() < LEN_CODE + 1 )
+        {
+            if ( firstLine )
+            {
+                badReply = TRUE;
+            }
+            else
+            {
+                wxLogTrace(_T("ftp"), _T("<== %s %s"),
+                           code.c_str(), m_lastResult.c_str());
+            }
+        }
+        else // line has at least 4 chars
+        {
+            // this is the char which tells us what we're dealing with
+            wxChar chMarker = m_lastResult.GetChar(LEN_CODE);
+
+            if ( firstLine )
+            {
+                code = wxString(m_lastResult, LEN_CODE);
+                wxLogTrace(_T("ftp"), _T("<== %s %s"),
+                           code.c_str(), m_lastResult.c_str() + LEN_CODE + 1);
+
+                switch ( chMarker )
+                {
+                    case _T(' '):
+                        endOfReply = TRUE;
+                        break;
+
+                    case _T('-'):
+                        firstLine = FALSE;
+                        break;
+
+                    default:
+                        // unexpected
+                        badReply = TRUE;
+                }
+            }
+            else // subsequent line of multiline reply
+            {
+                if ( wxStrncmp(m_lastResult, code, LEN_CODE) == 0 )
+                {
+                    if ( chMarker == _T(' ') )
+                    {
+                        endOfReply = TRUE;
+                    }
+
+                    wxLogTrace(_T("ftp"), _T("<== %s %s"),
+                               code.c_str(), m_lastResult.c_str() + LEN_CODE + 1);
+                }
+                else
+                {
+                    // just part of reply
+                    wxLogTrace(_T("ftp"), _T("<== %s %s"),
+                               code.c_str(), m_lastResult.c_str());
+                }
+            }
+        }
+    }
 
-  if (m_lastResult.GetChar(3) == '-') {
-    wxString key = m_lastResult.Left((size_t)3);
+    if ( badReply )
+    {
+        wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
+                   m_lastResult.c_str());
 
-    key += wxT(' ');
+        m_lastError = wxPROTO_PROTERR;
 
-    while (m_lastResult.Index(key) != 0) {
-      m_lastError = GetLine(this, m_lastResult);
-      if ( m_lastError )
         return FALSE;
     }
-  }
-  return TRUE;
+
+    if ( code.GetChar(0) != exp )
+    {
+        m_lastError = wxPROTO_PROTERR;
+
+        return FALSE;
+    }
+
+    return TRUE;
 }
 
 ////////////////////////////////////////////////////////////////
@@ -204,15 +306,45 @@ bool wxFTP::RmDir(const wxString& dir)
 
 wxString wxFTP::Pwd()
 {
-  int beg, end;
-
-  if (!SendCommand(wxT("PWD"), '2'))
-    return wxString((char *)NULL);
-  
-  beg = m_lastResult.Find(wxT('\"'),FALSE);
-  end = m_lastResult.Find(wxT('\"'),TRUE);
+    wxString path;
+
+    if ( SendCommand(wxT("PWD"), '2') )
+    {
+        // the result is at least that long if SendCommand() succeeded
+        const wxChar *p = m_lastResult.c_str() + LEN_CODE + 1;
+        if ( *p != _T('"') )
+        {
+            wxLogDebug(_T("Missing starting quote in reply for PWD: %s"), p);
+        }
+        else
+        {
+            for ( p++; *p; p++ )
+            {
+                if ( *p == _T('"') )
+                {
+                    // check if the quote is doubled
+                    p++;
+                    if ( !*p || *p != _T('"') )
+                    {
+                        // no, this is the end
+                        break;
+                    }
+                    //else: yes, it is: this is an embedded quote in the
+                    //      filename, treat as normal char
+                }
+
+                path += *p;
+            }
+
+            if ( !*p )
+            {
+                wxLogDebug(_T("Missing ending quote in reply for PWD: %s"),
+                           m_lastResult.c_str() + LEN_CODE + 1);
+            }
+        }
+    }
 
-  return wxString(beg+1, end);
+    return path;
 }
 
 bool wxFTP::Rename(const wxString& src, const wxString& dst)
@@ -312,12 +444,16 @@ wxSocketClient *wxFTP::GetPort()
   return client;
 }
 
-bool wxFTP::Abort(void)
+bool wxFTP::Abort()
 {
-  m_streaming = FALSE;
-  if (!SendCommand(wxT("ABOR"), '4'))
-    return FALSE;
-  return GetResult('2');
+    if ( !m_streaming )
+        return TRUE;
+        
+    m_streaming = FALSE;
+    if ( !SendCommand(wxT("ABOR"), '4') )
+        return FALSE;
+
+    return GetResult('2');
 }
 
 wxInputStream *wxFTP::GetInputStream(const wxString& path)
@@ -369,6 +505,42 @@ wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
   return new wxOutputFTPStream(this, sock);
 }
 
+bool wxFTP::GetList(wxArrayString& files, const wxString& wildcard)
+{
+    wxSocketBase *sock = GetPort();
+    if ( !sock )
+    {
+        return FALSE;
+    }
+
+    wxString line = _T("NLST");
+    if ( !!wildcard )
+    {
+        // notice that there is no space here
+        line += wildcard;
+    }
+
+    if ( !SendCommand(line, '1') )
+    {
+        return FALSE;
+    }
+
+    files.Empty();
+
+    while ( ReadLine(sock, line) == wxPROTO_NOERR )
+    {
+        files.Add(line);
+    }
+
+    delete sock;
+
+    // the file list should be terminated by "226 Transfer complete""
+    if ( !GetResult('2') )
+        return FALSE;
+
+    return TRUE;
+}
+
 wxList *wxFTP::GetList(const wxString& wildcard)
 {
   wxList *file_list = new wxList;
index 542752e318a1059446724592b96ceba8eab1cbc1..875ad85af0bc02e7a45c44170415a8b7a9b1cf95 100644 (file)
@@ -82,6 +82,56 @@ bool wxProtocol::Reconnect()
   return TRUE;
 }
 
+// ----------------------------------------------------------------------------
+// Read a line from socket
+// ----------------------------------------------------------------------------
+
+// TODO ReadLine() should use buffers private to wxProtocol for efficiency!
+
+// static
+wxProtocolError wxProtocol::ReadLine(wxSocketBase *socket, wxString& result)
+{
+    result.Empty();
+    char ch, chLast = '\0';
+    while ( !socket->Read(&ch, sizeof(ch)).Error() )
+    {
+        switch ( ch )
+        {
+            case '\r':
+                // remember it, if the following is '\n', we're done
+                chLast = '\r';
+                break;
+
+            case '\n':
+                // only ends line if the previous character was '\r'
+                if ( chLast == '\r' )
+                {
+                    // EOL found
+                    return wxPROTO_NOERR;
+                }
+                //else: fall through
+
+            default:
+                // normal char
+                if ( chLast )
+                {
+                    result += chLast;
+                    chLast = '\0';
+                }
+
+                result += ch;
+        }
+    }
+
+    return wxPROTO_NETERR;
+}
+
+wxProtocolError wxProtocol::ReadLine(wxString& result)
+{
+    return ReadLine(this, result);
+}
+
+// old function which only chops '\n' and not '\r\n'
 wxProtocolError GetLine(wxSocketBase *sock, wxString& result) {
 #define PROTO_BSIZE 2048
   size_t avail, size;
index cf81fcfceb6a4bce23d17ad4b22766e68b776228..e9056b41293f58606cfda04aaefe82771a5ed282 100644 (file)
@@ -1,4 +1,4 @@
-# This file was automatically generated by tmake at 15:55, 2000/03/14
+# This file was automatically generated by tmake at 09:19, 2000/03/15
 # DO NOT CHANGE THIS FILE, YOUR CHANGES WILL BE LOST! CHANGE BASE.T!
 ALL_SOURCES = \
                common/init.cpp \
@@ -19,7 +19,9 @@ ALL_SOURCES = \
                common/fs_inet.cpp \
                common/fs_mem.cpp \
                common/fs_zip.cpp \
+               common/ftp.cpp \
                common/hash.cpp \
+               common/http.cpp \
                common/intl.cpp \
                common/list.cpp \
                common/log.cpp \
@@ -30,7 +32,13 @@ ALL_SOURCES = \
                common/object.cpp \
                common/objstrm.cpp \
                common/process.cpp \
+               common/protocol.cpp \
+               common/sckaddr.cpp \
+               common/sckfile.cpp \
+               common/sckipc.cpp \
+               common/sckstrm.cpp \
                common/serbase.cpp \
+               common/socket.cpp \
                common/strconv.cpp \
                common/stream.cpp \
                common/string.cpp \
@@ -39,6 +47,7 @@ ALL_SOURCES = \
                common/tokenzr.cpp \
                common/txtstrm.cpp \
                common/unzip.c \
+               common/url.cpp \
                common/utilscmn.cpp \
                common/variant.cpp \
                common/wfstream.cpp \
@@ -118,6 +127,8 @@ ALL_HEADERS = \
                protocol/protocol.h
 
 BASE_OBJS = \
+               ipcbase.o \
+               gsocket.o \
                init.o \
                appcmn.o \
                cmdline.o \
@@ -136,7 +147,9 @@ BASE_OBJS = \
                fs_inet.o \
                fs_mem.o \
                fs_zip.o \
+               ftp.o \
                hash.o \
+               http.o \
                intl.o \
                list.o \
                log.o \
@@ -147,7 +160,13 @@ BASE_OBJS = \
                object.o \
                objstrm.o \
                process.o \
+               protocol.o \
+               sckaddr.o \
+               sckfile.o \
+               sckipc.o \
+               sckstrm.o \
                serbase.o \
+               socket.o \
                strconv.o \
                stream.o \
                string.o \
@@ -156,6 +175,7 @@ BASE_OBJS = \
                tokenzr.o \
                txtstrm.o \
                unzip.o \
+               url.o \
                utilscmn.o \
                variant.o \
                wfstream.o \
@@ -182,7 +202,9 @@ BASE_DEPS = \
                fs_inet.d \
                fs_mem.d \
                fs_zip.d \
+               ftp.d \
                hash.d \
+               http.d \
                intl.d \
                list.d \
                log.d \
@@ -193,7 +215,13 @@ BASE_DEPS = \
                object.d \
                objstrm.d \
                process.d \
+               protocol.d \
+               sckaddr.d \
+               sckfile.d \
+               sckipc.d \
+               sckstrm.d \
                serbase.d \
+               socket.d \
                strconv.d \
                stream.d \
                string.d \
@@ -202,6 +230,7 @@ BASE_DEPS = \
                tokenzr.d \
                txtstrm.d \
                unzip.d \
+               url.d \
                utilscmn.d \
                variant.d \
                wfstream.d \
@@ -234,7 +263,9 @@ BASE_DEPS = \
                fs_inet.d \
                fs_mem.d \
                fs_zip.d \
+               ftp.d \
                hash.d \
+               http.d \
                intl.d \
                list.d \
                log.d \
@@ -245,7 +276,13 @@ BASE_DEPS = \
                object.d \
                objstrm.d \
                process.d \
+               protocol.d \
+               sckaddr.d \
+               sckfile.d \
+               sckipc.d \
+               sckstrm.d \
                serbase.d \
+               socket.d \
                strconv.d \
                stream.d \
                string.d \
@@ -254,6 +291,7 @@ BASE_DEPS = \
                tokenzr.d \
                txtstrm.d \
                unzip.d \
+               url.d \
                utilscmn.d \
                variant.d \
                wfstream.d \
index 53834426de67d34b04f61c9a1153a2b639bff749..b45ba4e9a2d4627d1f643aa703998996b295ad4a 100644 (file)
@@ -90,27 +90,30 @@ struct sockaddr_un {
 
 
 #ifndef __GSOCKET_STANDALONE__
-
-#include "wx/unix/gsockunx.h"
-#include "wx/gsocket.h"
-
+#  include "wx/unix/gsockunx.h"
+#  include "wx/gsocket.h"
 #else
-
-#include "gsockunx.h"
-#include "gsocket.h"
-
+#  include "gsockunx.h"
+#  include "gsocket.h"
 #endif /* __GSOCKET_STANDALONE__ */
 
 /* redefine some GUI-only functions to do nothing in console mode */
 #if defined(wxUSE_GUI) && !wxUSE_GUI
-    #define _GSocket_GUI_Init(socket)
-    #define _GSocket_GUI_Destroy(socket)
-    #define _GSocket_Enable_Events(socket)
-    #define _GSocket_Disable_Events(socket)
-    #define _GSocket_Install_Callback(socket, event)
-    #define _GSocket_Uninstall_Callback(socket, event)
+#  define _GSocket_GUI_Init(socket) (1)
+#  define _GSocket_GUI_Destroy(socket)
+#  define _GSocket_Enable_Events(socket)
+#  define _GSocket_Disable_Events(socket)
+#  define _GSocket_Install_Callback(socket, event)
+#  define _GSocket_Uninstall_Callback(socket, event)
 #endif /* wxUSE_GUI */
 
+/* debugging helpers */
+#ifdef __GSOCKET_DEBUG__
+    #define GSocket_Debug(args)       printf args
+#else
+    #define GSocket_Debug(args)
+#endif // __GSOCKET_DEBUG__
+
 /* Global initialisers */
 
 bool GSocket_Init(void)
@@ -126,7 +129,7 @@ void GSocket_Cleanup(void)
 
 GSocket *GSocket_new(void)
 {
-  int i;
+  int i, success;
   GSocket *socket;
 
   socket = (GSocket *)malloc(sizeof(GSocket));
@@ -152,7 +155,8 @@ GSocket *GSocket_new(void)
   socket->m_establishing        = FALSE;
 
   /* Per-socket GUI-specific initialization */
-  if (!_GSocket_GUI_Init(socket))
+  success = _GSocket_GUI_Init(socket);
+  if (!success)
   {
     free(socket);
     return NULL;
@@ -719,7 +723,7 @@ int GSocket_Write(GSocket *socket, const char *buffer, int size)
 
   assert(socket != NULL);
   
-  printf( "GSocket_Write #1, size %d\n", size );
+  GSocket_Debug(( "GSocket_Write #1, size %d\n", size ));
 
   if (socket->m_fd == -1 || socket->m_server)
   {
@@ -727,13 +731,13 @@ int GSocket_Write(GSocket *socket, const char *buffer, int size)
     return -1;
   }
 
-  printf( "GSocket_Write #2, size %d\n", size );
+  GSocket_Debug(( "GSocket_Write #2, size %d\n", size ));
 
   /* If the socket is blocking, wait for writability (with a timeout) */
   if (_GSocket_Output_Timeout(socket) == GSOCK_TIMEDOUT)
     return -1;
 
-  printf( "GSocket_Write #3, size %d\n", size );
+  GSocket_Debug(( "GSocket_Write #3, size %d\n", size ));
 
   /* Write the data */
   if (socket->m_stream)
@@ -741,19 +745,19 @@ int GSocket_Write(GSocket *socket, const char *buffer, int size)
   else
     ret = _GSocket_Send_Dgram(socket, buffer, size);
     
-  printf( "GSocket_Write #4, size %d\n", size );
+  GSocket_Debug(( "GSocket_Write #4, size %d\n", size ));
 
   if (ret == -1)
   {
     if (errno == EWOULDBLOCK)
     {
       socket->m_error = GSOCK_WOULDBLOCK;
-      printf( "GSocket_Write error WOULDBLOCK\n" );
+      GSocket_Debug(( "GSocket_Write error WOULDBLOCK\n" ));
     }
     else
     {
       socket->m_error = GSOCK_IOERR;
-      printf( "GSocket_Write error IOERR\n" );
+      GSocket_Debug(( "GSocket_Write error IOERR\n" ));
     }
 
     /* Only reenable OUTPUT events after an error (just like WSAAsyncSelect
@@ -765,7 +769,7 @@ int GSocket_Write(GSocket *socket, const char *buffer, int size)
     return -1;
   }
   
-  printf( "GSocket_Write #5, size %d ret %d\n", size, ret );
+  GSocket_Debug(( "GSocket_Write #5, size %d ret %d\n", size, ret ));
 
   return ret;
 }
@@ -779,9 +783,104 @@ int GSocket_Write(GSocket *socket, const char *buffer, int size)
  */
 GSocketEventFlags GSocket_Select(GSocket *socket, GSocketEventFlags flags)
 {
+#if defined(wxUSE_GUI) && !wxUSE_GUI
+
+  GSocketEventFlags result = 0;
+  fd_set readfds;
+  fd_set writefds;
+  fd_set exceptfds;
+  struct timeval tv;
+
+  /* Do not use a static struct, Linux can garble it */
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
   assert(socket != NULL);
 
+  FD_ZERO(&readfds);
+  FD_ZERO(&writefds);
+  FD_ZERO(&exceptfds);
+  FD_SET(socket->m_fd, &readfds);
+  FD_SET(socket->m_fd, &writefds);
+  FD_SET(socket->m_fd, &exceptfds);
+
+  /* Check known state first */
+  result |= (GSOCK_CONNECTION_FLAG & socket->m_detected & flags);
+  result |= (GSOCK_LOST_FLAG       & socket->m_detected & flags);
+
+  /* Try select now */
+  if (select(socket->m_fd + 1, &readfds, &writefds, &exceptfds, &tv) <= 0)
+    return result;
+
+  /* Check for readability */
+  if (FD_ISSET(socket->m_fd, &readfds))
+  {
+    char c;
+
+    if (recv(socket->m_fd, &c, 1, MSG_PEEK) > 0)
+    {
+      result |= (GSOCK_INPUT_FLAG & flags);
+    }
+    else
+    {
+      if (socket->m_server && socket->m_stream)
+      {
+        result |= (GSOCK_CONNECTION_FLAG & flags);
+        socket->m_detected |= GSOCK_CONNECTION_FLAG;
+      }
+      else
+      {
+        result |= (GSOCK_LOST_FLAG & flags);
+        socket->m_detected = GSOCK_LOST_FLAG;
+      }
+    }
+  }
+
+  /* Check for writability */
+  if (FD_ISSET(socket->m_fd, &writefds))
+  {
+    if (socket->m_establishing && !socket->m_server)
+    {
+      int error;
+      SOCKLEN_T len = sizeof(error);
+
+      socket->m_establishing = FALSE;
+
+      getsockopt(socket->m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len);
+
+      if (error)
+      {
+        result |= (GSOCK_LOST_FLAG & flags);
+        socket->m_detected = GSOCK_LOST_FLAG;
+      }
+      else
+      {
+        result |= (GSOCK_CONNECTION_FLAG & flags);
+        socket->m_detected |= GSOCK_CONNECTION_FLAG;
+      }
+    }
+    else
+    {
+      result |= (GSOCK_OUTPUT_FLAG & flags);
+    }
+  }
+
+  /* Check for exceptions and errors (is this useful in Unices?) */
+  if (FD_ISSET(socket->m_fd, &exceptfds))
+  {
+    result |= (GSOCK_LOST_FLAG & flags);
+    socket->m_establishing = FALSE;
+    socket->m_detected = GSOCK_LOST_FLAG;
+  }
+
+  return result;
+
+#else 
+
+  assert(socket != NULL);
   return flags & socket->m_detected;
+
+#endif /* !wxUSE_GUI */
 }
 
 /* Flags */
@@ -928,17 +1027,17 @@ GSocketError _GSocket_Input_Timeout(GSocket *socket)
     ret = select(socket->m_fd + 1, &readfds, NULL, NULL, &tv);
     if (ret == 0)
     {
-      printf( "GSocket_Input_Timeout, select returned 0\n" );
+      GSocket_Debug(( "GSocket_Input_Timeout, select returned 0\n" ));
       socket->m_error = GSOCK_TIMEDOUT;
       return GSOCK_TIMEDOUT;
     }
     if (ret == -1)
     {
-      printf( "GSocket_Input_Timeout, select returned -1\n" );
-      if (errno == EBADF) printf( "Invalid file descriptor\n" );
-      if (errno == EINTR) printf( "A non blocked signal was caught\n" );
-      if (errno == EINVAL) printf( "The highest number descriptor is negative\n" );
-      if (errno == ENOMEM) printf( "Not enough memory\n" );
+      GSocket_Debug(( "GSocket_Input_Timeout, select returned -1\n" ));
+      if (errno == EBADF) GSocket_Debug(( "Invalid file descriptor\n" ));
+      if (errno == EINTR) GSocket_Debug(( "A non blocked signal was caught\n" ));
+      if (errno == EINVAL) GSocket_Debug(( "The highest number descriptor is negative\n" ));
+      if (errno == ENOMEM) GSocket_Debug(( "Not enough memory\n" ));
       socket->m_error = GSOCK_TIMEDOUT;
       return GSOCK_TIMEDOUT;
     }
@@ -967,17 +1066,17 @@ GSocketError _GSocket_Output_Timeout(GSocket *socket)
     ret = select(socket->m_fd + 1, NULL, &writefds, NULL, &tv);
     if (ret == 0)
     {
-      printf( "GSocket_Output_Timeout, select returned 0\n" );
+      GSocket_Debug(( "GSocket_Output_Timeout, select returned 0\n" ));
       socket->m_error = GSOCK_TIMEDOUT;
       return GSOCK_TIMEDOUT;
     }
     if (ret == -1)
     {
-      printf( "GSocket_Output_Timeout, select returned -1\n" );
-      if (errno == EBADF) printf( "Invalid file descriptor\n" );
-      if (errno == EINTR) printf( "A non blocked signal was caught\n" );
-      if (errno == EINVAL) printf( "The highest number descriptor is negative\n" );
-      if (errno == ENOMEM) printf( "Not enough memory\n" );
+      GSocket_Debug(( "GSocket_Output_Timeout, select returned -1\n" ));
+      if (errno == EBADF) GSocket_Debug(( "Invalid file descriptor\n" ));
+      if (errno == EINTR) GSocket_Debug(( "A non blocked signal was caught\n" ));
+      if (errno == EINVAL) GSocket_Debug(( "The highest number descriptor is negative\n" ));
+      if (errno == ENOMEM) GSocket_Debug(( "Not enough memory\n" ));
       socket->m_error = GSOCK_TIMEDOUT;
       return GSOCK_TIMEDOUT;
     }
@@ -1069,26 +1168,21 @@ int _GSocket_Send_Dgram(GSocket *socket, const char *buffer, int size)
 void _GSocket_Detected_Read(GSocket *socket)
 {
   char c;
-  int ret;
-
-  ret = recv(socket->m_fd, &c, 1, MSG_PEEK);
 
-  if (socket->m_stream)
-  {
-    if (ret < 0 && socket->m_server)
-    {
-      CALL_CALLBACK(socket, GSOCK_CONNECTION);
-      return;
-    }
-  }
-
-  if (ret > 0)
+  if (recv(socket->m_fd, &c, 1, MSG_PEEK) > 0)
   {
     CALL_CALLBACK(socket, GSOCK_INPUT);
   }
   else
   {
-    CALL_CALLBACK(socket, GSOCK_LOST);
+    if (socket->m_server && socket->m_stream)
+    {
+      CALL_CALLBACK(socket, GSOCK_CONNECTION);
+    }
+    else
+    {
+      CALL_CALLBACK(socket, GSOCK_LOST);
+    }
   }
 }
 
@@ -1101,7 +1195,7 @@ void _GSocket_Detected_Write(GSocket *socket)
 
     socket->m_establishing = FALSE;
 
-    getsockopt(socket->m_fd, SOL_SOCKET, SO_ERROR, (void*) &error, &len);
+    getsockopt(socket->m_fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len);
 
     if (error)
     {