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

@@ -51,40 +51,37 @@ public:
{ {
FTP_PORT = 21 FTP_PORT = 21
}; };
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();
/// Creates an FTPClientSession. /// Creates an FTPClientSession.
/// ///
/// 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.
/// ///
/// Passive mode will be used for data transfers. /// Passive mode will be used for data transfers.
virtual ~FTPClientSession(); virtual ~FTPClientSession();
/// Destroys the FTPClientSession. /// Destroys the FTPClientSession.
void setTimeout(const Poco::Timespan& timeout); void setTimeout(const Poco::Timespan& timeout);
/// Sets the timeout for socket operations. /// Sets the timeout for socket operations.
Poco::Timespan getTimeout() const; Poco::Timespan getTimeout() const;
/// Returns the timeout for socket operations. /// Returns the timeout for socket operations.
@@ -95,14 +92,11 @@ public:
/// EPSV command is used (with a fallback to PASV if EPSV fails) /// EPSV command is used (with a fallback to PASV if EPSV fails)
/// for switching to passive mode. The same applies to /// for switching to passive mode. The same applies to
/// EPRT and PORT for active connections. /// EPRT and PORT for active connections.
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,18 +111,21 @@ 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.
/// ///
/// Throws a FTPException in case of a FTP-specific error, or a /// Throws a FTPException in case of a FTP-specific error, or a
/// NetException in case of a general network communication failure. /// NetException in case of a general network communication failure.
std::string systemType(); std::string systemType();
/// Returns the system type of the FTP server. /// Returns the system type of the FTP server.
/// ///
/// Sends a SYST command to the server and returns the result. /// Sends a SYST command to the server and returns the result.
void setFileType(FileType type); void setFileType(FileType type);
/// Sets the file type for transferring files. /// Sets the file type for transferring files.
/// ///
@@ -144,7 +141,7 @@ public:
void setWorkingDirectory(const std::string& path); void setWorkingDirectory(const std::string& path);
/// Changes the current working directory on the server. /// Changes the current working directory on the server.
/// ///
/// Sends a CWD command with the given path as argument to the /// Sends a CWD command with the given path as argument to the
/// server. /// server.
/// ///
/// Throws a FTPException in case of a FTP-specific error, or a /// Throws a FTPException in case of a FTP-specific error, or a
@@ -155,7 +152,7 @@ public:
/// ///
/// Throws a FTPException in case of a FTP-specific error, or a /// Throws a FTPException in case of a FTP-specific error, or a
/// NetException in case of a general network communication failure. /// NetException in case of a general network communication failure.
void cdup(); void cdup();
/// Moves one directory up from the current working directory /// Moves one directory up from the current working directory
/// on the server. /// on the server.
@@ -164,7 +161,7 @@ public:
/// ///
/// Throws a FTPException in case of a FTP-specific error, or a /// Throws a FTPException in case of a FTP-specific error, or a
/// NetException in case of a general network communication failure. /// NetException in case of a general network communication failure.
void rename(const std::string& oldName, const std::string& newName); void rename(const std::string& oldName, const std::string& newName);
/// Renames the file on the server given by oldName to newName. /// Renames the file on the server given by oldName to newName.
/// ///
@@ -172,7 +169,7 @@ public:
/// ///
/// Throws a FTPException in case of a FTP-specific error, or a /// Throws a FTPException in case of a FTP-specific error, or a
/// NetException in case of a general network communication failure. /// NetException in case of a general network communication failure.
void remove(const std::string& path); void remove(const std::string& path);
/// Deletes the file specified by path on the server. /// Deletes the file specified by path on the server.
/// ///
@@ -217,11 +214,11 @@ public:
/// the native text file format. /// the native text file format.
/// The InputLineEndingConverter class from the Foundation /// The InputLineEndingConverter class from the Foundation
/// library can be used for that purpose. /// library can be used for that purpose.
void endDownload(); void endDownload();
/// Must be called to complete a download initiated with /// Must be called to complete a download initiated with
/// beginDownload(). /// beginDownload().
std::ostream& beginUpload(const std::string& path); std::ostream& beginUpload(const std::string& path);
/// Starts uploading the file with the given name. /// Starts uploading the file with the given name.
/// After all data has been written to the returned stream, /// After all data has been written to the returned stream,
@@ -274,9 +271,9 @@ public:
/// client waits for a connection request from the server. /// client waits for a connection request from the server.
/// After establishing the data connection, a SocketStream /// After establishing the data connection, a SocketStream
/// for transferring the data is created. /// for transferring the data is created.
void endList(); void endList();
/// Must be called to complete a directory listing download /// Must be called to complete a directory listing download
/// initiated with beginList(). /// initiated with beginList().
void abort(); void abort();
@@ -287,7 +284,7 @@ public:
/// ///
/// A separate call to endDownload() or endUpload() is /// A separate call to endDownload() or endUpload() is
/// not necessary. /// not necessary.
int sendCommand(const std::string& command, std::string& response); int sendCommand(const std::string& command, std::string& response);
/// Sends the given command verbatim to the server /// Sends the given command verbatim to the server
/// and waits for a response. /// and waits for a response.
@@ -304,13 +301,12 @@ public:
bool isSecure() const; bool isSecure() const;
/// 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,9 +316,10 @@ 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
}; };
static bool isPositivePreliminary(int status); static bool isPositivePreliminary(int status);
@@ -343,16 +340,16 @@ protected:
void parseAddress(const std::string& str, SocketAddress& addr); void parseAddress(const std::string& str, SocketAddress& addr);
void parseExtAddress(const std::string& str, SocketAddress& addr); void parseExtAddress(const std::string& str, SocketAddress& addr);
void endTransfer(); void endTransfer();
DialogSocket* _pControlSocket = nullptr; DialogSocket* _pControlSocket = nullptr;
SocketStream* _pDataStream = nullptr; SocketStream* _pDataStream = nullptr;
private: private:
FTPClientSession(const FTPClientSession&); FTPClientSession(const FTPClientSession&);
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;
@@ -360,7 +357,7 @@ private:
bool _isLoggedIn = false; bool _isLoggedIn = false;
Poco::Timespan _timeout = DEFAULT_TIMEOUT; Poco::Timespan _timeout = DEFAULT_TIMEOUT;
std::string _welcomeMessage; std::string _welcomeMessage;
Poco::FastMutex _wmMutex; Poco::FastMutex _wmMutex;
}; };

View File

@@ -30,8 +30,8 @@ namespace Net {
class Net_API FTPPasswordProvider 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();
@@ -64,13 +64,13 @@ public:
~FTPStreamFactory(); ~FTPStreamFactory();
/// Destroys the FTPStreamFactory. /// Destroys the FTPStreamFactory.
std::istream* open(const Poco::URI& uri); std::istream* open(const Poco::URI& uri);
/// Creates and opens a HTTP stream for the given URI. /// Creates and opens a HTTP stream for the given URI.
/// The URI must be a ftp://... URI. /// The URI must be a ftp://... URI.
/// ///
/// Throws a NetException if anything goes wrong. /// Throws a NetException if anything goes wrong.
static void setAnonymousPassword(const std::string& password); static void setAnonymousPassword(const std::string& password);
/// Sets the password used for anonymous FTP. /// Sets the password used for anonymous FTP.
/// ///
@@ -78,10 +78,10 @@ public:
/// thread-safe, so it's best to call this method /// thread-safe, so it's best to call this method
/// during application initialization, before the /// during application initialization, before the
/// FTPStreamFactory is used for the first time. /// FTPStreamFactory is used for the first time.
static const std::string& getAnonymousPassword(); static const std::string& getAnonymousPassword();
/// Returns the password used for anonymous FTP. /// Returns the password used for anonymous FTP.
static void setPasswordProvider(FTPPasswordProvider* pProvider); static void setPasswordProvider(FTPPasswordProvider* pProvider);
/// Sets the FTPPasswordProvider. If NULL is given, /// Sets the FTPPasswordProvider. If NULL is given,
/// no password provider is used. /// no password provider is used.
@@ -90,7 +90,7 @@ public:
/// thread-safe, so it's best to call this method /// thread-safe, so it's best to call this method
/// during application initialization, before the /// during application initialization, before the
/// FTPStreamFactory is used for the first time. /// FTPStreamFactory is used for the first time.
static FTPPasswordProvider* getPasswordProvider(); static FTPPasswordProvider* getPasswordProvider();
/// Returns the FTPPasswordProvider currently in use, /// Returns the FTPPasswordProvider currently in use,
/// or NULL if no one has been set. /// or NULL if no one has been set.
@@ -107,7 +107,7 @@ protected:
static void splitUserInfo(const std::string& userInfo, std::string& username, std::string& password); static void splitUserInfo(const std::string& userInfo, std::string& username, std::string& password);
static void getUserInfo(const Poco::URI& uri, std::string& username, std::string& password); static void getUserInfo(const Poco::URI& uri, std::string& username, std::string& password);
static void getPathAndType(const Poco::URI& uri, std::string& path, char& type); static void getPathAndType(const Poco::URI& uri, std::string& path, char& type);
private: private:
static std::string _anonymousPassword; static std::string _anonymousPassword;
static FTPPasswordProvider* _pPasswordProvider; static FTPPasswordProvider* _pPasswordProvider;

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),
@@ -41,7 +41,7 @@ FTPClientSession::FTPClientSession():
{ {
} }
FTPClientSession::FTPClientSession(const StreamSocket& socket, bool readWelcomeMessage): FTPClientSession::FTPClientSession(const StreamSocket& socket, bool readWelcomeMessage):
_pControlSocket(new DialogSocket(socket)), _pControlSocket(new DialogSocket(socket)),
_pDataStream(0), _pDataStream(0),
@@ -55,14 +55,14 @@ FTPClientSession::FTPClientSession(const StreamSocket& socket, bool readWelcomeM
_timeout(DEFAULT_TIMEOUT) _timeout(DEFAULT_TIMEOUT)
{ {
_pControlSocket->setReceiveTimeout(_timeout); _pControlSocket->setReceiveTimeout(_timeout);
if (readWelcomeMessage) if (readWelcomeMessage)
{ {
receiveServerReadyReply(); receiveServerReadyReply();
} }
else else
{ {
_serverReady = true; _serverReady = true;
} }
} }
@@ -93,7 +93,7 @@ FTPClientSession::~FTPClientSession()
{ {
close(); close();
} }
catch (...) catch (...)
{ {
} }
} }
@@ -108,7 +108,7 @@ void FTPClientSession::setTimeout(const Poco::Timespan& timeout)
_pControlSocket->setReceiveTimeout(timeout); _pControlSocket->setReceiveTimeout(timeout);
} }
Poco::Timespan FTPClientSession::getTimeout() const Poco::Timespan FTPClientSession::getTimeout() const
{ {
return _timeout; return _timeout;
@@ -121,7 +121,7 @@ void FTPClientSession::setPassive(bool flag, bool useRFC1738)
_supports1738 = useRFC1738; _supports1738 = useRFC1738;
} }
bool FTPClientSession::getPassive() const bool FTPClientSession::getPassive() const
{ {
return _passiveMode; return _passiveMode;
@@ -183,7 +183,7 @@ void FTPClientSession::login(const std::string& username, const std::string& pas
status = sendCommand("USER", username, response); status = sendCommand("USER", username, response);
if (isPositiveIntermediate(status)) if (isPositiveIntermediate(status))
status = sendCommand("PASS", password, response); status = sendCommand("PASS", password, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException("Login denied", response, status); throw FTPException("Login denied", response, status);
setFileType(_fileType); setFileType(_fileType);
@@ -198,12 +198,12 @@ void FTPClientSession::logout()
if (_isLoggedIn) if (_isLoggedIn)
{ {
try try
{ {
endTransfer(); endTransfer();
} }
catch (...) catch (...)
{ {
} }
_isLoggedIn = false; _isLoggedIn = false;
std::string response; std::string response;
@@ -214,11 +214,11 @@ void FTPClientSession::logout()
void FTPClientSession::close() void FTPClientSession::close()
{ {
try try
{ {
logout(); logout();
} }
catch (...) catch (...)
{ {
} }
_serverReady = false; _serverReady = false;
@@ -285,24 +285,24 @@ void FTPClientSession::cdup()
throw FTPException("Cannot change directory", response, status); throw FTPException("Cannot change directory", response, status);
} }
void FTPClientSession::rename(const std::string& oldName, const std::string& newName) void FTPClientSession::rename(const std::string& oldName, const std::string& newName)
{ {
std::string response; std::string response;
int status = sendCommand("RNFR", oldName, response); int status = sendCommand("RNFR", oldName, response);
if (!isPositiveIntermediate(status)) if (!isPositiveIntermediate(status))
throw FTPException(std::string("Cannot rename ") + oldName, response, status); throw FTPException(std::string("Cannot rename ") + oldName, response, status);
status = sendCommand("RNTO", newName, response); status = sendCommand("RNTO", newName, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException(std::string("Cannot rename to ") + newName, response, status); throw FTPException(std::string("Cannot rename to ") + newName, response, status);
} }
void FTPClientSession::remove(const std::string& path) void FTPClientSession::remove(const std::string& path)
{ {
std::string response; std::string response;
int status = sendCommand("DELE", path, response); int status = sendCommand("DELE", path, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException(std::string("Cannot remove " + path), response, status); throw FTPException(std::string("Cannot remove " + path), response, status);
} }
@@ -311,7 +311,7 @@ void FTPClientSession::createDirectory(const std::string& path)
{ {
std::string response; std::string response;
int status = sendCommand("MKD", path, response); int status = sendCommand("MKD", path, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException(std::string("Cannot create directory ") + path, response, status); throw FTPException(std::string("Cannot create directory ") + path, response, status);
} }
@@ -320,7 +320,7 @@ void FTPClientSession::removeDirectory(const std::string& path)
{ {
std::string response; std::string response;
int status = sendCommand("RMD", path, response); int status = sendCommand("RMD", path, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException(std::string("Cannot remove directory ") + path, response, status); throw FTPException(std::string("Cannot remove directory ") + path, response, status);
} }
@@ -336,13 +336,13 @@ std::istream& FTPClientSession::beginDownload(const std::string& path)
return *_pDataStream; return *_pDataStream;
} }
void FTPClientSession::endDownload() void FTPClientSession::endDownload()
{ {
endTransfer(); endTransfer();
} }
std::ostream& FTPClientSession::beginUpload(const std::string& path) std::ostream& FTPClientSession::beginUpload(const std::string& path)
{ {
if (!isOpen()) if (!isOpen())
@@ -378,7 +378,7 @@ void FTPClientSession::endList()
endTransfer(); endTransfer();
} }
void FTPClientSession::abort() void FTPClientSession::abort()
{ {
if (!isOpen()) if (!isOpen())
@@ -390,7 +390,7 @@ void FTPClientSession::abort()
int status = sendCommand("ABOR", response); int status = sendCommand("ABOR", response);
if (status == 426) if (status == 426)
status = _pControlSocket->receiveStatusMessage(response); status = _pControlSocket->receiveStatusMessage(response);
if (status != 226) if (status != 226)
throw FTPException("Cannot abort transfer", response, status); throw FTPException("Cannot abort transfer", response, status);
} }
@@ -456,7 +456,7 @@ StreamSocket FTPClientSession::activeDataConnection(const std::string& command,
sendPortCommand(server.address()); sendPortCommand(server.address());
std::string response; std::string response;
int status = sendCommand(command, arg, response); int status = sendCommand(command, arg, response);
if (!isPositivePreliminary(status)) if (!isPositivePreliminary(status))
throw FTPException(command + " command failed", response, status); throw FTPException(command + " command failed", response, status);
if (server.poll(_timeout, Socket::SELECT_READ)) if (server.poll(_timeout, Socket::SELECT_READ))
return server.acceptConnection(); return server.acceptConnection();
@@ -474,7 +474,7 @@ StreamSocket FTPClientSession::passiveDataConnection(const std::string& command,
sock.setSendTimeout(_timeout); sock.setSendTimeout(_timeout);
std::string response; std::string response;
int status = sendCommand(command, arg, response); int status = sendCommand(command, arg, response);
if (!isPositivePreliminary(status)) if (!isPositivePreliminary(status))
throw FTPException(command + " command failed", response, status); throw FTPException(command + " command failed", response, status);
return sock; return sock;
} }
@@ -542,7 +542,7 @@ void FTPClientSession::sendPORT(const SocketAddress& addr)
arg += NumberFormatter::format(port % 256); arg += NumberFormatter::format(port % 256);
std::string response; std::string response;
int status = sendCommand("PORT", arg, response); int status = sendCommand("PORT", arg, response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException("PORT command failed", response, status); throw FTPException("PORT command failed", response, status);
} }
@@ -568,7 +568,7 @@ void FTPClientSession::sendPASV(SocketAddress& addr)
{ {
std::string response; std::string response;
int status = sendCommand("PASV", response); int status = sendCommand("PASV", response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException("PASV command failed", response, status); throw FTPException("PASV command failed", response, status);
parseAddress(response, addr); parseAddress(response, addr);
} }
@@ -609,8 +609,8 @@ void FTPClientSession::parseExtAddress(const std::string& str, SocketAddress& ad
if (it != end && *it == delim) ++it; if (it != end && *it == delim) ++it;
if (it != end && *it == delim) ++it; if (it != end && *it == delim) ++it;
Poco::UInt16 port = 0; Poco::UInt16 port = 0;
while (it != end && Poco::Ascii::isDigit(*it)) { port *= 10; port += *it++ - '0'; } while (it != end && Poco::Ascii::isDigit(*it)) { port *= 10; port += *it++ - '0'; }
addr = SocketAddress(_pControlSocket->peerAddress().host(), port); addr = SocketAddress(_pControlSocket->peerAddress().host(), port);
} }
@@ -622,7 +622,7 @@ void FTPClientSession::endTransfer()
_pDataStream = 0; _pDataStream = 0;
std::string response; std::string response;
int status = _pControlSocket->receiveStatusMessage(response); int status = _pControlSocket->receiveStatusMessage(response);
if (!isPositiveCompletion(status)) if (!isPositiveCompletion(status))
throw FTPException("Data transfer failed", response, status); throw FTPException("Data transfer failed", response, status);
} }
} }

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());