poco/Net/src/FTPClientSession.cpp
2014-09-29 12:09:01 +02:00

598 lines
14 KiB
C++

//
// FTPClientSession.cpp
//
// $Id: //poco/svn/Net/src/FTPClientSession.cpp#2 $
//
// Library: Net
// Package: FTP
// Module: FTPClientSession
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/FTPClientSession.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/NetException.h"
#include "Poco/NumberFormatter.h"
#include "Poco/Ascii.h"
using Poco::NumberFormatter;
namespace Poco {
namespace Net {
FTPClientSession::FTPClientSession():
_port(0),
_pControlSocket(0),
_pDataStream(0),
_passiveMode(true),
_fileType(TYPE_BINARY),
_supports1738(true),
_serverReady(false),
_isLoggedIn(false),
_timeout(DEFAULT_TIMEOUT)
{
}
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),
_serverReady(false),
_isLoggedIn(false),
_timeout(DEFAULT_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 (...)
{
}
}
void FTPClientSession::setTimeout(const Poco::Timespan& timeout)
{
if (!isOpen())
throw FTPException("Connection is closed.");
_timeout = timeout;
_pControlSocket->setReceiveTimeout(timeout);
}
Poco::Timespan FTPClientSession::getTimeout() const
{
return _timeout;
}
void FTPClientSession::setPassive(bool flag, bool useRFC1738)
{
_passiveMode = flag;
_supports1738 = useRFC1738;
}
bool FTPClientSession::getPassive() const
{
return _passiveMode;
}
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;
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);
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()
{
logout();
_pControlSocket->close();
delete _pControlSocket;
_pControlSocket = 0;
_serverReady = false;
}
void FTPClientSession::setFileType(FTPClientSession::FileType type)
{
std::string response;
int status = sendCommand("TYPE", (type == TYPE_TEXT ? "A" : "I"), response);
if (!isPositiveCompletion(status)) throw FTPException("Cannot set file type", response, status);
_fileType = type;
}
FTPClientSession::FileType FTPClientSession::getFileType() const
{
return _fileType;
}
std::string FTPClientSession::systemType()
{
std::string response;
int status = sendCommand("SYST", response);
if (isPositiveCompletion(status))
return response.substr(4);
else
throw FTPException("Cannot get remote system type", response, status);
}
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);
}
std::string FTPClientSession::getWorkingDirectory()
{
std::string response;
int status = sendCommand("PWD", response);
if (isPositiveCompletion(status))
return extractPath(response);
else
throw FTPException("Cannot get current working directory", response, status);
}
void FTPClientSession::cdup()
{
std::string response;
int status = sendCommand("CDUP", response);
if (!isPositiveCompletion(status))
throw FTPException("Cannot change directory", response, status);
}
void FTPClientSession::rename(const std::string& oldName, const std::string& newName)
{
std::string response;
int status = sendCommand("RNFR", oldName, response);
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);
}
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);
}
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);
}
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);
}
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));
return *_pDataStream;
}
void FTPClientSession::endDownload()
{
endTransfer();
}
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));
return *_pDataStream;
}
void FTPClientSession::endUpload()
{
endTransfer();
}
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));
return *_pDataStream;
}
void FTPClientSession::endList()
{
endTransfer();
}
void FTPClientSession::abort()
{
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 = _pControlSocket->receiveStatusMessage(response);
if (status != 226)
throw FTPException("Cannot abort transfer", response, status);
}
int FTPClientSession::sendCommand(const std::string& command, std::string& 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)
{
if (!isOpen())
throw FTPException("Connection is closed.");
_pControlSocket->sendMessage(command, arg);
return _pControlSocket->receiveStatusMessage(response);
}
std::string FTPClientSession::extractPath(const std::string& response)
{
std::string path;
std::string::const_iterator it = response.begin();
std::string::const_iterator end = response.end();
while (it != end && *it != '"') ++it;
if (it != end)
{
++it;
while (it != end)
{
if (*it == '"')
{
++it;
if (it == end || (it != end && *it != '"')) break;
}
path += *it++;
}
}
return path;
}
StreamSocket FTPClientSession::establishDataConnection(const std::string& command, const std::string& arg)
{
if (_passiveMode)
return passiveDataConnection(command, arg);
else
return activeDataConnection(command, arg);
}
StreamSocket FTPClientSession::activeDataConnection(const std::string& command, const std::string& arg)
{
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 (server.poll(_timeout, Socket::SELECT_READ))
return server.acceptConnection();
else
throw FTPException("The server has not initiated a data connection");
}
StreamSocket FTPClientSession::passiveDataConnection(const std::string& command, const std::string& arg)
{
SocketAddress sa(sendPassiveCommand());
StreamSocket sock(sa);
std::string response;
int status = sendCommand(command, arg, response);
if (!isPositivePreliminary(status))
throw FTPException(command + " command failed", response, status);
return sock;
}
void FTPClientSession::sendPortCommand(const SocketAddress& addr)
{
if (_supports1738)
{
if (sendEPRT(addr))
return;
else
_supports1738 = false;
}
sendPORT(addr);
}
SocketAddress FTPClientSession::sendPassiveCommand()
{
SocketAddress addr;
if (_supports1738)
{
if (sendEPSV(addr))
return addr;
else
_supports1738 = false;
}
sendPASV(addr);
return addr;
}
bool FTPClientSession::sendEPRT(const SocketAddress& addr)
{
std::string arg("|");
arg += addr.af() == AF_INET ? '1' : '2';
arg += '|';
arg += addr.host().toString();
arg += '|';
arg += NumberFormatter::format(addr.port());
arg += '|';
std::string response;
int status = sendCommand("EPRT", arg, response);
if (isPositiveCompletion(status))
return true;
else if (isPermanentNegative(status))
return false;
else
throw FTPException("EPRT command failed", response, status);
}
void FTPClientSession::sendPORT(const SocketAddress& addr)
{
std::string arg(addr.host().toString());
for (std::string::iterator it = arg.begin(); it != arg.end(); ++it)
{
if (*it == '.') *it = ',';
}
arg += ',';
Poco::UInt16 port = addr.port();
arg += NumberFormatter::format(port/256);
arg += ',';
arg += NumberFormatter::format(port % 256);
std::string response;
int status = sendCommand("PORT", arg, response);
if (!isPositiveCompletion(status))
throw FTPException("PORT command failed", response, status);
}
bool FTPClientSession::sendEPSV(SocketAddress& addr)
{
std::string response;
int status = sendCommand("EPSV", response);
if (isPositiveCompletion(status))
{
parseExtAddress(response, addr);
return true;
}
else if (isPermanentNegative(status))
{
return false;
}
else throw FTPException("EPSV command failed", response, status);
}
void FTPClientSession::sendPASV(SocketAddress& addr)
{
std::string response;
int status = sendCommand("PASV", response);
if (!isPositiveCompletion(status))
throw FTPException("PASV command failed", response, status);
parseAddress(response, addr);
}
void FTPClientSession::parseAddress(const std::string& str, SocketAddress& addr)
{
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while (it != end && *it != '(') ++it;
if (it != end) ++it;
std::string host;
while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
if (it != end && *it == ',') { host += '.'; ++it; }
while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
if (it != end && *it == ',') { host += '.'; ++it; }
while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
if (it != end && *it == ',') { host += '.'; ++it; }
while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
if (it != end && *it == ',') ++it;
Poco::UInt16 portHi = 0;
while (it != end && Poco::Ascii::isDigit(*it)) { portHi *= 10; portHi += *it++ - '0'; }
if (it != end && *it == ',') ++it;
Poco::UInt16 portLo = 0;
while (it != end && Poco::Ascii::isDigit(*it)) { portLo *= 10; portLo += *it++ - '0'; }
addr = SocketAddress(host, portHi*256 + portLo);
}
void FTPClientSession::parseExtAddress(const std::string& str, SocketAddress& addr)
{
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while (it != end && *it != '(') ++it;
if (it != end) ++it;
char delim = '|';
if (it != end) delim = *it++;
if (it != end && *it == delim) ++it;
if (it != end && *it == delim) ++it;
Poco::UInt16 port = 0;
while (it != end && Poco::Ascii::isDigit(*it)) { port *= 10; port += *it++ - '0'; }
addr = SocketAddress(_pControlSocket->peerAddress().host(), port);
}
void FTPClientSession::endTransfer()
{
if (_pDataStream)
{
delete _pDataStream;
_pDataStream = 0;
std::string response;
int status = _pControlSocket->receiveStatusMessage(response);
if (!isPositiveCompletion(status))
throw FTPException("Data transfer failed", response, status);
}
}
} } // namespace Poco::Net