SftpNegotiationCommand.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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,
  67. const std::shared_ptr<Request>& req,
  68. const std::shared_ptr<FileEntry>& fileEntry,
  69. RequestGroup* requestGroup,
  70. DownloadEngine* e,
  71. const std::shared_ptr<SocketCore>& socket,
  72. Seq seq)
  73. : AbstractCommand(cuid, req, fileEntry, requestGroup, e, socket),
  74. sequence_(seq),
  75. authConfig_(e->getAuthConfigFactory()->createAuthConfig
  76. (req, requestGroup->getOption().get()))
  77. {
  78. path_ = getPath();
  79. disableReadCheckSocket();
  80. setWriteCheckSocket(getSocket());
  81. }
  82. SftpNegotiationCommand::~SftpNegotiationCommand() {}
  83. bool SftpNegotiationCommand::executeInternal() {
  84. disableWriteCheckSocket();
  85. for (;;) {
  86. switch(sequence_) {
  87. case SEQ_HANDSHAKE:
  88. setReadCheckSocket(getSocket());
  89. if (!getSocket()->sshHandshake()) {
  90. goto again;
  91. }
  92. A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SSH handshake success", getCuid()));
  93. sequence_ = SEQ_AUTH_PASSWORD;
  94. break;
  95. case SEQ_AUTH_PASSWORD:
  96. if (!getSocket()->sshAuthPassword(authConfig_->getUser(),
  97. authConfig_->getPassword())) {
  98. goto again;
  99. }
  100. A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SSH authentication success",
  101. getCuid()));
  102. sequence_ = SEQ_SFTP_OPEN;
  103. break;
  104. case SEQ_SFTP_OPEN: {
  105. if (!getSocket()->sshSFTPOpen(path_)) {
  106. goto again;
  107. }
  108. A2_LOG_DEBUG(fmt("CUID#%" PRId64 " - SFTP file %s opened", getCuid(),
  109. path_.c_str()));
  110. sequence_ = SEQ_SFTP_STAT;
  111. break;
  112. }
  113. case SEQ_SFTP_STAT: {
  114. int64_t totalLength;
  115. time_t mtime;
  116. if (!getSocket()->sshSFTPStat(totalLength, mtime, path_)) {
  117. goto again;
  118. }
  119. Time t(mtime);
  120. A2_LOG_INFO(fmt("CUID#%" PRId64 " - SFTP File %s, size=%" PRId64
  121. ", mtime=%s",
  122. getCuid(), path_.c_str(), totalLength,
  123. t.toHTTPDate().c_str()));
  124. if (!getPieceStorage()) {
  125. getRequestGroup()->updateLastModifiedTime(Time(mtime));
  126. onFileSizeDetermined(totalLength);
  127. } else {
  128. getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
  129. totalLength);
  130. sequence_ = SEQ_NEGOTIATION_COMPLETED;
  131. }
  132. break;
  133. }
  134. case SEQ_FILE_PREPARATION:
  135. sequence_ = SEQ_NEGOTIATION_COMPLETED;
  136. disableReadCheckSocket();
  137. disableWriteCheckSocket();
  138. return false;
  139. case SEQ_NEGOTIATION_COMPLETED: {
  140. auto command = make_unique<SftpDownloadCommand>
  141. (getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
  142. getDownloadEngine(), getSocket(), std::move(authConfig_));
  143. command->setStartupIdleTime
  144. (getOption()->getAsInt(PREF_STARTUP_IDLE_TIME));
  145. command->setLowestDownloadSpeedLimit
  146. (getOption()->getAsInt(PREF_LOWEST_SPEED_LIMIT));
  147. command->setStatus(Command::STATUS_ONESHOT_REALTIME);
  148. getDownloadEngine()->setNoWait(true);
  149. if(getFileEntry()->isUniqueProtocol()) {
  150. getFileEntry()->removeURIWhoseHostnameIs(getRequest()->getHost());
  151. }
  152. getRequestGroup()->getURISelector()->tuneDownloadCommand
  153. (getFileEntry()->getRemainingUris(), command.get());
  154. getDownloadEngine()->addCommand(std::move(command));
  155. return true;
  156. }
  157. case SEQ_DOWNLOAD_ALREADY_COMPLETED:
  158. case SEQ_HEAD_OK:
  159. case SEQ_EXIT:
  160. return true;
  161. };
  162. }
  163. again:
  164. addCommandSelf();
  165. if (getSocket()->wantWrite()) {
  166. setWriteCheckSocket(getSocket());
  167. }
  168. return false;
  169. }
  170. void SftpNegotiationCommand::onFileSizeDetermined(int64_t totalLength)
  171. {
  172. getFileEntry()->setLength(totalLength);
  173. if(getFileEntry()->getPath().empty()) {
  174. auto suffixPath = util::createSafePath
  175. (util::percentDecode(std::begin(getRequest()->getFile()),
  176. std::end(getRequest()->getFile())));
  177. getFileEntry()->setPath
  178. (util::applyDir(getOption()->get(PREF_DIR), suffixPath));
  179. getFileEntry()->setSuffixPath(suffixPath);
  180. }
  181. getRequestGroup()->preDownloadProcessing();
  182. if(totalLength == 0) {
  183. sequence_ = SEQ_NEGOTIATION_COMPLETED;
  184. if(getOption()->getAsBool(PREF_DRY_RUN)) {
  185. getRequestGroup()->initPieceStorage();
  186. onDryRunFileFound();
  187. return;
  188. }
  189. if(getDownloadContext()->knowsTotalLength() &&
  190. getRequestGroup()->downloadFinishedByFileLength()) {
  191. // TODO Known issue: if .aria2 file exists, it will not be
  192. // deleted on successful verification, because .aria2 file is
  193. // not loaded. See also
  194. // HttpResponseCommand::handleOtherEncoding()
  195. getRequestGroup()->initPieceStorage();
  196. if(getDownloadContext()->isChecksumVerificationNeeded()) {
  197. A2_LOG_DEBUG("Zero length file exists. Verify checksum.");
  198. auto entry = make_unique<ChecksumCheckIntegrityEntry>
  199. (getRequestGroup());
  200. entry->initValidator();
  201. getPieceStorage()->getDiskAdaptor()->openExistingFile();
  202. getDownloadEngine()->getCheckIntegrityMan()->pushEntry
  203. (std::move(entry));
  204. sequence_ = SEQ_EXIT;
  205. }
  206. else {
  207. getPieceStorage()->markAllPiecesDone();
  208. getDownloadContext()->setChecksumVerified(true);
  209. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  210. A2_LOG_NOTICE
  211. (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED,
  212. GroupId::toHex(getRequestGroup()->getGID()).c_str(),
  213. getRequestGroup()->getFirstFilePath().c_str()));
  214. }
  215. poolConnection();
  216. return;
  217. }
  218. getRequestGroup()->adjustFilename
  219. (std::make_shared<NullProgressInfoFile>());
  220. getRequestGroup()->initPieceStorage();
  221. getPieceStorage()->getDiskAdaptor()->initAndOpenFile();
  222. if(getDownloadContext()->knowsTotalLength()) {
  223. A2_LOG_DEBUG("File length becomes zero and it means download completed.");
  224. // TODO Known issue: if .aria2 file exists, it will not be
  225. // deleted on successful verification, because .aria2 file is
  226. // not loaded. See also
  227. // HttpResponseCommand::handleOtherEncoding()
  228. if(getDownloadContext()->isChecksumVerificationNeeded()) {
  229. A2_LOG_DEBUG("Verify checksum for zero-length file");
  230. auto entry = make_unique<ChecksumCheckIntegrityEntry>
  231. (getRequestGroup());
  232. entry->initValidator();
  233. getDownloadEngine()->getCheckIntegrityMan()->pushEntry
  234. (std::move(entry));
  235. sequence_ = SEQ_EXIT;
  236. } else
  237. {
  238. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  239. getPieceStorage()->markAllPiecesDone();
  240. }
  241. poolConnection();
  242. return;
  243. }
  244. // We have to make sure that command that has Request object must
  245. // have segment after PieceStorage is initialized. See
  246. // AbstractCommand::execute()
  247. getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
  248. return;
  249. } else {
  250. auto progressInfoFile = std::make_shared<DefaultBtProgressInfoFile>
  251. (getDownloadContext(), nullptr, getOption().get());
  252. getRequestGroup()->adjustFilename(progressInfoFile);
  253. getRequestGroup()->initPieceStorage();
  254. if(getOption()->getAsBool(PREF_DRY_RUN)) {
  255. onDryRunFileFound();
  256. return;
  257. }
  258. auto checkIntegrityEntry = getRequestGroup()->createCheckIntegrityEntry();
  259. if(!checkIntegrityEntry) {
  260. sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED;
  261. poolConnection();
  262. return;
  263. }
  264. checkIntegrityEntry->pushNextCommand(std::unique_ptr<Command>(this));
  265. // We have to make sure that command that has Request object must
  266. // have segment after PieceStorage is initialized. See
  267. // AbstractCommand::execute()
  268. getSegmentMan()->getSegmentWithIndex(getCuid(), 0);
  269. prepareForNextAction(std::move(checkIntegrityEntry));
  270. disableReadCheckSocket();
  271. sequence_ = SEQ_FILE_PREPARATION;
  272. }
  273. }
  274. void SftpNegotiationCommand::poolConnection() const
  275. {
  276. if(getOption()->getAsBool(PREF_FTP_REUSE_CONNECTION)) {
  277. // TODO we don't need options. Probably, we need to pool socket
  278. // using scheme, port and auth info as key
  279. getDownloadEngine()->poolSocket(getRequest(), authConfig_->getUser(),
  280. createProxyRequest(), getSocket(), "");
  281. }
  282. }
  283. void SftpNegotiationCommand::onDryRunFileFound()
  284. {
  285. getPieceStorage()->markAllPiecesDone();
  286. getDownloadContext()->setChecksumVerified(true);
  287. poolConnection();
  288. sequence_ = SEQ_HEAD_OK;
  289. }
  290. std::string SftpNegotiationCommand::getPath() const {
  291. auto &req = getRequest();
  292. auto path = req->getDir() + req->getFile();
  293. return util::percentDecode(std::begin(path), std::end(path));
  294. }
  295. } // namespace aria2