summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2015-05-12 00:55:12 +0900
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2015-05-12 00:55:12 +0900
commit89c1916fa57d87ea09142cc3588044c691d1928f (patch)
treeea35f4171e9e5b85e7336688b6089786a9c6701d /src
parent4654f1974a5faa832518ba3875ecb3e77224e656 (diff)
parent2758fba10c79a76e07f7cfb894f5cfcbaff85ff7 (diff)
Merge branch 'sftp'
Diffstat (limited to 'src')
-rw-r--r--src/AbstractCommand.cc5
-rw-r--r--src/AuthConfigFactory.cc3
-rw-r--r--src/DownloadCommand.cc10
-rw-r--r--src/DownloadCommand.h6
-rw-r--r--src/FeatureConfig.cc19
-rw-r--r--src/FeatureConfig.h1
-rw-r--r--src/FtpInitiateConnectionCommand.cc89
-rw-r--r--src/FtpTunnelResponseCommand.cc12
-rw-r--r--src/InitiateConnectionCommandFactory.cc8
-rw-r--r--src/Makefile.am8
-rw-r--r--src/MetalinkParserController.cc2
-rw-r--r--src/Platform.cc17
-rw-r--r--src/SSHSession.cc240
-rw-r--r--src/SSHSession.h137
-rw-r--r--src/SftpDownloadCommand.cc99
-rw-r--r--src/SftpDownloadCommand.h66
-rw-r--r--src/SftpFinishDownloadCommand.cc119
-rw-r--r--src/SftpFinishDownloadCommand.h59
-rw-r--r--src/SftpNegotiationCommand.cc320
-rw-r--r--src/SftpNegotiationCommand.h87
-rw-r--r--src/SftpNegotiationConnectChain.h66
-rw-r--r--src/SocketCore.cc161
-rw-r--r--src/SocketCore.h26
23 files changed, 1526 insertions, 34 deletions
diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc
index 2df0141b..ba1297bc 100644
--- a/src/AbstractCommand.cc
+++ b/src/AbstractCommand.cc
@@ -677,7 +677,7 @@ std::string getProxyUri(const std::string& protocol, const Option* option)
option);
}
- if (protocol == "ftp") {
+ if (protocol == "ftp" || protocol == "sftp") {
return getProxyOptionFor
(PREF_FTP_PROXY, PREF_FTP_PROXY_USER, PREF_FTP_PROXY_PASSWD, option);
}
@@ -883,7 +883,8 @@ bool AbstractCommand::checkIfConnectionEstablished
const std::string&
AbstractCommand::resolveProxyMethod(const std::string& protocol) const
{
- if (getOption()->get(PREF_PROXY_METHOD) == V_TUNNEL || protocol == "https") {
+ if (getOption()->get(PREF_PROXY_METHOD) == V_TUNNEL || protocol == "https" ||
+ protocol == "sftp") {
return V_TUNNEL;
}
return V_GET;
diff --git a/src/AuthConfigFactory.cc b/src/AuthConfigFactory.cc
index e8d55b04..2f3c0573 100644
--- a/src/AuthConfigFactory.cc
+++ b/src/AuthConfigFactory.cc
@@ -87,7 +87,8 @@ AuthConfigFactory::createAuthConfig
createHttpAuthResolver(op)->resolveAuthConfig(request->getHost());
}
}
- } else if(request->getProtocol() == "ftp") {
+ } else if(request->getProtocol() == "ftp" ||
+ request->getProtocol() == "sftp") {
if(!request->getUsername().empty()) {
if(request->hasPassword()) {
return AuthConfig::create(request->getUsername(),
diff --git a/src/DownloadCommand.cc b/src/DownloadCommand.cc
index 5be8d551..20337611 100644
--- a/src/DownloadCommand.cc
+++ b/src/DownloadCommand.cc
@@ -134,6 +134,7 @@ bool DownloadCommand::executeInternal() {
|| getRequestGroup()->doesDownloadSpeedExceed()) {
addCommandSelf();
disableReadCheckSocket();
+ disableWriteCheckSocket();
return false;
}
setReadCheckSocket(getSocket());
@@ -195,7 +196,8 @@ bool DownloadCommand::executeInternal() {
// Note that GrowSegment::complete() always returns false.
if(sinkFilterOnly_) {
if(segment->complete() ||
- segment->getPositionToWrite() == getFileEntry()->getLastOffset()) {
+ (getFileEntry()->getLength() != 0 &&
+ segment->getPositionToWrite() == getFileEntry()->getLastOffset())) {
segmentPartComplete = true;
} else if(segment->getLength() == 0 && eof) {
segmentPartComplete = true;
@@ -275,13 +277,17 @@ bool DownloadCommand::executeInternal() {
return prepareForNextSegment();
} else {
checkLowestDownloadSpeed();
- setWriteCheckSocketIf(getSocket(), getSocket()->wantWrite());
+ setWriteCheckSocketIf(getSocket(), shouldEnableWriteCheck());
checkSocketRecvBuffer();
addCommandSelf();
return false;
}
}
+bool DownloadCommand::shouldEnableWriteCheck() {
+ return getSocket()->wantWrite();
+}
+
void DownloadCommand::checkLowestDownloadSpeed() const
{
if(lowestDownloadSpeedLimit_ > 0 &&
diff --git a/src/DownloadCommand.h b/src/DownloadCommand.h
index 3efaf5a0..485cdc74 100644
--- a/src/DownloadCommand.h
+++ b/src/DownloadCommand.h
@@ -76,6 +76,12 @@ protected:
// This is file local offset
virtual int64_t getRequestEndOffset() const = 0;
+
+ // Returns true if socket should be monitored for writing. The
+ // default implementation is return the return value of
+ // getSocket()->wantWrite().
+ virtual bool shouldEnableWriteCheck();
+
public:
DownloadCommand(cuid_t cuid,
const std::shared_ptr<Request>& req,
diff --git a/src/FeatureConfig.cc b/src/FeatureConfig.cc
index 1d2d30bb..1b5b183a 100644
--- a/src/FeatureConfig.cc
+++ b/src/FeatureConfig.cc
@@ -67,7 +67,9 @@
#ifdef HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif // HAVE_SYS_UTSNAME_H
-
+#ifdef HAVE_LIBSSH2
+# include <libssh2.h>
+#endif // HAVE_LIBSSH2
#include "util.h"
namespace aria2 {
@@ -80,6 +82,8 @@ uint16_t getDefaultPort(const std::string& protocol)
return 443;
} else if(protocol == "ftp") {
return 21;
+ } else if(protocol == "sftp") {
+ return 22;
} else {
return 0;
}
@@ -166,6 +170,14 @@ const char* strSupportedFeature(int feature)
#endif // !ENABLE_XML_RPC
break;
+ case(FEATURE_SFTP):
+#ifdef HAVE_LIBSSH2
+ return "SFTP";
+#else // !HAVE_LIBSSH2
+ return nullptr;
+#endif // !HAVE_LIBSSH2
+ break;
+
default:
return nullptr;
}
@@ -221,6 +233,11 @@ std::string usedLibs()
#ifdef HAVE_LIBCARES
res += "c-ares/" ARES_VERSION_STR " ";
#endif // HAVE_LIBCARES
+
+#ifdef HAVE_LIBSSH2
+ res += "libssh2/" LIBSSH2_VERSION " ";
+#endif // HAVE_LIBSSH2
+
if(!res.empty()) {
res.erase(res.length()-1);
}
diff --git a/src/FeatureConfig.h b/src/FeatureConfig.h
index 9c0a2798..214dca25 100644
--- a/src/FeatureConfig.h
+++ b/src/FeatureConfig.h
@@ -53,6 +53,7 @@ enum FeatureType {
FEATURE_MESSAGE_DIGEST,
FEATURE_METALINK,
FEATURE_XML_RPC,
+ FEATURE_SFTP,
MAX_FEATURE
};
diff --git a/src/FtpInitiateConnectionCommand.cc b/src/FtpInitiateConnectionCommand.cc
index 52fc07a4..ae3e20e1 100644
--- a/src/FtpInitiateConnectionCommand.cc
+++ b/src/FtpInitiateConnectionCommand.cc
@@ -60,6 +60,10 @@
#include "FtpNegotiationConnectChain.h"
#include "FtpTunnelRequestConnectChain.h"
#include "HttpRequestConnectChain.h"
+#ifdef HAVE_LIBSSH2
+# include "SftpNegotiationConnectChain.h"
+# include "SftpNegotiationCommand.h"
+#endif // HAVE_LIBSSH2
namespace aria2 {
@@ -82,6 +86,7 @@ std::unique_ptr<Command> FtpInitiateConnectionCommand::createNextCommandProxied
std::shared_ptr<SocketCore> pooledSocket;
std::string proxyMethod = resolveProxyMethod(getRequest()->getProtocol());
+ // sftp always use tunnel mode
if(proxyMethod == V_GET) {
pooledSocket = getDownloadEngine()->popPooledSocket
(getRequest()->getHost(), getRequest()->getPort(),
@@ -127,18 +132,33 @@ std::unique_ptr<Command> FtpInitiateConnectionCommand::createNextCommandProxied
setConnectedAddrInfo(getRequest(), hostname, pooledSocket);
if(proxyMethod == V_TUNNEL) {
+#ifdef HAVE_LIBSSH2
+ if (getRequest()->getProtocol() == "sftp") {
+ return make_unique<SftpNegotiationCommand>
+ (getCuid(),
+ getRequest(),
+ getFileEntry(),
+ getRequestGroup(),
+ getDownloadEngine(),
+ pooledSocket,
+ SftpNegotiationCommand::SEQ_SFTP_OPEN);
+ }
+#endif // HAVE_LIBSSH2
+
// options contains "baseWorkingDir"
return make_unique<FtpNegotiationCommand>
(getCuid(),
- getRequest(),
- getFileEntry(),
- getRequestGroup(),
- getDownloadEngine(),
- pooledSocket,
- FtpNegotiationCommand::SEQ_SEND_CWD_PREP,
- options);
+ getRequest(),
+ getFileEntry(),
+ getRequestGroup(),
+ getDownloadEngine(),
+ pooledSocket,
+ FtpNegotiationCommand::SEQ_SEND_CWD_PREP,
+ options);
}
+ assert(getRequest()->getProtocol() == "ftp");
+
if(proxyMethod != V_GET) {
assert(0);
return nullptr;
@@ -180,30 +200,51 @@ std::unique_ptr<Command> FtpInitiateConnectionCommand::createNextCommandPlain
getSocket()->establishConnection(addr, port);
getRequest()->setConnectedAddrInfo(hostname, addr, port);
auto c = make_unique<ConnectCommand>(getCuid(),
- getRequest(),
- nullptr,
- getFileEntry(),
- getRequestGroup(),
- getDownloadEngine(),
- getSocket());
+ getRequest(),
+ nullptr,
+ getFileEntry(),
+ getRequestGroup(),
+ getDownloadEngine(),
+ getSocket());
- c->setControlChain(std::make_shared<FtpNegotiationConnectChain>());
+ if(getRequest()->getProtocol() == "sftp") {
+#ifdef HAVE_LIBSSH2
+ c->setControlChain(std::make_shared<SftpNegotiationConnectChain>());
+#else // !HAVE_LIBSSH2
+ assert(0);
+#endif // !HAVE_LIBSSH2
+ } else {
+ c->setControlChain(std::make_shared<FtpNegotiationConnectChain>());
+ }
setupBackupConnection(hostname, addr, port, c.get());
return std::move(c);
}
+ setConnectedAddrInfo(getRequest(), hostname, pooledSocket);
+
+#ifdef HAVE_LIBSSH2
+ if (getRequest()->getProtocol() == "sftp") {
+ return make_unique<SftpNegotiationCommand>
+ (getCuid(),
+ getRequest(),
+ getFileEntry(),
+ getRequestGroup(),
+ getDownloadEngine(),
+ pooledSocket,
+ SftpNegotiationCommand::SEQ_SFTP_OPEN);
+ }
+#endif // HAVE_LIBSSH2
+
// options contains "baseWorkingDir"
- auto command = make_unique<FtpNegotiationCommand>
+ return make_unique<FtpNegotiationCommand>
(getCuid(),
- getRequest(),
- getFileEntry(),
- getRequestGroup(),
- getDownloadEngine(),
- pooledSocket,
- FtpNegotiationCommand::SEQ_SEND_CWD_PREP,
- options);
- setConnectedAddrInfo(getRequest(), hostname, pooledSocket);
- return std::move(command);
+ getRequest(),
+ getFileEntry(),
+ getRequestGroup(),
+ getDownloadEngine(),
+ pooledSocket,
+ FtpNegotiationCommand::SEQ_SEND_CWD_PREP,
+ options);
}
std::unique_ptr<Command> FtpInitiateConnectionCommand::createNextCommand
diff --git a/src/FtpTunnelResponseCommand.cc b/src/FtpTunnelResponseCommand.cc
index ed626f0d..057a5e89 100644
--- a/src/FtpTunnelResponseCommand.cc
+++ b/src/FtpTunnelResponseCommand.cc
@@ -40,6 +40,9 @@
#include "Segment.h"
#include "SocketCore.h"
#include "SocketRecvBuffer.h"
+#ifdef HAVE_LIBSSH2
+# include "SftpNegotiationCommand.h"
+#endif // HAVE_LIBSSH2
namespace aria2 {
@@ -59,6 +62,15 @@ FtpTunnelResponseCommand::~FtpTunnelResponseCommand() {}
std::unique_ptr<Command> FtpTunnelResponseCommand::getNextCommand()
{
+#ifdef HAVE_LIBSSH2
+ if (getRequest()->getProtocol() == "sftp") {
+ return make_unique<SftpNegotiationCommand>
+ (getCuid(), getRequest(), getFileEntry(),
+ getRequestGroup(), getDownloadEngine(),
+ getSocket());
+ }
+#endif // HAVE_LIBSSH2
+
return make_unique<FtpNegotiationCommand>
(getCuid(), getRequest(), getFileEntry(),
getRequestGroup(), getDownloadEngine(),
diff --git a/src/InitiateConnectionCommandFactory.cc b/src/InitiateConnectionCommandFactory.cc
index 2856b244..b4615d7a 100644
--- a/src/InitiateConnectionCommandFactory.cc
+++ b/src/InitiateConnectionCommandFactory.cc
@@ -71,10 +71,14 @@ InitiateConnectionCommandFactory::createInitiateConnectionCommand
return make_unique<HttpInitiateConnectionCommand>(cuid, req, fileEntry,
requestGroup, e);
- } else if(req->getProtocol() == "ftp") {
+ } else if(req->getProtocol() == "ftp"
+#ifdef HAVE_LIBSSH2
+ || req->getProtocol() == "sftp"
+#endif // HAVE_LIBSSH2
+ ) {
if(req->getFile().empty()) {
throw DL_ABORT_EX
- (fmt("FTP URI %s doesn't contain file path.",
+ (fmt("FTP/SFTP URI %s doesn't contain file path.",
req->getUri().c_str()));
}
return make_unique<FtpInitiateConnectionCommand>(cuid, req, fileEntry,
diff --git a/src/Makefile.am b/src/Makefile.am
index 236a055e..bfa99857 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -427,6 +427,14 @@ SRCS += \
Sqlite3CookieParserImpl.cc Sqlite3CookieParserImpl.h
endif # HAVE_SQLITE3
+if HAVE_LIBSSH2
+SRCS += SSHSession.cc SSHSession.h \
+ SftpNegotiationCommand.cc SftpNegotiationCommand.h \
+ SftpNegotiationConnectChain.h \
+ SftpDownloadCommand.cc SftpDownloadCommand.h \
+ SftpFinishDownloadCommand.cc SftpFinishDownloadCommand.h
+endif # HAVE_LIBSSH2
+
if ENABLE_ASYNC_DNS
SRCS += \
AsyncNameResolver.cc AsyncNameResolver.h\
diff --git a/src/MetalinkParserController.cc b/src/MetalinkParserController.cc
index 91ce7053..fec63aa0 100644
--- a/src/MetalinkParserController.cc
+++ b/src/MetalinkParserController.cc
@@ -193,7 +193,7 @@ void MetalinkParserController::setTypeOfResource(std::string type)
if(!tResource_) {
return;
}
- if(type == "ftp") {
+ if(type == "ftp" || type == "sftp") {
tResource_->type = MetalinkResource::TYPE_FTP;
} else if(type == "http") {
tResource_->type = MetalinkResource::TYPE_HTTP;
diff --git a/src/Platform.cc b/src/Platform.cc
index d93c48eb..8520440d 100644
--- a/src/Platform.cc
+++ b/src/Platform.cc
@@ -56,6 +56,10 @@
# include <ares.h>
#endif // ENABLE_ASYNC_DNS
+#ifdef HAVE_LIBSSH2
+# include <libssh2.h>
+#endif // HAVE_LIBSSH2
+
#include "a2netcompat.h"
#include "DlAbortEx.h"
#include "message.h"
@@ -149,6 +153,15 @@ bool Platform::setUp()
}
#endif // CARES_HAVE_ARES_LIBRARY_INIT
+#ifdef HAVE_LIBSSH2
+ {
+ auto rv = libssh2_init(0);
+ if (rv != 0) {
+ throw DL_ABORT_EX(fmt("libssh2_init() failed, code: %d", rv));
+ }
+ }
+#endif // HAVE_LIBSSH2
+
#ifdef HAVE_WINSOCK2_H
WSADATA wsaData;
memset(reinterpret_cast<char*>(&wsaData), 0, sizeof(wsaData));
@@ -181,6 +194,10 @@ bool Platform::tearDown()
ares_library_cleanup();
#endif // CARES_HAVE_ARES_LIBRARY_CLEANUP
+#ifdef HAVE_LIBSSH2
+ libssh2_exit();
+#endif // HAVE_LIBSSH2
+
#ifdef HAVE_WINSOCK2_H
WSACleanup();
#endif // HAVE_WINSOCK2_H
diff --git a/src/SSHSession.cc b/src/SSHSession.cc
new file mode 100644
index 00000000..1b939de6
--- /dev/null
+++ b/src/SSHSession.cc
@@ -0,0 +1,240 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "SSHSession.h"
+
+#include <cassert>
+
+namespace aria2 {
+
+SSHSession::SSHSession()
+ : ssh2_(nullptr),
+ sftp_(nullptr),
+ sftph_(nullptr),
+ fd_(-1)
+{}
+
+SSHSession::~SSHSession()
+{
+ closeConnection();
+}
+
+int SSHSession::closeConnection()
+{
+ if (sftph_) {
+ // TODO this could return LIBSSH2_ERROR_EAGAIN
+ libssh2_sftp_close(sftph_);
+ sftph_ = nullptr;
+ }
+ if (sftp_) {
+ // TODO this could return LIBSSH2_ERROR_EAGAIN
+ libssh2_sftp_shutdown(sftp_);
+ sftp_ = nullptr;
+ }
+ if (ssh2_) {
+ // TODO this could return LIBSSH2_ERROR_EAGAIN
+ libssh2_session_disconnect(ssh2_, "bye");
+ libssh2_session_free(ssh2_);
+ ssh2_ = nullptr;
+ }
+ return SSH_ERR_OK;
+}
+
+int SSHSession::gracefulShutdown()
+{
+ if (sftph_) {
+ auto rv = libssh2_sftp_close(sftph_);
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ sftph_ = nullptr;
+ }
+ if (sftp_) {
+ auto rv = libssh2_sftp_shutdown(sftp_);
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ sftp_ = nullptr;
+ }
+ if (ssh2_) {
+ auto rv = libssh2_session_disconnect(ssh2_, "bye");
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ libssh2_session_free(ssh2_);
+ ssh2_ = nullptr;
+ }
+ return SSH_ERR_OK;
+}
+
+int SSHSession::sftpClose()
+{
+ if (!sftph_) {
+ return SSH_ERR_OK;
+ }
+
+ auto rv = libssh2_sftp_close(sftph_);
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ sftph_ = nullptr;
+ return SSH_ERR_OK;
+}
+
+int SSHSession::init(sock_t sockfd)
+{
+ ssh2_ = libssh2_session_init();
+ if (!ssh2_) {
+ return SSH_ERR_ERROR;
+ }
+ libssh2_session_set_blocking(ssh2_, 0);
+ fd_ = sockfd;
+ return SSH_ERR_OK;
+}
+
+
+int SSHSession::checkDirection()
+{
+ auto dir = libssh2_session_block_directions(ssh2_);
+ if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ return SSH_WANT_WRITE;
+ }
+ return SSH_WANT_READ;
+}
+
+ssize_t SSHSession::writeData(const void* data, size_t len)
+{
+ // net implemented yet
+ assert(0);
+}
+
+ssize_t SSHSession::readData(void* data, size_t len)
+{
+ auto nread = libssh2_sftp_read(sftph_, static_cast<char*>(data), len);
+ if (nread == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (nread < 0) {
+ return SSH_ERR_ERROR;
+ }
+ return nread;
+}
+
+int SSHSession::handshake()
+{
+ auto rv = libssh2_session_handshake(ssh2_, fd_);
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ // TODO we have to validate server's fingerprint
+ return SSH_ERR_OK;
+}
+
+int SSHSession::authPassword(const std::string& user,
+ const std::string& password)
+{
+ auto rv = libssh2_userauth_password(ssh2_, user.c_str(), password.c_str());
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ return SSH_ERR_OK;
+}
+
+int SSHSession::sftpOpen(const std::string& path)
+{
+ if (!sftp_) {
+ sftp_ = libssh2_sftp_init(ssh2_);
+ if (!sftp_) {
+ if (libssh2_session_last_errno(ssh2_) == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ return SSH_ERR_ERROR;
+ }
+ }
+ if (!sftph_) {
+ sftph_ = libssh2_sftp_open(sftp_, path.c_str(), LIBSSH2_FXF_READ, 0);
+ if (!sftph_) {
+ if (libssh2_session_last_errno(ssh2_) == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ return SSH_ERR_ERROR;
+ }
+ }
+ return SSH_ERR_OK;
+}
+
+int SSHSession::sftpStat(int64_t& totalLength, time_t& mtime)
+{
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+ auto rv = libssh2_sftp_fstat_ex(sftph_, &attrs, 0);
+ if (rv == LIBSSH2_ERROR_EAGAIN) {
+ return SSH_ERR_WOULDBLOCK;
+ }
+ if (rv != 0) {
+ return SSH_ERR_ERROR;
+ }
+ totalLength = attrs.filesize;
+ mtime = attrs.mtime;
+ return SSH_ERR_OK;
+}
+
+std::string SSHSession::getLastErrorString()
+{
+ if (!ssh2_) {
+ return "SSH session has not been initialized yet";
+ }
+ char* errmsg;
+ libssh2_session_last_error(ssh2_, &errmsg, nullptr, 0);
+ return errmsg;
+}
+
+} // namespace aria2
diff --git a/src/SSHSession.h b/src/SSHSession.h
new file mode 100644
index 00000000..31941e5d
--- /dev/null
+++ b/src/SSHSession.h
@@ -0,0 +1,137 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef SSH_SESSION_H
+#define SSH_SESSION_H
+
+#include "common.h"
+#include "a2netcompat.h"
+
+#include <string>
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+namespace aria2 {
+
+enum SSHDirection {
+ SSH_WANT_READ = 1,
+ SSH_WANT_WRITE
+};
+
+enum SSHErrorCode {
+ SSH_ERR_OK = 0,
+ SSH_ERR_ERROR = -1,
+ SSH_ERR_WOULDBLOCK = -2
+};
+
+class SSHSession {
+public:
+ SSHSession();
+
+ // MUST deallocate all resources
+ ~SSHSession();
+
+ SSHSession(const SSHSession&) = delete;
+ SSHSession& operator=(const SSHSession&) = delete;
+
+ // Initializes SSH session. The |sockfd| is the underlying
+ // transport socket. This function returns SSH_ERR_OK if it
+ // succeeds, or SSH_ERR_ERROR.
+ int init(sock_t sockfd);
+
+ // Closes the SSH session. Don't close underlying transport
+ // socket. This function returns SSH_ERR_OK if it succeeds, or
+ // SSH_ERR_ERROR.
+ int closeConnection();
+
+ int gracefulShutdown();
+
+ // Returns SSH_WANT_READ if SSH session needs more data from remote
+ // endpoint to proceed, or SSH_WANT_WRITE if SSH session needs to
+ // write more data to proceed. If SSH session needs neither read nor
+ // write data at the moment, SSH_WANT_READ must be returned.
+ int checkDirection();
+
+ // Sends |data| with length |len|. This function returns the number
+ // of bytes sent if it succeeds, or SSH_ERR_WOULDBLOCK if the
+ // underlying transport blocks, or SSH_ERR_ERROR.
+ ssize_t writeData(const void* data, size_t len);
+
+ // Receives data into |data| with length |len|. This function
+ // returns the number of bytes received if it succeeds, or
+ // SSH_ERR_WOULDBLOCK if the underlying transport blocks, or
+ // SSH_ERR_ERROR.
+ ssize_t readData(void* data, size_t len);
+
+ // Performs handshake. This function returns SSH_ERR_OK
+ // if it succeeds, or SSH_ERR_WOULDBLOCK if the underlying transport
+ // blocks, or SSH_ERR_ERROR.
+ int handshake();
+
+ // Performs authentication using username and password. This
+ // function returns SSH_ERR_OK if it succeeds, or SSH_ERR_WOULDBLOCK
+ // if the underlying transport blocks, or SSH_ERR_ERROR.
+ int authPassword(const std::string& user, const std::string& password);
+
+ // Starts SFTP session and opens remote file |path|. This function
+ // returns SSH_ERR_OK if it succeeds, or SSH_ERR_WOULDBLOCK if the
+ // underlying transport blocks, or SSH_ERR_ERROR.
+ int sftpOpen(const std::string& path);
+
+ // Closes remote file opened by sftpOpen(). This function returns
+ // SSH_ERR_OK if it succeeds, or SSH_ERR_WOULDBLOCK if the
+ // underlying transport blocks, or SSH_ERR_ERROR.
+ int sftpClose();
+
+ // Gets total length and modified time of opened file by sftpOpen().
+ // On success, total length and modified time are assigned to
+ // |totalLength| and |mtime|. This function returns SSH_ERR_OK if
+ // it succeeds, or SSH_ERR_WOULDBLOCK if the underlying transport
+ // blocks, or SSH_ERR_ERROR.
+ int sftpStat(int64_t& totalLength, time_t& mtime);
+
+ // Returns last error string
+ std::string getLastErrorString();
+
+private:
+ LIBSSH2_SESSION* ssh2_;
+ LIBSSH2_SFTP* sftp_;
+ LIBSSH2_SFTP_HANDLE* sftph_;
+ sock_t fd_;
+};
+
+}
+
+#endif // SSH_SESSION_H
diff --git a/src/SftpDownloadCommand.cc b/src/SftpDownloadCommand.cc
new file mode 100644
index 00000000..84bfa913
--- /dev/null
+++ b/src/SftpDownloadCommand.cc
@@ -0,0 +1,99 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "SftpDownloadCommand.h"
+#include "Request.h"
+#include "SocketCore.h"
+#include "Segment.h"
+#include "DownloadEngine.h"
+#include "RequestGroup.h"
+#include "Option.h"
+#include "FileEntry.h"
+#include "SocketRecvBuffer.h"
+#include "AuthConfig.h"
+#include "SftpFinishDownloadCommand.h"
+
+namespace aria2 {
+
+SftpDownloadCommand::SftpDownloadCommand
+(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& socket,
+ std::unique_ptr<AuthConfig> authConfig)
+ : DownloadCommand(cuid, req, fileEntry, requestGroup, e, socket,
+ std::make_shared<SocketRecvBuffer>(socket)),
+ authConfig_(std::move(authConfig))
+{
+ setWriteCheckSocket(getSocket());
+}
+
+SftpDownloadCommand::~SftpDownloadCommand() {}
+
+bool SftpDownloadCommand::prepareForNextSegment()
+{
+ if(getOption()->getAsBool(PREF_FTP_REUSE_CONNECTION) &&
+ getFileEntry()->gtoloff(getSegments().front()->getPositionToWrite()) ==
+ getFileEntry()->getLength()) {
+
+ auto c = make_unique<SftpFinishDownloadCommand>
+ (getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
+ getDownloadEngine(), getSocket());
+
+ c->setStatus(Command::STATUS_ONESHOT_REALTIME);
+ getDownloadEngine()->setNoWait(true);
+ getDownloadEngine()->addCommand(std::move(c));
+
+ if(getRequestGroup()->downloadFinished()) {
+ // To run checksum checking, we had to call following function here.
+ DownloadCommand::prepareForNextSegment();
+ }
+ return true;
+ }
+
+ return DownloadCommand::prepareForNextSegment();
+}
+
+int64_t SftpDownloadCommand::getRequestEndOffset() const
+{
+ return getFileEntry()->getLength();
+}
+
+bool SftpDownloadCommand::shouldEnableWriteCheck() {
+ return getSocket()->wantWrite() || !getSocket()->wantRead();
+}
+
+} // namespace aria2
diff --git a/src/SftpDownloadCommand.h b/src/SftpDownloadCommand.h
new file mode 100644
index 00000000..cbb6372f
--- /dev/null
+++ b/src/SftpDownloadCommand.h
@@ -0,0 +1,66 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_SFTP_DOWNLOAD_COMMAND_H
+#define D_SFTP_DOWNLOAD_COMMAND_H
+
+#include "DownloadCommand.h"
+
+namespace aria2 {
+
+class AuthConfig;
+
+class SftpDownloadCommand : public DownloadCommand {
+private:
+ std::unique_ptr<AuthConfig> authConfig_;
+
+protected:
+ virtual bool prepareForNextSegment() CXX11_OVERRIDE;
+ virtual int64_t getRequestEndOffset() const CXX11_OVERRIDE;
+ virtual bool shouldEnableWriteCheck() CXX11_OVERRIDE;
+
+public:
+ SftpDownloadCommand(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& socket,
+ std::unique_ptr<AuthConfig> authConfig);
+ virtual ~SftpDownloadCommand();
+};
+
+} // namespace aria2
+
+#endif // D_SFTP_DOWNLOAD_COMMAND_H
diff --git a/src/SftpFinishDownloadCommand.cc b/src/SftpFinishDownloadCommand.cc
new file mode 100644
index 00000000..399f2755
--- /dev/null
+++ b/src/SftpFinishDownloadCommand.cc
@@ -0,0 +1,119 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "SftpFinishDownloadCommand.h"
+
+#include "Request.h"
+#include "DownloadEngine.h"
+#include "prefs.h"
+#include "Option.h"
+#include "message.h"
+#include "fmt.h"
+#include "DlAbortEx.h"
+#include "SocketCore.h"
+#include "RequestGroup.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "wallclock.h"
+#include "AuthConfigFactory.h"
+#include "AuthConfig.h"
+
+namespace aria2 {
+
+SftpFinishDownloadCommand::SftpFinishDownloadCommand
+(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& socket)
+ : AbstractCommand(cuid, req, fileEntry, requestGroup, e, socket)
+{
+ disableReadCheckSocket();
+ setWriteCheckSocket(getSocket());
+}
+
+SftpFinishDownloadCommand::~SftpFinishDownloadCommand() {}
+
+// overrides AbstractCommand::execute().
+// AbstractCommand::_segments is empty.
+bool SftpFinishDownloadCommand::execute()
+{
+ if(getRequestGroup()->isHaltRequested()) {
+ return true;
+ }
+ try {
+ if(readEventEnabled() || writeEventEnabled() || hupEventEnabled()) {
+ getCheckPoint() = global::wallclock();
+
+ if (!getSocket()->sshSFTPClose()) {
+ setWriteCheckSocketIf(getSocket(), getSocket()->wantWrite());
+ setReadCheckSocketIf(getSocket(), getSocket()->wantRead());
+ addCommandSelf();
+ return false;
+ }
+
+ auto authConfig =
+ getDownloadEngine()->getAuthConfigFactory()->createAuthConfig
+ (getRequest(), getRequestGroup()->getOption().get());
+
+ getDownloadEngine()->poolSocket
+ (getRequest(), authConfig->getUser(), createProxyRequest(),
+ getSocket(), "");
+ } else if(getCheckPoint().difference(global::wallclock()) >= getTimeout()) {
+ A2_LOG_INFO(fmt("CUID#%" PRId64
+ " - Timeout before receiving transfer complete.",
+ getCuid()));
+ } else {
+ addCommandSelf();
+ return false;
+ }
+ } catch(RecoverableException& e) {
+ A2_LOG_INFO_EX(fmt("CUID#%" PRId64
+ " - Exception was thrown, but download was"
+ " finished, so we can ignore the exception.",
+ getCuid()),
+ e);
+ }
+ if(getRequestGroup()->downloadFinished()) {
+ return true;
+ } else {
+ return prepareForRetry(0);
+ }
+}
+
+// This function never be called.
+bool SftpFinishDownloadCommand::executeInternal() { return true; }
+
+} // namespace aria2
diff --git a/src/SftpFinishDownloadCommand.h b/src/SftpFinishDownloadCommand.h
new file mode 100644
index 00000000..fbdd86f4
--- /dev/null
+++ b/src/SftpFinishDownloadCommand.h
@@ -0,0 +1,59 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_SFTP_FINISH_DOWNLOAD_COMMAND_H
+#define D_SFTP_FINISH_DOWNLOAD_COMMAND_H
+
+#include "AbstractCommand.h"
+
+namespace aria2 {
+
+class SftpFinishDownloadCommand : public AbstractCommand {
+protected:
+ virtual bool execute() CXX11_OVERRIDE;
+
+ virtual bool executeInternal() CXX11_OVERRIDE;
+public:
+ SftpFinishDownloadCommand(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& socket);
+ virtual ~SftpFinishDownloadCommand();
+};
+
+} // namespace aria2
+
+#endif // D_SFTP_FINISH_DOWNLOAD_COMMAND_H
diff --git a/src/SftpNegotiationCommand.cc b/src/SftpNegotiationCommand.cc
new file mode 100644
index 00000000..5a477025
--- /dev/null
+++ b/src/SftpNegotiationCommand.cc
@@ -0,0 +1,320 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "SftpNegotiationCommand.h"
+
+#include <cassert>
+#include <utility>
+
+#include "Request.h"
+#include "DownloadEngine.h"
+#include "RequestGroup.h"
+#include "PieceStorage.h"
+#include "FileEntry.h"
+#include "message.h"
+#include "util.h"
+#include "Option.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "Segment.h"
+#include "DownloadContext.h"
+#include "DefaultBtProgressInfoFile.h"
+#include "RequestGroupMan.h"
+#include "SocketCore.h"
+#include "fmt.h"
+#include "DiskAdaptor.h"
+#include "SegmentMan.h"
+#include "AuthConfigFactory.h"
+#include "AuthConfig.h"
+#include "a2functional.h"
+#include "URISelector.h"
+#include "CheckIntegrityEntry.h"
+#include "NullProgressInfoFile.h"
+#include "ChecksumCheckIntegrityEntry.h"
+#include "SftpDownloadCommand.h"
+
+namespace aria2 {
+
+SftpNegotiationCommand::SftpNegotiationCommand
+(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& socket,
+ Seq seq)
+ : AbstractCommand(cuid, req, fileEntry, requestGroup, e, socket),
+ sequence_(seq),
+ authConfig_(e->getAuthConfigFactory()->createAuthConfig
+ (req, requestGroup->getOption().get()))
+
+{
+ path_ = getPath();
+ disableReadCheckSocket();
+ setWriteCheckSocket(getSocket());
+}
+
+SftpNegotiationCommand::~SftpNegotiationCommand() {}
+
+bool SftpNegotiationCommand::executeInternal() {
+ disableWriteCheckSocket();
+ for (;;) {
+ switch(sequence_) {
+ case SEQ_HANDSHAKE:
+ setReadCheckSocket(getSocket());
+ if (!getSocket()->sshHandshake()) {
+ goto again;
+ }
+ A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SSH handshake success", getCuid()));
+ sequence_ = SEQ_AUTH_PASSWORD;
+ break;
+ case SEQ_AUTH_PASSWORD:
+ if (!getSocket()->sshAuthPassword(authConfig_->getUser(),
+ authConfig_->getPassword())) {
+ goto again;
+ }
+ A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SSH authentication success",
+ getCuid()));
+ sequence_ = SEQ_SFTP_OPEN;
+ break;
+ case SEQ_SFTP_OPEN: {
+ if (!getSocket()->sshSFTPOpen(path_)) {
+ goto again;
+ }
+ A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SFTP file %s opened", getCuid(),
+ path_.c_str()));
+ sequence_ = SEQ_SFTP_STAT;
+ break;
+ }
+ case SEQ_SFTP_STAT: {
+ int64_t totalLength;
+ time_t mtime;
+ if (!getSocket()->sshSFTPStat(totalLength, mtime, path_)) {
+ goto again;
+ }
+ Time t(mtime);
+ A2_LOG_INFO(fmt("CUID#%" PRId64 " - SFTP File %s, size=%" PRId64
+ ", mtime=%s",
+ getCuid(), path_.c_str(), totalLength,
+ t.toHTTPDate().c_str()));
+ if (!getPieceStorage()) {
+ getRequestGroup()->updateLastModifiedTime(Time(mtime));
+ onFileSizeDetermined(totalLength);
+ } else {
+ getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
+ totalLength);
+ sequence_ = SEQ_NEGOTIATION_COMPLETED;
+ }
+ break;
+ }
+ case SEQ_FILE_PREPARATION:
+ sequence_ = SEQ_NEGOTIATION_COMPLETED;
+ disableReadCheckSocket();
+ disableWriteCheckSocket();
+ return false;
+ case SEQ_NEGOTIATION_COMPLETED: {
+ auto command = make_unique<SftpDownloadCommand>
+ (getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
+ getDownloadEngine(), getSocket(), std::move(authConfig_));
+ command->setStartupIdleTime
+ (getOption()->getAsInt(PREF_STARTUP_IDLE_TIME));
+ command->setLowestDownloadSpeedLimit
+ (getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
+ command->setStatus(Command::STATUS_ONESHOT_REALTIME);
+
+ getDownloadEngine()->setNoWait(true);
+
+ if(getFileEntry()->isUniqueProtocol()) {
+ getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());
+ }
+ getRequestGroup()->getURISelector()->tuneDownloadCommand
+ (getFileEntry()->getRemainingUris(), command.get());
+ getDownloadEngine()->addCommand(std::move(command));
+ return true;
+ }
+ case SEQ_DOWNLOAD_ALREADY_COMPLETED:
+ case SEQ_HEAD_OK:
+ case SEQ_EXIT:
+ return true;
+ };
+ }
+ again:
+ addCommandSelf();
+ if (getSocket()->wantWrite()) {
+ setWriteCheckSocket(getSocket());
+ }
+ return false;
+}
+
+void SftpNegotiationCommand::onFileSizeDetermined(int64_t totalLength)
+{
+ getFileEntry()->setLength(totalLength);
+ if(getFileEntry()->getPath().empty()) {
+ auto suffixPath = util::createSafePath
+ (util::percentDecode(std::begin(getRequest()->getFile()),
+ std::end(getRequest()->getFile())));
+
+ getFileEntry()->setPath
+ (util::applyDir(getOption()->get(PREF_DIR), suffixPath));
+ getFileEntry()->setSuffixPath(suffixPath);
+ }
+ getRequestGroup()->preDownloadProcessing();
+
+ if(totalLength == 0) {
+ sequence_ = SEQ_NEGOTIATION_COMPLETED;
+
+ if(getOption()->getAsBool(PREF_DRY_RUN)) {
+ getRequestGroup()->initPieceStorage();
+ onDryRunFileFound();
+ return;
+ }
+
+ if(getDownloadContext()->knowsTotalLength() &&
+ getRequestGroup()->downloadFinishedByFileLength()) {
+ // TODO Known issue: if .aria2 file exists, it will not be
+ // deleted on successful verification, because .aria2 file is
+ // not loaded. See also
+ // HttpResponseCommand::handleOtherEncoding()
+ getRequestGroup()->initPieceStorage();
+ if(getDownloadContext()->isChecksumVerificationNeeded()) {
+ A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
+ auto entry = make_unique<ChecksumCheckIntegrityEntry>
+ (getRequestGroup());
+ entry->initValidator();
+ getPieceStorage()->getDiskAdaptor()->openExistingFile();
+ getDownloadEngine()->getCheckIntegrityMan()->pushEntry
+ (std::move(entry));
+ sequence_ = SEQ_EXIT;
+ }
+ else {
+ getPieceStorage()->markAllPiecesDone();
+ getDownloadContext()->setChecksumVerified(true);
+ sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
+ A2_LOG_NOTICE
+ (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
+ GroupId::toHex(getRequestGroup()->getGID()).c_str(),
+ getRequestGroup()->getFirstFilePath().c_str()));
+ }
+ poolConnection();
+ return;
+ }
+
+ getRequestGroup()->adjustFilename
+ (std::make_shared<NullProgressInfoFile>());
+ getRequestGroup()->initPieceStorage();
+ getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
+
+ if(getDownloadContext()->knowsTotalLength()) {
+ A2_LOG_DEBUG("File length becomes zero and it means download completed.");
+ // TODO Known issue: if .aria2 file exists, it will not be
+ // deleted on successful verification, because .aria2 file is
+ // not loaded. See also
+ // HttpResponseCommand::handleOtherEncoding()
+ if(getDownloadContext()->isChecksumVerificationNeeded()) {
+ A2_LOG_DEBUG("Verify checksum for zero-length file");
+ auto entry = make_unique<ChecksumCheckIntegrityEntry>
+ (getRequestGroup());
+ entry->initValidator();
+ getDownloadEngine()->getCheckIntegrityMan()->pushEntry
+ (std::move(entry));
+ sequence_ = SEQ_EXIT;
+ } else
+ {
+ sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
+ getPieceStorage()->markAllPiecesDone();
+ }
+ poolConnection();
+ return;
+ }
+ // We have to make sure that command that has Request object must
+ // have segment after PieceStorage is initialized. See
+ // AbstractCommand::execute()
+ getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
+ return;
+ } else {
+ auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>
+ (getDownloadContext(), nullptr, getOption().get());
+ getRequestGroup()->adjustFilename(progressInfoFile);
+ getRequestGroup()->initPieceStorage();
+
+ if(getOption()->getAsBool(PREF_DRY_RUN)) {
+ onDryRunFileFound();
+ return;
+ }
+
+ auto checkIntegrityEntry = getRequestGroup()->createCheckIntegrityEntry();
+ if(!checkIntegrityEntry) {
+ sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
+ poolConnection();
+ return;
+ }
+ checkIntegrityEntry->pushNextCommand(std::unique_ptr<Command>(this));
+ // We have to make sure that command that has Request object must
+ // have segment after PieceStorage is initialized. See
+ // AbstractCommand::execute()
+ getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
+
+ prepareForNextAction(std::move(checkIntegrityEntry));
+
+ disableReadCheckSocket();
+
+ sequence_ = SEQ_FILE_PREPARATION;
+ }
+}
+
+void SftpNegotiationCommand::poolConnection() const
+{
+ if(getOption()->getAsBool(PREF_FTP_REUSE_CONNECTION)) {
+ // TODO we don't need options. Probably, we need to pool socket
+ // using scheme, port and auth info as key
+ getDownloadEngine()->poolSocket(getRequest(), authConfig_->getUser(),
+ createProxyRequest(), getSocket(), "");
+ }
+}
+
+void SftpNegotiationCommand::onDryRunFileFound()
+{
+ getPieceStorage()->markAllPiecesDone();
+ getDownloadContext()->setChecksumVerified(true);
+ poolConnection();
+ sequence_ = SEQ_HEAD_OK;
+}
+
+std::string SftpNegotiationCommand::getPath() const {
+ auto &req = getRequest();
+ auto path = req->getDir() + req->getFile();
+ return util::percentDecode(std::begin(path), std::end(path));
+}
+
+} // namespace aria2
diff --git a/src/SftpNegotiationCommand.h b/src/SftpNegotiationCommand.h
new file mode 100644
index 00000000..e6b77ab6
--- /dev/null
+++ b/src/SftpNegotiationCommand.h
@@ -0,0 +1,87 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_SFTP_NEGOTIATION_COMMAND_H
+#define D_SFTP_NEGOTIATION_COMMAND_H
+
+#include "AbstractCommand.h"
+
+namespace aria2 {
+
+class SocketCore;
+class AuthConfig;
+
+class SftpNegotiationCommand : public AbstractCommand {
+public:
+ enum Seq {
+ SEQ_HANDSHAKE,
+ SEQ_AUTH_PASSWORD,
+ SEQ_SFTP_OPEN,
+ SEQ_SFTP_STAT,
+ SEQ_NEGOTIATION_COMPLETED,
+ SEQ_DOWNLOAD_ALREADY_COMPLETED,
+ SEQ_HEAD_OK,
+ SEQ_FILE_PREPARATION,
+ SEQ_EXIT,
+ };
+
+private:
+ void onFileSizeDetermined(int64_t totalLength);
+ void poolConnection() const;
+ void onDryRunFileFound();
+ std::string getPath() const;
+
+ std::shared_ptr<SocketCore> socket_;
+ Seq sequence_;
+ std::unique_ptr<AuthConfig> authConfig_;
+ // remote file path
+ std::string path_;
+
+protected:
+ virtual bool executeInternal() CXX11_OVERRIDE;
+
+public:
+ SftpNegotiationCommand(cuid_t cuid,
+ const std::shared_ptr<Request>& req,
+ const std::shared_ptr<FileEntry>& fileEntry,
+ RequestGroup* requestGroup,
+ DownloadEngine* e,
+ const std::shared_ptr<SocketCore>& s,
+ Seq seq = SEQ_HANDSHAKE);
+ virtual ~SftpNegotiationCommand();
+};
+
+} // namespace aria2
+
+#endif // D_SFTP_NEGOTIATION_COMMAND_H
diff --git a/src/SftpNegotiationConnectChain.h b/src/SftpNegotiationConnectChain.h
new file mode 100644
index 00000000..e78081ce
--- /dev/null
+++ b/src/SftpNegotiationConnectChain.h
@@ -0,0 +1,66 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2015 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef SFTP_NEGOTIATION_CONNECT_CHAIN_H
+#define SFTP_NEGOTIATION_CONNECT_CHAIN_H
+
+#include "ControlChain.h"
+#include "ConnectCommand.h"
+#include "DownloadEngine.h"
+#include "SftpNegotiationCommand.h"
+
+namespace aria2 {
+
+struct SftpNegotiationConnectChain : public ControlChain<ConnectCommand*> {
+ SftpNegotiationConnectChain() {}
+ virtual ~SftpNegotiationConnectChain() {}
+ virtual int run(ConnectCommand* t, DownloadEngine* e) CXX11_OVERRIDE
+ {
+ auto c = make_unique<SftpNegotiationCommand>
+ (t->getCuid(),
+ t->getRequest(),
+ t->getFileEntry(),
+ t->getRequestGroup(),
+ t->getDownloadEngine(),
+ t->getSocket());
+ c->setStatus(Command::STATUS_ONESHOT_REALTIME);
+ e->setNoWait(true);
+ e->addCommand(std::move(c));
+ return 0;
+ }
+};
+
+} // namespace aria2
+
+#endif // SFTP_NEGOTIATION_CONNECT_CHAIN_H
diff --git a/src/SocketCore.cc b/src/SocketCore.cc
index b42c7ecd..05ee28bf 100644
--- a/src/SocketCore.cc
+++ b/src/SocketCore.cc
@@ -45,6 +45,7 @@
#include <cerrno>
#include <cstring>
+#include <cassert>
#include <sstream>
#include "message.h"
@@ -60,6 +61,9 @@
# include "TLSContext.h"
# include "TLSSession.h"
#endif // ENABLE_SSL
+#ifdef HAVE_LIBSSH2
+# include "SSHSession.h"
+#endif // HAVE_LIBSSH2
namespace aria2 {
@@ -608,6 +612,14 @@ void SocketCore::closeConnection()
tlsSession_.reset();
}
#endif // ENABLE_SSL
+
+#ifdef HAVE_LIBSSH2
+ if(sshSession_) {
+ sshSession_->closeConnection();
+ sshSession_.reset();
+ }
+#endif // HAVE_LIBSSH2
+
if(sockfd_ != (sock_t) -1) {
shutdown(sockfd_, SHUT_WR);
CLOSE(sockfd_);
@@ -796,7 +808,23 @@ void SocketCore::readData(void* data, size_t& len)
wantRead_ = false;
wantWrite_ = false;
- if(!secure_) {
+ if(sshSession_) {
+#ifdef HAVE_LIBSSH2
+ ret = sshSession_->readData(data, len);
+ if(ret < 0) {
+ if(ret != SSH_ERR_WOULDBLOCK) {
+ throw DL_RETRY_EX(fmt(EX_SOCKET_RECV,
+ sshSession_->getLastErrorString().c_str()));
+ }
+ if(sshSession_->checkDirection() == SSH_WANT_READ) {
+ wantRead_ = true;
+ } else {
+ wantWrite_ = true;
+ }
+ ret = 0;
+ }
+#endif // HAVE_LIBSSH2
+ } else if(!secure_) {
// Cast for Windows recv()
while((ret = recv(sockfd_, reinterpret_cast<char*>(data), len, 0)) == -1 &&
SOCKET_ERRNO == A2_EINTR);
@@ -957,6 +985,137 @@ bool SocketCore::tlsHandshake(TLSContext* tlsctx, const std::string& hostname)
#endif // ENABLE_SSL
+#ifdef HAVE_LIBSSH2
+
+bool SocketCore::sshHandshake()
+{
+ wantRead_ = false;
+ wantWrite_ = false;
+
+ if (!sshSession_) {
+ sshSession_ = make_unique<SSHSession>();
+ if (sshSession_->init(sockfd_) == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX("Could not create SSH session");
+ }
+ }
+ auto rv = sshSession_->handshake();
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH handshake failure: %s",
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+bool SocketCore::sshAuthPassword(const std::string& user,
+ const std::string& password)
+{
+ assert(sshSession_);
+
+ wantRead_ = false;
+ wantWrite_ = false;
+
+ auto rv = sshSession_->authPassword(user, password);
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH authentication failure: %s",
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+bool SocketCore::sshSFTPOpen(const std::string& path)
+{
+ assert(sshSession_);
+
+ wantRead_ = false;
+ wantWrite_ = false;
+
+ auto rv = sshSession_->sftpOpen(path);
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH opening SFTP path %s failed: %s",
+ path.c_str(),
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+bool SocketCore::sshSFTPClose()
+{
+ assert(sshSession_);
+
+ wantRead_ = false;
+ wantWrite_ = false;
+
+ auto rv = sshSession_->sftpClose();
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH closing SFTP failed: %s",
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+bool SocketCore::sshSFTPStat(int64_t& totalLength, time_t& mtime,
+ const std::string& path)
+{
+ assert(sshSession_);
+
+ wantRead_ = false;
+ wantWrite_ = false;
+
+ auto rv = sshSession_->sftpStat(totalLength, mtime);
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH stat SFTP path %s filed: %s",
+ path.c_str(),
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+bool SocketCore::sshGracefulShutdown()
+{
+ assert(sshSession_);
+ auto rv = sshSession_->gracefulShutdown();
+ if (rv == SSH_ERR_WOULDBLOCK) {
+ sshCheckDirection();
+ return false;
+ }
+ if (rv == SSH_ERR_ERROR) {
+ throw DL_ABORT_EX(fmt("SSH graceful shutdown failed: %s",
+ sshSession_->getLastErrorString().c_str()));
+ }
+ return true;
+}
+
+void SocketCore::sshCheckDirection()
+{
+ if (sshSession_->checkDirection() == SSH_WANT_READ) {
+ wantRead_ = true;
+ } else {
+ wantWrite_ = true;
+ }
+}
+
+#endif // HAVE_LIBSSH2
+
ssize_t SocketCore::writeData(const void* data, size_t len,
const std::string& host, uint16_t port)
{
diff --git a/src/SocketCore.h b/src/SocketCore.h
index 35f0f83f..92bc67e3 100644
--- a/src/SocketCore.h
+++ b/src/SocketCore.h
@@ -55,6 +55,10 @@ class TLSContext;
class TLSSession;
#endif // ENABLE_SSL
+#ifdef HAVE_LIBSSH2
+class SSHSession;
+#endif // HAVE_LIBSSH2
+
class SocketCore {
friend bool operator==(const SocketCore& s1, const SocketCore& s2);
friend bool operator!=(const SocketCore& s1, const SocketCore& s2);
@@ -95,6 +99,12 @@ private:
bool tlsHandshake(TLSContext* tlsctx, const std::string& hostname);
#endif // ENABLE_SSL
+#ifdef HAVE_LIBSSH2
+ std::unique_ptr<SSHSession> sshSession_;
+
+ void sshCheckDirection();
+#endif // HAVE_LIBSSH2
+
void init();
void bind(const struct sockaddr* addr, socklen_t addrlen);
@@ -290,6 +300,22 @@ public:
bool tlsConnect(const std::string& hostname);
#endif // ENABLE_SSL
+#ifdef HAVE_LIBSSH2
+ // Performs SSH handshake
+ bool sshHandshake();
+ // Performs SSH authentication using username and password.
+ bool sshAuthPassword(const std::string& user, const std::string& password);
+ // Starts sftp session and open remote file |path|.
+ bool sshSFTPOpen(const std::string& path);
+ // Closes sftp remote file gracefully
+ bool sshSFTPClose();
+ // Gets total length and modified time for remote file currently
+ // opened. |path| is used for logging.
+ bool sshSFTPStat(int64_t& totalLength, time_t& mtime,
+ const std::string& path);
+ bool sshGracefulShutdown();
+#endif // HAVE_LIBSSH2
+
bool operator==(const SocketCore& s) {
return sockfd_ == s.sockfd_;
}