SftpNegotiationCommand.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /* <!-- copyright */
  2. /*
  3. * aria2 - The high speed download utility
  4. *
  5. * Copyright (C) 2015 Tatsuhiro Tsujikawa
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * In addition, as a special exception, the copyright holders give
  22. * permission to link the code of portions of this program with the
  23. * OpenSSL library under certain conditions as described in each
  24. * individual source file, and distribute linked combinations
  25. * including the two.
  26. * You must obey the GNU General Public License in all respects
  27. * for all of the code used other than OpenSSL. If you modify
  28. * file(s) with this exception, you may extend this exception to your
  29. * version of the file(s), but you are not obligated to do so. If you
  30. * do not wish to do so, delete this exception statement from your
  31. * version. If you delete this exception statement from all source
  32. * files in the program, then also delete it here.
  33. */
  34. /* copyright --> */
  35. #include "SftpNegotiationCommand.h"
  36. #include <cassert>
  37. #include <utility>
  38. #include "Request.h"
  39. #include "DownloadEngine.h"
  40. #include "RequestGroup.h"
  41. #include "PieceStorage.h"
  42. #include "FileEntry.h"
  43. #include "message.h"
  44. #include "util.h"
  45. #include "Option.h"
  46. #include "Logger.h"
  47. #include "LogFactory.h"
  48. #include "Segment.h"
  49. #include "DownloadContext.h"
  50. #include "DefaultBtProgressInfoFile.h"
  51. #include "RequestGroupMan.h"
  52. #include "SocketCore.h"
  53. #include "fmt.h"
  54. #include "DiskAdaptor.h"
  55. #include "SegmentMan.h"
  56. #include "AuthConfigFactory.h"
  57. #include "AuthConfig.h"
  58. #include "a2functional.h"
  59. #include "URISelector.h"
  60. #include "CheckIntegrityEntry.h"
  61. #include "NullProgressInfoFile.h"
  62. #include "ChecksumCheckIntegrityEntry.h"
  63. #include "SftpDownloadCommand.h"
  64. namespace aria2 {
  65. SftpNegotiationCommand::SftpNegotiationCommand(
  66. cuid_t cuid, const std::shared_ptr<Request>& req,
  67. const std::shared_ptr<FileEntry>& fileEntry, RequestGroup* requestGroup,
  68. DownloadEngine* e, const std::shared_ptr<SocketCore>& socket, Seq seq)
  69. : AbstractCommand(cuid, req, fileEntry, requestGroup, e, socket),
  70. sequence_(seq),
  71. authConfig_(e->getAuthConfigFactory()->createAuthConfig(
  72. req, requestGroup->getOption().get()))
  73. {
  74. path_ = getPath();
  75. setWriteCheckSocket(getSocket());
  76. const std::string& checksum = getOption()->get(PREF_SSH_HOST_KEY_MD);
  77. if (!checksum.empty()) {
  78. auto p = util::divide(std::begin(checksum), std::end(checksum), '=');
  79. hashType_.assign(p.first.first, p.first.second);
  80. util::lowercase(hashType_);
  81. digest_ = util::fromHex(p.second.first, p.second.second);
  82. }
  83. }
  84. SftpNegotiationCommand::~SftpNegotiationCommand() = default;
  85. bool SftpNegotiationCommand::executeInternal()
  86. {
  87. disableWriteCheckSocket();
  88. for (;;) {
  89. switch (sequence_) {
  90. case SEQ_HANDSHAKE:
  91. setReadCheckSocket(getSocket());
  92. if (!getSocket()->sshHandshake(hashType_, digest_)) {
  93. goto again;
  94. }
  95. A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SSH handshake success", getCuid()));
  96. sequence_ = SEQ_AUTH_PASSWORD;
  97. break;
  98. case SEQ_AUTH_PASSWORD:
  99. if (!getSocket()->sshAuthPassword(authConfig_->getUser(),
  100. authConfig_->getPassword())) {
  101. goto again;
  102. }
  103. A2_LOG_DEBUG(
  104. fmt("CUID#%" PRId64 " - SSH authentication success", getCuid()));
  105. sequence_ = SEQ_SFTP_OPEN;
  106. break;
  107. case SEQ_SFTP_OPEN:
  108. if (!getSocket()->sshSFTPOpen(path_)) {
  109. goto again;
  110. }
  111. A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SFTP file %s opened", getCuid(),
  112. path_.c_str()));
  113. sequence_ = SEQ_SFTP_STAT;
  114. break;
  115. case SEQ_SFTP_STAT: {
  116. int64_t totalLength;
  117. time_t mtime;
  118. if (!getSocket()->sshSFTPStat(totalLength, mtime, path_)) {
  119. goto again;
  120. }
  121. Time t(mtime);
  122. A2_LOG_INFO(
  123. fmt("CUID#%" PRId64 " - SFTP File %s, size=%" PRId64 ", mtime=%s",
  124. getCuid(), path_.c_str(), totalLength, t.toHTTPDate().c_str()));
  125. if (!getPieceStorage()) {
  126. getRequestGroup()->updateLastModifiedTime(Time(mtime));
  127. onFileSizeDetermined(totalLength);
  128. }
  129. else {
  130. getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
  131. totalLength);
  132. sequence_ = SEQ_SFTP_SEEK;
  133. }
  134. break;
  135. }
  136. case SEQ_SFTP_SEEK: {
  137. sequence_ = SEQ_NEGOTIATION_COMPLETED;
  138. if (getSegments().empty()) {
  139. break;
  140. }
  141. auto& segment = getSegments().front();
  142. A2_LOG_INFO(fmt("CUID#%" PRId64 " - SFTP seek to %" PRId64, getCuid(),
  143. segment->getPositionToWrite()));
  144. getSocket()->sshSFTPSeek(segment->getPositionToWrite());
  145. break;
  146. }
  147. case SEQ_FILE_PREPARATION:
  148. sequence_ = SEQ_SFTP_SEEK;
  149. disableReadCheckSocket();
  150. disableWriteCheckSocket();
  151. return false;
  152. case SEQ_NEGOTIATION_COMPLETED: {
  153. auto command = make_unique<SftpDownloadCommand>(
  154. getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
  155. getDownloadEngine(), getSocket(), std::move(authConfig_));
  156. command->setStartupIdleTime(
  157. std::chrono::seconds(getOption()->getAsInt(PREF_STARTUP_IDLE_TIME)));
  158. command->setLowestDownloadSpeedLimit(
  159. getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
  160. command->setStatus(Command::STATUS_ONESHOT_REALTIME);
  161. getDownloadEngine()->setNoWait(true);
  162. if (getFileEntry()->isUniqueProtocol()) {
  163. getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());
  164. }
  165. getRequestGroup()->getURISelector()->tuneDownloadCommand(
  166. getFileEntry()->getRemainingUris(), command.get());
  167. getDownloadEngine()->addCommand(std::move(command));
  168. return true;
  169. }
  170. case SEQ_DOWNLOAD_ALREADY_COMPLETED:
  171. case SEQ_HEAD_OK:
  172. case SEQ_EXIT:
  173. return true;
  174. };
  175. }
  176. again:
  177. addCommandSelf();
  178. if (getSocket()->wantWrite()) {
  179. setWriteCheckSocket(getSocket());
  180. }
  181. return false;
  182. }
  183. void SftpNegotiationCommand::onFileSizeDetermined(int64_t totalLength)
  184. {
  185. getFileEntry()->setLength(totalLength);
  186. if (getFileEntry()->getPath().empty()) {
  187. auto suffixPath = util::createSafePath(
  188. util::percentDecode(std::begin(getRequest()->getFile()),
  189. std::end(getRequest()->getFile())));
  190. getFileEntry()->setPath(
  191. util::applyDir(getOption()->get(PREF_DIR), suffixPath));
  192. getFileEntry()->setSuffixPath(suffixPath);
  193. }
  194. getRequestGroup()->preDownloadProcessing();
  195. if (totalLength == 0) {
  196. sequence_ = SEQ_NEGOTIATION_COMPLETED;
  197. if (getOption()->getAsBool(PREF_DRY_RUN)) {
  198. getRequestGroup()->initPieceStorage();
  199. onDryRunFileFound();
  200. return;
  201. }
  202. if (getDownloadContext()->knowsTotalLength() &&
  203. getRequestGroup()->downloadFinishedByFileLength()) {
  204. // TODO Known issue: if .aria2 file exists, it will not be
  205. // deleted on successful verification, because .aria2 file is
  206. // not loaded. See also
  207. // HttpResponseCommand::handleOtherEncoding()
  208. getRequestGroup()->initPieceStorage();
  209. if (getDownloadContext()->isChecksumVerificationNeeded()) {
  210. A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
  211. auto entry =
  212. make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
  213. entry->initValidator();
  214. getPieceStorage()->getDiskAdaptor()->openExistingFile();
  215. getDownloadEngine()->getCheckIntegrityMan()->pushEntry(
  216. std::move(entry));
  217. sequence_ = SEQ_EXIT;
  218. }
  219. else {
  220. getPieceStorage()->markAllPiecesDone();
  221. getDownloadContext()->setChecksumVerified(true);
  222. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  223. A2_LOG_NOTICE(fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
  224. GroupId::toHex(getRequestGroup()->getGID()).c_str(),
  225. getRequestGroup()->getFirstFilePath().c_str()));
  226. }
  227. poolConnection();
  228. return;
  229. }
  230. getRequestGroup()->adjustFilename(std::make_shared<NullProgressInfoFile>());
  231. getRequestGroup()->initPieceStorage();
  232. getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
  233. if (getDownloadContext()->knowsTotalLength()) {
  234. A2_LOG_DEBUG("File length becomes zero and it means download completed.");
  235. // TODO Known issue: if .aria2 file exists, it will not be
  236. // deleted on successful verification, because .aria2 file is
  237. // not loaded. See also
  238. // HttpResponseCommand::handleOtherEncoding()
  239. if (getDownloadContext()->isChecksumVerificationNeeded()) {
  240. A2_LOG_DEBUG("Verify checksum for zero-length file");
  241. auto entry =
  242. make_unique<ChecksumCheckIntegrityEntry>(getRequestGroup());
  243. entry->initValidator();
  244. getDownloadEngine()->getCheckIntegrityMan()->pushEntry(
  245. std::move(entry));
  246. sequence_ = SEQ_EXIT;
  247. }
  248. else {
  249. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  250. getPieceStorage()->markAllPiecesDone();
  251. }
  252. poolConnection();
  253. return;
  254. }
  255. // We have to make sure that command that has Request object must
  256. // have segment after PieceStorage is initialized. See
  257. // AbstractCommand::execute()
  258. getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
  259. return;
  260. }
  261. else {
  262. auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>(
  263. getDownloadContext(), nullptr, getOption().get());
  264. getRequestGroup()->adjustFilename(progressInfoFile);
  265. getRequestGroup()->initPieceStorage();
  266. if (getOption()->getAsBool(PREF_DRY_RUN)) {
  267. onDryRunFileFound();
  268. return;
  269. }
  270. auto checkIntegrityEntry = getRequestGroup()->createCheckIntegrityEntry();
  271. if (!checkIntegrityEntry) {
  272. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  273. poolConnection();
  274. return;
  275. }
  276. // We have to make sure that command that has Request object must
  277. // have segment after PieceStorage is initialized. See
  278. // AbstractCommand::execute()
  279. getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
  280. checkIntegrityEntry->pushNextCommand(std::unique_ptr<Command>(this));
  281. prepareForNextAction(std::move(checkIntegrityEntry));
  282. disableReadCheckSocket();
  283. sequence_ = SEQ_FILE_PREPARATION;
  284. }
  285. }
  286. void SftpNegotiationCommand::poolConnection() const
  287. {
  288. if (getOption()->getAsBool(PREF_FTP_REUSE_CONNECTION)) {
  289. // TODO we don't need options. Probably, we need to pool socket
  290. // using scheme, port and auth info as key
  291. getDownloadEngine()->poolSocket(getRequest(), authConfig_->getUser(),
  292. createProxyRequest(), getSocket(), "");
  293. }
  294. }
  295. void SftpNegotiationCommand::onDryRunFileFound()
  296. {
  297. getPieceStorage()->markAllPiecesDone();
  298. getDownloadContext()->setChecksumVerified(true);
  299. poolConnection();
  300. sequence_ = SEQ_HEAD_OK;
  301. }
  302. std::string SftpNegotiationCommand::getPath() const
  303. {
  304. auto& req = getRequest();
  305. auto path = req->getDir() + req->getFile();
  306. return util::percentDecode(std::begin(path), std::end(path));
  307. }
  308. } // namespace aria2