From 8e907a13eae0ef1cd0eb4a318c824ba095a64684 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 15 Mar 2000 17:16:35 +0000 Subject: [PATCH] 1. wxFTP works (somehow) 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 | 1 + docs/latex/wx/ftp.tex | 7 +- include/wx/protocol/ftp.h | 31 ++-- include/wx/protocol/protocol.h | 130 +++++++++------ samples/console/console.cpp | 157 +++++++++++++++--- src/common/ftp.cpp | 294 ++++++++++++++++++++++++++------- src/common/protocol.cpp | 50 ++++++ src/files.lst | 40 ++++- src/unix/gsocket.c | 194 ++++++++++++++++------ 9 files changed, 705 insertions(+), 199 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 75b98fdd27..2409dab2be 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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 diff --git a/docs/latex/wx/ftp.tex b/docs/latex/wx/ftp.tex index 07d711056e..f49796142e 100644 --- a/docs/latex/wx/ftp.tex +++ b/docs/latex/wx/ftp.tex @@ -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. % ---------------------------------------------------------------------------- diff --git a/include/wx/protocol/ftp.h b/include/wx/protocol/ftp.h index 9f71992f2b..459f2bfd6c 100644 --- a/include/wx/protocol/ftp.h +++ b/include/wx/protocol/ftp.h @@ -8,6 +8,7 @@ // Copyright: (c) 1997, 1998 Guilhem Lavaux // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// + #ifndef __WX_FTP_H__ #define __WX_FTP_H__ @@ -20,28 +21,23 @@ #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__ diff --git a/include/wx/protocol/protocol.h b/include/wx/protocol/protocol.h index 48bf1996af..1ac98294c8 100644 --- a/include/wx/protocol/protocol.h +++ b/include/wx/protocol/protocol.h @@ -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 @@ -17,78 +18,105 @@ #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 diff --git a/samples/console/console.cpp b/samples/console/console.cpp index 3704299795..88533852c7 100644 --- a/samples/console/console.cpp +++ b/samples/console/console.cpp @@ -45,15 +45,34 @@ //#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 +#include +#include +#include + +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 diff --git a/src/common/ftp.cpp b/src/common/ftp.cpp index 829d4bf212..80c0e1a057 100644 --- a/src/common/ftp.cpp +++ b/src/common/ftp.cpp @@ -38,11 +38,15 @@ #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; diff --git a/src/common/protocol.cpp b/src/common/protocol.cpp index 542752e318..875ad85af0 100644 --- a/src/common/protocol.cpp +++ b/src/common/protocol.cpp @@ -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; diff --git a/src/files.lst b/src/files.lst index cf81fcfceb..e9056b4129 100644 --- a/src/files.lst +++ b/src/files.lst @@ -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 \ diff --git a/src/unix/gsocket.c b/src/unix/gsocket.c index 53834426de..b45ba4e9a2 100644 --- a/src/unix/gsocket.c +++ b/src/unix/gsocket.c @@ -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) { -- 2.45.2