diff --git a/Net/include/Poco/Net/FTPClientSession.h b/Net/include/Poco/Net/FTPClientSession.h index 4eeae4581..d177b672a 100644 --- a/Net/include/Poco/Net/FTPClientSession.h +++ b/Net/include/Poco/Net/FTPClientSession.h @@ -1,7 +1,7 @@ // // FTPClientSession.h // -// $Id: //poco/1.4/Net/include/Poco/Net/FTPClientSession.h#1 $ +// $Id: //poco/svn/Net/include/Poco/Net/FTPClientSession.h#2 $ // // Library: Net // Package: FTP @@ -80,15 +80,24 @@ public: TYPE_BINARY // TYPE I (Image) }; + FTPClientSession(); + /// Creates an FTPClientSession. + /// + /// Passive mode will be used for data transfers. + explicit FTPClientSession(const StreamSocket& socket); /// Creates an FTPClientSession using the given /// connected socket for the control connection. /// /// Passive mode will be used for data transfers. - FTPClientSession(const std::string& host, Poco::UInt16 port = FTP_PORT); + FTPClientSession(const std::string& host, + Poco::UInt16 port = FTP_PORT, + const std::string& username = "", + const std::string& password = ""); /// Creates an FTPClientSession using a socket connected - /// to the given host and port. + /// to the given host and port. If username is supplied, + /// login is attempted. /// /// Passive mode will be used for data transfers. @@ -112,6 +121,13 @@ public: bool getPassive() const; /// Returns true iff passive mode is enabled for this connection. + void open(const std::string& host, + Poco::UInt16 port, + const std::string& username = "", + const std::string& password = ""); + /// Opens the FTP connection to the given host and port. + /// If username is supplied, login is attempted. + void login(const std::string& username, const std::string& password); /// Authenticates the user against the FTP server. Must be /// called before any other commands (except QUIT) can be sent. @@ -122,8 +138,10 @@ public: /// Throws a FTPException in case of a FTP-specific error, or a /// NetException in case of a general network communication failure. + void logout(); + 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 /// NetException in case of a general network communication failure. @@ -300,6 +318,12 @@ public: /// Sends the given command verbatim to the server /// and waits for a response. + bool isOpen() const; + /// Returns true if the connection with FTP server is opened. + + bool isLoggedIn() const; + /// Returns true if the session is logged in. + protected: enum StatusClass { @@ -334,16 +358,18 @@ protected: void endTransfer(); private: - FTPClientSession(); FTPClientSession(const FTPClientSession&); FTPClientSession& operator = (const FTPClientSession&); - DialogSocket _controlSocket; + std::string _host; + Poco::UInt16 _port; + DialogSocket* _pControlSocket; SocketStream* _pDataStream; - bool _passiveMode; + bool _passiveMode; FileType _fileType; - bool _supports1738; - bool _isOpen; + bool _supports1738; + bool _serverReady; + bool _isLoggedIn; Poco::Timespan _timeout; }; @@ -381,6 +407,18 @@ inline bool FTPClientSession::isPermanentNegative(int status) } +inline bool FTPClientSession::isOpen() const +{ + return _pControlSocket != 0; +} + + +inline bool FTPClientSession::isLoggedIn() const +{ + return _isLoggedIn; +} + + } } // namespace Poco::Net diff --git a/Net/src/FTPClientSession.cpp b/Net/src/FTPClientSession.cpp index 700984351..ea57a7e9d 100644 --- a/Net/src/FTPClientSession.cpp +++ b/Net/src/FTPClientSession.cpp @@ -1,7 +1,7 @@ // // FTPClientSession.cpp // -// $Id: //poco/1.4/Net/src/FTPClientSession.cpp#1 $ +// $Id: //poco/svn/Net/src/FTPClientSession.cpp#2 $ // // Library: Net // Package: FTP @@ -50,48 +50,72 @@ namespace Poco { namespace Net { -FTPClientSession::FTPClientSession(const StreamSocket& socket): - _controlSocket(socket), +FTPClientSession::FTPClientSession(): + _port(0), + _pControlSocket(0), _pDataStream(0), _passiveMode(true), _fileType(TYPE_BINARY), _supports1738(true), - _isOpen(true), + _serverReady(false), + _isLoggedIn(false), _timeout(DEFAULT_TIMEOUT) { - _controlSocket.setReceiveTimeout(_timeout); } -FTPClientSession::FTPClientSession(const std::string& host, Poco::UInt16 port): - _controlSocket(SocketAddress(host, port)), +FTPClientSession::FTPClientSession(const StreamSocket& socket): + _host(socket.address().host().toString()), + _port(socket.address().port()), + _pControlSocket(new DialogSocket(socket)), _pDataStream(0), _passiveMode(true), _fileType(TYPE_BINARY), _supports1738(true), - _isOpen(true), + _serverReady(false), + _isLoggedIn(false), _timeout(DEFAULT_TIMEOUT) { - _controlSocket.setReceiveTimeout(_timeout); + _pControlSocket->setReceiveTimeout(_timeout); } +FTPClientSession::FTPClientSession(const std::string& host, + Poco::UInt16 port, + const std::string& username, + const std::string& password): + _host(host), + _port(port), + _pControlSocket(new DialogSocket(SocketAddress(host, port))), + _pDataStream(0), + _passiveMode(true), + _fileType(TYPE_BINARY), + _supports1738(true), + _serverReady(false), + _isLoggedIn(false), + _timeout(DEFAULT_TIMEOUT) + { + if (!username.empty()) + login(username, password); + else + _pControlSocket->setReceiveTimeout(_timeout); + } + + FTPClientSession::~FTPClientSession() -{ - try { - close(); - } - catch (...) - { - } + try { close(); } + catch (...) { } } void FTPClientSession::setTimeout(const Poco::Timespan& timeout) { + if (!isOpen()) + throw FTPException("Connection is closed."); + _timeout = timeout; - _controlSocket.setReceiveTimeout(timeout); + _pControlSocket->setReceiveTimeout(timeout); } @@ -114,35 +138,78 @@ bool FTPClientSession::getPassive() const } +void FTPClientSession::open(const std::string& host, + Poco::UInt16 port, + const std::string& username, + const std::string& password) +{ + _host = host; + _port = port; + if (!username.empty()) + login(username, password); + else + { + _pControlSocket = new DialogSocket(SocketAddress(_host, _port)); + _pControlSocket->setReceiveTimeout(_timeout); + } +} + + void FTPClientSession::login(const std::string& username, const std::string& password) { + if (_isLoggedIn) logout(); + + int status = FTP_POSITIVE_COMPLETION * 100; std::string response; - int status = _controlSocket.receiveStatusMessage(response); - if (!isPositiveCompletion(status)) throw FTPException("Cannot login to server", response, status); + if (!_pControlSocket) + { + _pControlSocket = new DialogSocket(SocketAddress(_host, _port)); + _pControlSocket->setReceiveTimeout(_timeout); + } + + if (!_serverReady) + { + status = _pControlSocket->receiveStatusMessage(response); + if (!isPositiveCompletion(status)) + throw FTPException("Cannot login to server", response, status); + + _serverReady = true; + } + status = sendCommand("USER", username, response); if (isPositiveIntermediate(status)) status = sendCommand("PASS", password, response); - if (!isPositiveCompletion(status)) throw FTPException("Login denied", response, status); + if (!isPositiveCompletion(status)) + throw FTPException("Login denied", response, status); + setFileType(_fileType); + _isLoggedIn = true; +} + + +void FTPClientSession::logout() + { + if (!isOpen()) + throw FTPException("Connection is closed."); + + if (_isLoggedIn) + { + try { endTransfer(); } + catch (...) { } + std::string response; + sendCommand("QUIT", response); + _isLoggedIn = false; + } } void FTPClientSession::close() { - if (_isOpen) - { - try - { - endTransfer(); - } - catch (...) - { - } - std::string response; - sendCommand("QUIT", response); - _controlSocket.close(); - _isOpen = false; - } + logout(); + _pControlSocket->close(); + delete _pControlSocket; + _pControlSocket = 0; + _serverReady = false; } @@ -176,7 +243,8 @@ void FTPClientSession::setWorkingDirectory(const std::string& path) { std::string response; int status = sendCommand("CWD", path, response); - if (!isPositiveCompletion(status)) throw FTPException("Cannot change directory", response, status); + if (!isPositiveCompletion(status)) + throw FTPException("Cannot change directory", response, status); } @@ -195,7 +263,8 @@ void FTPClientSession::cdup() { std::string response; int status = sendCommand("CDUP", response); - if (!isPositiveCompletion(status)) throw FTPException("Cannot change directory", response, status); + if (!isPositiveCompletion(status)) + throw FTPException("Cannot change directory", response, status); } @@ -203,9 +272,11 @@ void FTPClientSession::rename(const std::string& oldName, const std::string& new { std::string response; int status = sendCommand("RNFR", oldName, response); - if (!isPositiveIntermediate(status)) throw FTPException(std::string("Cannot rename ") + oldName, response, status); + if (!isPositiveIntermediate(status)) + throw FTPException(std::string("Cannot rename ") + oldName, response, status); status = sendCommand("RNTO", newName, response); - if (!isPositiveCompletion(status)) throw FTPException(std::string("Cannot rename to ") + newName, response, status); + if (!isPositiveCompletion(status)) + throw FTPException(std::string("Cannot rename to ") + newName, response, status); } @@ -213,7 +284,8 @@ void FTPClientSession::remove(const std::string& path) { std::string response; int status = sendCommand("DELE", path, response); - if (!isPositiveCompletion(status)) throw FTPException(std::string("Cannot remove " + path), response, status); + if (!isPositiveCompletion(status)) + throw FTPException(std::string("Cannot remove " + path), response, status); } @@ -221,7 +293,8 @@ void FTPClientSession::createDirectory(const std::string& path) { std::string response; int status = sendCommand("MKD", path, response); - if (!isPositiveCompletion(status)) throw FTPException(std::string("Cannot create directory ") + path, response, status); + if (!isPositiveCompletion(status)) + throw FTPException(std::string("Cannot create directory ") + path, response, status); } @@ -229,12 +302,16 @@ void FTPClientSession::removeDirectory(const std::string& path) { std::string response; int status = sendCommand("RMD", path, response); - if (!isPositiveCompletion(status)) throw FTPException(std::string("Cannot remove directory ") + path, response, status); + if (!isPositiveCompletion(status)) + throw FTPException(std::string("Cannot remove directory ") + path, response, status); } std::istream& FTPClientSession::beginDownload(const std::string& path) { + if (!isOpen()) + throw FTPException("Connection is closed."); + delete _pDataStream; _pDataStream = 0; _pDataStream = new SocketStream(establishDataConnection("RETR", path)); @@ -250,6 +327,9 @@ void FTPClientSession::endDownload() std::ostream& FTPClientSession::beginUpload(const std::string& path) { + if (!isOpen()) + throw FTPException("Connection is closed."); + delete _pDataStream; _pDataStream = 0; _pDataStream = new SocketStream(establishDataConnection("STOR", path)); @@ -265,6 +345,9 @@ void FTPClientSession::endUpload() std::istream& FTPClientSession::beginList(const std::string& path, bool extended) { + if (!isOpen()) + throw FTPException("Connection is closed."); + delete _pDataStream; _pDataStream = 0; _pDataStream = new SocketStream(establishDataConnection(extended ? "LIST" : "NLST", path)); @@ -280,27 +363,37 @@ void FTPClientSession::endList() void FTPClientSession::abort() { - _controlSocket.sendByte(DialogSocket::TELNET_IP); - _controlSocket.synch(); + if (!isOpen()) + throw FTPException("Connection is closed."); + + _pControlSocket->sendByte(DialogSocket::TELNET_IP); + _pControlSocket->synch(); std::string response; int status = sendCommand("ABOR", response); if (status == 426) - status = _controlSocket.receiveStatusMessage(response); - if (status != 226) throw FTPException("Cannot abort transfer", response, status); + status = _pControlSocket->receiveStatusMessage(response); + if (status != 226) + throw FTPException("Cannot abort transfer", response, status); } int FTPClientSession::sendCommand(const std::string& command, std::string& response) { - _controlSocket.sendMessage(command); - return _controlSocket.receiveStatusMessage(response); + if (!isOpen()) + throw FTPException("Connection is closed."); + + _pControlSocket->sendMessage(command); + return _pControlSocket->receiveStatusMessage(response); } int FTPClientSession::sendCommand(const std::string& command, const std::string& arg, std::string& response) { - _controlSocket.sendMessage(command, arg); - return _controlSocket.receiveStatusMessage(response); + if (!isOpen()) + throw FTPException("Connection is closed."); + + _pControlSocket->sendMessage(command, arg); + return _pControlSocket->receiveStatusMessage(response); } @@ -329,23 +422,24 @@ std::string FTPClientSession::extractPath(const std::string& response) StreamSocket FTPClientSession::establishDataConnection(const std::string& command, const std::string& arg) { - StreamSocket ss; if (_passiveMode) - ss = passiveDataConnection(command, arg); + return passiveDataConnection(command, arg); else - ss = activeDataConnection(command, arg); - ss.setReceiveTimeout(_timeout); - return ss; + return activeDataConnection(command, arg); } StreamSocket FTPClientSession::activeDataConnection(const std::string& command, const std::string& arg) { - ServerSocket server(SocketAddress(_controlSocket.address().host(), 0)); + if (!isOpen()) + throw FTPException("Connection is closed."); + + ServerSocket server(SocketAddress(_pControlSocket->address().host(), 0)); sendPortCommand(server.address()); std::string response; int status = sendCommand(command, arg, response); - if (!isPositivePreliminary(status)) throw FTPException(command + " command failed", response, status); + if (!isPositivePreliminary(status)) + throw FTPException(command + " command failed", response, status); if (server.poll(_timeout, Socket::SELECT_READ)) return server.acceptConnection(); else @@ -359,7 +453,8 @@ StreamSocket FTPClientSession::passiveDataConnection(const std::string& command, StreamSocket sock(sa); std::string response; int status = sendCommand(command, arg, response); - if (!isPositivePreliminary(status)) throw FTPException(command + " command failed", response, status); + if (!isPositivePreliminary(status)) + throw FTPException(command + " command failed", response, status); return sock; } @@ -426,7 +521,8 @@ void FTPClientSession::sendPORT(const SocketAddress& addr) arg += NumberFormatter::format(port % 256); std::string response; int status = sendCommand("PORT", arg, response); - if (!isPositiveCompletion(status)) throw FTPException("PORT command failed", response, status); + if (!isPositiveCompletion(status)) + throw FTPException("PORT command failed", response, status); } @@ -451,7 +547,8 @@ void FTPClientSession::sendPASV(SocketAddress& addr) { std::string response; int status = sendCommand("PASV", response); - if (!isPositiveCompletion(status)) throw FTPException("PASV command failed", response, status); + if (!isPositiveCompletion(status)) + throw FTPException("PASV command failed", response, status); parseAddress(response, addr); } @@ -492,7 +589,7 @@ void FTPClientSession::parseExtAddress(const std::string& str, SocketAddress& ad if (it != end && *it == delim) ++it; Poco::UInt16 port = 0; while (it != end && Poco::Ascii::isDigit(*it)) { port *= 10; port += *it++ - '0'; } - addr = SocketAddress(_controlSocket.peerAddress().host(), port); + addr = SocketAddress(_pControlSocket->peerAddress().host(), port); } @@ -503,8 +600,9 @@ void FTPClientSession::endTransfer() delete _pDataStream; _pDataStream = 0; std::string response; - int status = _controlSocket.receiveStatusMessage(response); - if (!isPositiveCompletion(status)) throw FTPException("Data transfer failed", response, status); + int status = _pControlSocket->receiveStatusMessage(response); + if (!isPositiveCompletion(status)) + throw FTPException("Data transfer failed", response, status); } } diff --git a/Net/testsuite/src/FTPClientSessionTest.cpp b/Net/testsuite/src/FTPClientSessionTest.cpp index 7d80eb8bb..d49bbc5d0 100644 --- a/Net/testsuite/src/FTPClientSessionTest.cpp +++ b/Net/testsuite/src/FTPClientSessionTest.cpp @@ -1,7 +1,7 @@ // // FTPClientSessionTest.cpp // -// $Id: //poco/1.4/Net/testsuite/src/FTPClientSessionTest.cpp#1 $ +// $Id: //poco/svn/Net/testsuite/src/FTPClientSessionTest.cpp#2 $ // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. @@ -93,14 +93,11 @@ FTPClientSessionTest::~FTPClientSessionTest() } -void FTPClientSessionTest::testLogin() +void FTPClientSessionTest::login(DialogServer& server, FTPClientSession& session) { - DialogServer server; - server.addResponse("220 localhost FTP ready"); server.addResponse("331 Password required"); server.addResponse("230 Welcome"); server.addResponse("200 Type set to I"); - FTPClientSession session("localhost", server.port()); session.login("user", "password"); std::string cmd = server.popCommand(); assert (cmd == "USER user"); @@ -110,12 +107,86 @@ void FTPClientSessionTest::testLogin() assert (cmd == "TYPE I"); assert (session.getFileType() == FTPClientSession::TYPE_BINARY); - +} + + +void FTPClientSessionTest::testLogin1() +{ + DialogServer server; + server.addResponse("220 localhost FTP ready"); + FTPClientSession session("localhost", server.port()); + assert (session.isOpen()); + assert (!session.isLoggedIn()); + login(server, session); + assert (session.isOpen()); + assert (session.isLoggedIn()); + server.addResponse("221 Good Bye"); + session.logout(); + assert (session.isOpen()); + assert (!session.isLoggedIn()); + + server.clearCommands(); + server.clearResponses(); + login(server, session); + assert (session.isOpen()); + assert (session.isLoggedIn()); server.addResponse("221 Good Bye"); session.close(); + assert (!session.isOpen()); + assert (!session.isLoggedIn()); } +void FTPClientSessionTest::testLogin2() +{ + DialogServer server; + server.addResponse("220 localhost FTP ready"); + server.addResponse("331 Password required"); + server.addResponse("230 Welcome"); + server.addResponse("200 Type set to I"); + FTPClientSession session("localhost", server.port(), "user", "password"); + assert (session.isOpen()); + assert (session.isLoggedIn()); + server.addResponse("221 Good Bye"); + session.close(); + assert (!session.isOpen()); + assert (!session.isLoggedIn()); + + server.clearCommands(); + server.clearResponses(); + server.addResponse("220 localhost FTP ready"); + server.addResponse("331 Password required"); + server.addResponse("230 Welcome"); + server.addResponse("200 Type set to I"); + session.open("localhost", server.port(), "user", "password"); + assert (session.isOpen()); + assert (session.isLoggedIn()); + server.addResponse("221 Good Bye"); + session.close(); + assert (!session.isOpen()); + assert (!session.isLoggedIn()); +} + + +void FTPClientSessionTest::testLogin3() +{ + DialogServer server; + server.addResponse("220 localhost FTP ready"); + server.addResponse("331 Password required"); + server.addResponse("230 Welcome"); + server.addResponse("200 Type set to I"); + FTPClientSession session; + assert (!session.isOpen()); + assert (!session.isLoggedIn()); + session.open("localhost", server.port(), "user", "password"); + server.addResponse("221 Good Bye"); + session.close(); + assert (!session.isOpen()); + assert (!session.isLoggedIn()); +} + + + void FTPClientSessionTest::testLoginFailed1() { DialogServer server; @@ -535,7 +606,9 @@ CppUnit::Test* FTPClientSessionTest::suite() { CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FTPClientSessionTest"); - CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin); + CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin1); + CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin2); + CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin3); CppUnit_addTest(pSuite, FTPClientSessionTest, testLoginFailed1); CppUnit_addTest(pSuite, FTPClientSessionTest, testLoginFailed2); CppUnit_addTest(pSuite, FTPClientSessionTest, testCommands); diff --git a/Net/testsuite/src/FTPClientSessionTest.h b/Net/testsuite/src/FTPClientSessionTest.h index e73eb0eba..aa6f62aaa 100644 --- a/Net/testsuite/src/FTPClientSessionTest.h +++ b/Net/testsuite/src/FTPClientSessionTest.h @@ -1,7 +1,7 @@ // // FTPClientSessionTest.h // -// $Id: //poco/1.4/Net/testsuite/src/FTPClientSessionTest.h#1 $ +// $Id: //poco/svn/Net/testsuite/src/FTPClientSessionTest.h#2 $ // // Definition of the FTPClientSessionTest class. // @@ -40,13 +40,24 @@ #include "CppUnit/TestCase.h" +namespace Poco { +namespace Net { + +class FTPClientSession; + +} } + +class DialogServer; + class FTPClientSessionTest: public CppUnit::TestCase { public: FTPClientSessionTest(const std::string& name); ~FTPClientSessionTest(); - void testLogin(); + void testLogin1(); + void testLogin2(); + void testLogin3(); void testLoginFailed1(); void testLoginFailed2(); void testCommands(); @@ -63,6 +74,7 @@ public: static CppUnit::Test* suite(); private: + void login(DialogServer& server, Poco::Net::FTPClientSession& session); }; diff --git a/Net/testsuite/src/FTPClientTestSuite.cpp b/Net/testsuite/src/FTPClientTestSuite.cpp index 6bf5a7f03..198e2aac1 100644 --- a/Net/testsuite/src/FTPClientTestSuite.cpp +++ b/Net/testsuite/src/FTPClientTestSuite.cpp @@ -1,7 +1,7 @@ // // FTPClientTestSuite.cpp // -// $Id: //poco/1.4/Net/testsuite/src/FTPClientTestSuite.cpp#1 $ +// $Id: //poco/svn/Net/testsuite/src/FTPClientTestSuite.cpp#2 $ // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. diff --git a/Net/testsuite/src/FTPClientTestSuite.h b/Net/testsuite/src/FTPClientTestSuite.h index ecb7ace85..262a91c3f 100644 --- a/Net/testsuite/src/FTPClientTestSuite.h +++ b/Net/testsuite/src/FTPClientTestSuite.h @@ -1,7 +1,7 @@ // // FTPClientTestSuite.h // -// $Id: //poco/1.4/Net/testsuite/src/FTPClientTestSuite.h#1 $ +// $Id: //poco/svn/Net/testsuite/src/FTPClientTestSuite.h#2 $ // // Definition of the FTPClientTestSuite class. //