fixes and improvements to FTP(S)ClientSession

This commit is contained in:
Günter Obiltschnig
2020-01-23 17:55:30 +01:00
parent 878b8bbeca
commit d65bf03a83
10 changed files with 336 additions and 98 deletions

View File

@@ -54,8 +54,8 @@ public:
enum FileType enum FileType
{ {
TYPE_TEXT, // TYPE A (ASCII) TYPE_TEXT, /// TYPE A (ASCII)
TYPE_BINARY // TYPE I (Image) TYPE_BINARY /// TYPE I (Image/binary data)
}; };
FTPClientSession(); FTPClientSession();
@@ -63,16 +63,13 @@ public:
/// ///
/// Passive mode will be used for data transfers. /// Passive mode will be used for data transfers.
explicit FTPClientSession(const StreamSocket& socket, bool readWelcomeMessage = true); FTPClientSession(const StreamSocket& socket, bool readWelcomeMessage = true);
/// Creates an FTPClientSession using the given /// Creates an FTPClientSession using the given
/// connected socket for the control connection. /// connected socket for the control connection.
/// ///
/// Passive mode will be used for data transfers. /// Passive mode will be used for data transfers.
FTPClientSession(const std::string& host, FTPClientSession(const std::string& host, Poco::UInt16 port = FTP_PORT, const std::string& username = "", const std::string& password = "");
Poco::UInt16 port = FTP_PORT,
const std::string& username = "",
const std::string& password = "");
/// Creates an FTPClientSession using a socket connected /// Creates an FTPClientSession using a socket connected
/// to the given host and port. If username is supplied, /// to the given host and port. If username is supplied,
/// login is attempted. /// login is attempted.
@@ -99,10 +96,7 @@ public:
bool getPassive() const; bool getPassive() const;
/// Returns true iff passive mode is enabled for this connection. /// Returns true iff passive mode is enabled for this connection.
virtual void open(const std::string& host, virtual void open(const std::string& host, Poco::UInt16 port, const std::string& username = "", const std::string& password = "");
Poco::UInt16 port,
const std::string& username = "",
const std::string& password = "");
/// Opens the FTP connection to the given host and port. /// Opens the FTP connection to the given host and port.
/// If username is supplied, login is attempted. /// If username is supplied, login is attempted.
@@ -117,6 +111,9 @@ public:
/// NetException in case of a general network communication failure. /// NetException in case of a general network communication failure.
void logout(); void logout();
/// Logs out from the server by sending a QUIT command. Any transfer
/// that's in progress is ended. The control connection is kept
/// open.
void close(); void close();
/// Sends a QUIT command and closes the connection to the server. /// Sends a QUIT command and closes the connection to the server.
@@ -306,11 +303,10 @@ public:
/// Returns true if the session is FTPS. /// Returns true if the session is FTPS.
const std::string& welcomeMessage(); const std::string& welcomeMessage();
/// Returns welcome message. /// Returns the welcome message.
protected: protected:
virtual void receiveServerReadyReply(); virtual void receiveServerReadyReply();
/// Function that read server welcome message after connetion
enum StatusClass enum StatusClass
{ {
@@ -320,6 +316,7 @@ protected:
FTP_TRANSIENT_NEGATIVE = 4, FTP_TRANSIENT_NEGATIVE = 4,
FTP_PERMANENT_NEGATIVE = 5 FTP_PERMANENT_NEGATIVE = 5
}; };
enum enum
{ {
DEFAULT_TIMEOUT = 30000000 // 30 seconds default timeout for socket operations DEFAULT_TIMEOUT = 30000000 // 30 seconds default timeout for socket operations
@@ -352,7 +349,7 @@ private:
FTPClientSession& operator = (const FTPClientSession&); FTPClientSession& operator = (const FTPClientSession&);
std::string _host; std::string _host;
Poco::UInt16 _port = 0; Poco::UInt16 _port = FTP_PORT;
bool _passiveMode = true; bool _passiveMode = true;
FileType _fileType = TYPE_BINARY; FileType _fileType = TYPE_BINARY;
bool _supports1738 = true; bool _supports1738 = true;

View File

@@ -31,7 +31,7 @@ class Net_API FTPPasswordProvider
/// The base class for all password providers. /// The base class for all password providers.
/// An instance of a subclass of this class can be /// An instance of a subclass of this class can be
/// registered with the FTPStreamFactory to /// registered with the FTPStreamFactory to
/// provide a password /// provide a password.
{ {
public: public:
virtual std::string password(const std::string& username, const std::string& host) = 0; virtual std::string password(const std::string& username, const std::string& host) = 0;
@@ -56,7 +56,7 @@ class Net_API FTPStreamFactory: public Poco::URIStreamFactory
/// the FTP URL format specified in RFC 1738. /// the FTP URL format specified in RFC 1738.
/// ///
/// If the URI does not contain a username and password, the /// If the URI does not contain a username and password, the
/// username "anonymous" and the password " /// username "anonymous" and the password "poco@localhost".
{ {
public: public:
FTPStreamFactory(); FTPStreamFactory();

View File

@@ -31,7 +31,7 @@ namespace Net {
FTPClientSession::FTPClientSession(): FTPClientSession::FTPClientSession():
_pControlSocket(0), _pControlSocket(0),
_pDataStream(0), _pDataStream(0),
_port(0), _port(FTP_PORT),
_passiveMode(true), _passiveMode(true),
_fileType(TYPE_BINARY), _fileType(TYPE_BINARY),
_supports1738(true), _supports1738(true),

View File

@@ -17,7 +17,7 @@ objects = AcceptCertificateHandler RejectCertificateHandler ConsoleCertificateHa
SecureSocketImpl SecureStreamSocket SecureStreamSocketImpl \ SecureSocketImpl SecureStreamSocket SecureStreamSocketImpl \
SSLException SSLManager Utility VerificationErrorArgs \ SSLException SSLManager Utility VerificationErrorArgs \
X509Certificate Session SecureSMTPClientSession \ X509Certificate Session SecureSMTPClientSession \
FTPSClientSession FTPSClientSession FTPSStreamFactory
target = PocoNetSSL target = PocoNetSSL
target_version = $(LIBVERSION) target_version = $(LIBVERSION)

View File

@@ -28,6 +28,9 @@ namespace Net {
class NetSSL_API FTPSClientSession: public Poco::Net::FTPClientSession class NetSSL_API FTPSClientSession: public Poco::Net::FTPClientSession
/// This is an extension of FTPClientSession that supports
/// FTP over SSL/TLS using the AUTH SSL/AUTH TLS and PBSZ/PROT
/// commands according to RFC 4217.
{ {
public: public:
FTPSClientSession(); FTPSClientSession();
@@ -40,7 +43,7 @@ public:
/// ///
/// Passive mode will be used for data transfers. /// Passive mode will be used for data transfers.
explicit FTPSClientSession(const StreamSocket& socket, bool readWelcomeMessage = true, bool tryUseFTPS = true, Context::Ptr pContext = nullptr); FTPSClientSession(const StreamSocket& socket, bool readWelcomeMessage = true, bool enableFTPS = true, Context::Ptr pContext = nullptr);
/// Creates an FTPSClientSession using the given /// Creates an FTPSClientSession using the given
/// connected socket for the control connection. /// connected socket for the control connection.
/// ///
@@ -55,8 +58,8 @@ public:
virtual ~FTPSClientSession(); virtual ~FTPSClientSession();
void tryFTPSmode(bool tryFTPS); void enableFTPS(bool enable = true);
/// avoid or require TLS mode /// Enable or disable FTPS (FTP over SSL/TLS).
bool isSecure() const; bool isSecure() const;
/// Returns true if the session is FTPS. /// Returns true if the session is FTPS.
@@ -75,7 +78,7 @@ private:
void afterCreateControlSocket(); void afterCreateControlSocket();
///Send commands to make SSL negotiating of control channel ///Send commands to make SSL negotiating of control channel
bool _tryFTPS = true; bool _enableFTPS = true;
bool _secureDataConnection = false; bool _secureDataConnection = false;
Context::Ptr _pContext; Context::Ptr _pContext;
}; };

View File

@@ -0,0 +1,74 @@
//
// FTPSStreamFactory.h
//
// Library: Net
// Package: FTP
// Module: FTPSStreamFactory
//
// Definition of the FTPSStreamFactory class.
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Net_FTPSStreamFactory_INCLUDED
#define Net_FTPSStreamFactory_INCLUDED
#include "Poco/Net/Net.h"
#include "Poco/Net/HTTPSession.h"
#include "Poco/Net/FTPStreamFactory.h"
namespace Poco {
namespace Net {
class Net_API FTPSStreamFactory: public Poco::Net::FTPStreamFactory
/// An implementation of the URIStreamFactory interface
/// that handles secure File Transfer Protocol (ftps) URIs
/// according to RFC 4217, based on the FTPSClientSession class.
///
/// The URI's path may end with an optional type specification
/// in the form (;type=<typecode>), where <typecode> is
/// one of a, i or d. If type=a, the file identified by the path
/// is transferred in ASCII (text) mode. If type=i, the file
/// is transferred in Image (binary) mode. If type=d, a directory
/// listing (in NLST format) is returned. This corresponds with
/// the FTP URL format specified in RFC 1738.
///
/// If the URI does not contain a username and password, the
/// username "anonymous" and the password "poco@localhost".
///
/// Note that ftps is a non-standard URI scheme.
{
public:
FTPSStreamFactory();
/// Creates the FTPSStreamFactory.
~FTPSStreamFactory();
/// Destroys the FTPSStreamFactory.
std::istream* open(const Poco::URI& uri);
/// Creates and opens a HTTP stream for the given URI.
/// The URI must be a ftps://... URI.
///
/// Throws a NetException if anything goes wrong.
static void registerFactory();
/// Registers the FTPSStreamFactory with the
/// default URIStreamOpener instance.
static void unregisterFactory();
/// Unregisters the FTPSStreamFactory with the
/// default URIStreamOpener instance.
};
} } // namespace Poco::Net
#endif // Net_FTPSStreamFactory_INCLUDED

View File

@@ -19,6 +19,7 @@
#include "Poco/Net/HTTPStreamFactory.h" #include "Poco/Net/HTTPStreamFactory.h"
#include "Poco/Net/HTTPSStreamFactory.h" #include "Poco/Net/HTTPSStreamFactory.h"
#include "Poco/Net/FTPStreamFactory.h" #include "Poco/Net/FTPStreamFactory.h"
#include "Poco/Net/FTPSStreamFactory.h"
#include "Poco/Net/SSLManager.h" #include "Poco/Net/SSLManager.h"
#include "Poco/Net/KeyConsoleHandler.h" #include "Poco/Net/KeyConsoleHandler.h"
#include "Poco/Net/ConsoleCertificateHandler.h" #include "Poco/Net/ConsoleCertificateHandler.h"
@@ -35,6 +36,7 @@ using Poco::Exception;
using Poco::Net::HTTPStreamFactory; using Poco::Net::HTTPStreamFactory;
using Poco::Net::HTTPSStreamFactory; using Poco::Net::HTTPSStreamFactory;
using Poco::Net::FTPStreamFactory; using Poco::Net::FTPStreamFactory;
using Poco::Net::FTPSStreamFactory;
using Poco::Net::SSLManager; using Poco::Net::SSLManager;
using Poco::Net::Context; using Poco::Net::Context;
using Poco::Net::KeyConsoleHandler; using Poco::Net::KeyConsoleHandler;
@@ -64,6 +66,7 @@ int main(int argc, char** argv)
HTTPStreamFactory::registerFactory(); HTTPStreamFactory::registerFactory();
HTTPSStreamFactory::registerFactory(); HTTPSStreamFactory::registerFactory();
FTPStreamFactory::registerFactory(); FTPStreamFactory::registerFactory();
FTPSStreamFactory::registerFactory();
if (argc != 2) if (argc != 2)
{ {

View File

@@ -35,9 +35,9 @@ FTPSClientSession::FTPSClientSession(Context::Ptr pContext):
{ {
} }
FTPSClientSession::FTPSClientSession(const StreamSocket& socket, bool readWelcomeMessage, bool tryUseFTPS, Context::Ptr pContext): FTPSClientSession::FTPSClientSession(const StreamSocket& socket, bool readWelcomeMessage, bool enableFTPS, Context::Ptr pContext):
FTPClientSession(socket, readWelcomeMessage), FTPClientSession(socket, readWelcomeMessage),
_tryFTPS(tryUseFTPS), _enableFTPS(enableFTPS),
_pContext(pContext) _pContext(pContext)
{ {
} }
@@ -55,9 +55,9 @@ FTPSClientSession::~FTPSClientSession()
} }
void FTPSClientSession::tryFTPSmode(bool tryFTPS) void FTPSClientSession::enableFTPS(bool enable)
{ {
_tryFTPS = tryFTPS; _enableFTPS = enable;
} }
@@ -81,7 +81,7 @@ void FTPSClientSession::beforeCreateDataSocket()
void FTPSClientSession::afterCreateControlSocket() void FTPSClientSession::afterCreateControlSocket()
{ {
if (!_tryFTPS) return; if (!_enableFTPS) return;
_pControlSocket->setNoDelay(true); _pControlSocket->setNoDelay(true);
if (_pControlSocket->secure()) return; if (_pControlSocket->secure()) return;
@@ -106,7 +106,7 @@ void FTPSClientSession::afterCreateControlSocket()
} }
else else
{ {
_tryFTPS = false; _enableFTPS = false;
} }
} }

View File

@@ -0,0 +1,161 @@
//
// FTPSStreamFactory.cpp
//
// Library: Net
// Package: FTP
// Module: FTPSStreamFactory
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/FTPSStreamFactory.h"
#include "Poco/Net/FTPSClientSession.h"
#include "Poco/Net/NetException.h"
#include "Poco/URI.h"
#include "Poco/URIStreamOpener.h"
#include "Poco/UnbufferedStreamBuf.h"
#include "Poco/Path.h"
using Poco::URIStreamFactory;
using Poco::URI;
using Poco::URIStreamOpener;
using Poco::UnbufferedStreamBuf;
using Poco::Path;
namespace Poco {
namespace Net {
class FTPSStreamBuf: public UnbufferedStreamBuf
{
public:
FTPSStreamBuf(std::istream& istr):
_istr(istr)
{
// make sure exceptions from underlying string propagate
_istr.exceptions(std::ios::badbit);
}
~FTPSStreamBuf()
{
}
private:
int readFromDevice()
{
return _istr.get();
}
std::istream& _istr;
};
class FTPSIOS: public virtual std::ios
{
public:
FTPSIOS(std::istream& istr):
_buf(istr)
{
poco_ios_init(&_buf);
}
~FTPSIOS()
{
}
FTPSStreamBuf* rdbuf()
{
return &_buf;
}
protected:
FTPSStreamBuf _buf;
};
class FTPSStream: public FTPSIOS, public std::istream
{
public:
FTPSStream(std::istream& istr, FTPSClientSession* pSession):
FTPSIOS(istr),
std::istream(&_buf),
_pSession(pSession)
{
}
~FTPSStream()
{
delete _pSession;
}
private:
FTPSClientSession* _pSession;
};
FTPSStreamFactory::FTPSStreamFactory()
{
}
FTPSStreamFactory::~FTPSStreamFactory()
{
}
std::istream* FTPSStreamFactory::open(const URI& uri)
{
poco_assert (uri.getScheme() == "ftps");
Poco::UInt16 port = uri.getPort();
if (port == 0) port = FTPClientSession::FTP_PORT;
FTPSClientSession* pSession = new FTPSClientSession(uri.getHost(), port);
try
{
std::string username;
std::string password;
getUserInfo(uri, username, password);
std::string path;
char type;
getPathAndType(uri, path, type);
pSession->login(username, password);
if (type == 'a')
pSession->setFileType(FTPClientSession::TYPE_TEXT);
Path p(path, Path::PATH_UNIX);
p.makeFile();
for (int i = 0; i < p.depth(); ++i)
pSession->setWorkingDirectory(p[i]);
std::string file(p.getFileName());
std::istream& istr = (type == 'd' ? pSession->beginList(file) : pSession->beginDownload(file));
return new FTPSStream(istr, pSession);
}
catch (...)
{
delete pSession;
throw;
}
}
void FTPSStreamFactory::registerFactory()
{
URIStreamOpener::defaultOpener().registerStreamFactory("ftps", new FTPSStreamFactory);
}
void FTPSStreamFactory::unregisterFactory()
{
URIStreamOpener::defaultOpener().unregisterStreamFactory("ftps");
}
} } // namespace Poco::Net

View File

@@ -108,7 +108,7 @@ void FTPSClientSessionTest::testLogin1()
server.clearCommands(); server.clearCommands();
server.clearResponses(); server.clearResponses();
session.tryFTPSmode(true); session.enableFTPS(true);
login(server, session); login(server, session);
assertTrue (session.isOpen()); assertTrue (session.isOpen());
assertTrue (session.isLoggedIn()); assertTrue (session.isLoggedIn());
@@ -143,7 +143,7 @@ void FTPSClientSessionTest::testLogin2()
server.addResponse("331 Password required"); server.addResponse("331 Password required");
server.addResponse("230 Welcome"); server.addResponse("230 Welcome");
server.addResponse("200 Type set to I"); server.addResponse("200 Type set to I");
session.tryFTPSmode(true); session.enableFTPS(true);
session.open("127.0.0.1", serverPort, "user", "password"); session.open("127.0.0.1", serverPort, "user", "password");
assertTrue (session.isOpen()); assertTrue (session.isOpen());
assertTrue (session.isLoggedIn()); assertTrue (session.isLoggedIn());