SessionSerializer.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /* <!-- copyright */
  2. /*
  3. * aria2 - The high speed download utility
  4. *
  5. * Copyright (C) 2010 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 "SessionSerializer.h"
  36. #include <cstdio>
  37. #include <cassert>
  38. #include <iterator>
  39. #include <set>
  40. #include "RequestGroupMan.h"
  41. #include "a2functional.h"
  42. #include "File.h"
  43. #include "A2STR.h"
  44. #include "download_helper.h"
  45. #include "Option.h"
  46. #include "DownloadResult.h"
  47. #include "FileEntry.h"
  48. #include "prefs.h"
  49. #include "util.h"
  50. #include "array_fun.h"
  51. #include "BufferedFile.h"
  52. #include "OptionParser.h"
  53. #include "OptionHandler.h"
  54. #include "MessageDigest.h"
  55. #if HAVE_ZLIB
  56. #include "GZipFile.h"
  57. #endif
  58. namespace aria2 {
  59. SessionSerializer::SessionSerializer
  60. (RequestGroupMan* requestGroupMan)
  61. : rgman_{requestGroupMan},
  62. saveError_{true},
  63. saveInProgress_{true},
  64. saveWaiting_{true}
  65. {}
  66. bool SessionSerializer::save(const std::string& filename) const
  67. {
  68. std::string tempFilename = filename;
  69. tempFilename += "__temp";
  70. {
  71. std::unique_ptr<IOFile> fp;
  72. #if HAVE_ZLIB
  73. if (util::endsWith(filename, ".gz")) {
  74. fp = make_unique<GZipFile>(tempFilename.c_str(), IOFile::WRITE);
  75. }
  76. else
  77. #endif
  78. {
  79. fp = make_unique<BufferedFile>(tempFilename.c_str(), IOFile::WRITE);
  80. }
  81. if(!*fp) {
  82. return false;
  83. }
  84. if(!save(*fp) || fp->close() == EOF) {
  85. return false;
  86. }
  87. }
  88. return File(tempFilename).renameTo(filename);
  89. }
  90. namespace {
  91. // Write 1 line of option name/value pair. This function returns true
  92. // if it succeeds, or false.
  93. bool writeOptionLine(IOFile& fp, PrefPtr pref,
  94. const std::string& val)
  95. {
  96. size_t prefLen = strlen(pref->k);
  97. return fp.write(" ", 1) == 1 &&
  98. fp.write(pref->k, prefLen) == prefLen &&
  99. fp.write("=", 1) == 1 &&
  100. fp.write(val.c_str(), val.size()) == val.size() &&
  101. fp.write("\n", 1) == 1;
  102. }
  103. } // namespace
  104. namespace {
  105. bool writeOption(IOFile& fp, const std::shared_ptr<Option>& op)
  106. {
  107. const std::shared_ptr<OptionParser>& oparser = OptionParser::getInstance();
  108. for(size_t i = 1, len = option::countOption(); i < len; ++i) {
  109. PrefPtr pref = option::i2p(i);
  110. const OptionHandler* h = oparser->find(pref);
  111. if(h && h->getInitialOption() && op->definedLocal(pref)) {
  112. if(h->getCumulative()) {
  113. const std::string& val = op->get(pref);
  114. std::vector<std::string> v;
  115. util::split(val.begin(), val.end(), std::back_inserter(v), '\n',
  116. false, false);
  117. for(std::vector<std::string>::const_iterator j = v.begin(),
  118. eoj = v.end(); j != eoj; ++j) {
  119. if(!writeOptionLine(fp, pref, *j)) {
  120. return false;
  121. }
  122. }
  123. } else {
  124. if(!writeOptionLine(fp, pref, op->get(pref))) {
  125. return false;
  126. }
  127. }
  128. }
  129. }
  130. return true;
  131. }
  132. } // namespace
  133. namespace {
  134. template<typename T>
  135. class Unique {
  136. typedef T type;
  137. struct PointerCmp {
  138. inline bool operator()(const type* x, const type* y) {
  139. return *x < *y;
  140. }
  141. };
  142. std::set<const type*, PointerCmp> known;
  143. public:
  144. inline bool operator()(const type& v) {
  145. return known.insert(&v).second;
  146. }
  147. };
  148. bool writeUri(IOFile& fp, const std::string& uri)
  149. {
  150. return fp.write(uri.c_str(), uri.size()) == uri.size() &&
  151. fp.write("\t", 1) == 1;
  152. }
  153. template<typename InputIterator, class UnaryPredicate>
  154. bool writeUri(IOFile& fp, InputIterator first, InputIterator last,
  155. UnaryPredicate& filter)
  156. {
  157. for(; first != last; ++first) {
  158. if (!filter(*first)) {
  159. continue;
  160. }
  161. if(!writeUri(fp, *first)) {
  162. return false;
  163. }
  164. }
  165. return true;
  166. }
  167. } // namespace
  168. // The downloads whose followedBy() is empty is persisted with its
  169. // GID without no problem. For other cases, there are several patterns.
  170. //
  171. // 1. magnet URI
  172. // GID of metadata download is persisted.
  173. // 2. URI to torrent file
  174. // GID of torrent file download is persisted.
  175. // 3. URI to metalink file
  176. // GID of metalink file download is persisted.
  177. // 4. local torrent file
  178. // GID of torrent download itself is persisted.
  179. // 5. local metalink file
  180. // No GID is persisted. GID is saved but it is just a random GID.
  181. namespace {
  182. bool writeDownloadResult
  183. (IOFile& fp, std::set<a2_gid_t>& metainfoCache,
  184. const std::shared_ptr<DownloadResult>& dr)
  185. {
  186. const std::shared_ptr<MetadataInfo>& mi = dr->metadataInfo;
  187. if(dr->belongsTo != 0 || (mi && mi->dataOnly())) {
  188. return true;
  189. }
  190. if(!mi) {
  191. // With --force-save option, same gid may be saved twice. (e.g.,
  192. // Downloading .meta4 followed by its content download. First
  193. // .meta4 download is saved and second content download is also
  194. // saved with the same gid.)
  195. if(metainfoCache.count(dr->gid->getNumericId()) != 0) {
  196. return true;
  197. } else {
  198. metainfoCache.insert(dr->gid->getNumericId());
  199. }
  200. // only save first file entry
  201. if(dr->fileEntries.empty()) {
  202. return true;
  203. }
  204. const std::shared_ptr<FileEntry>& file = dr->fileEntries[0];
  205. // Don't save download if there are no URIs.
  206. const bool hasRemaining = !file->getRemainingUris().empty();
  207. const bool hasSpent = !file->getSpentUris().empty();
  208. if (!hasRemaining && !hasSpent) {
  209. return true;
  210. }
  211. // Save spent URIs + remaining URIs. Remove URI in spent URI which
  212. // also exists in remaining URIs.
  213. {
  214. Unique<std::string> unique;
  215. if (hasRemaining && !writeUri(fp, file->getRemainingUris().begin(),
  216. file->getRemainingUris().end(),
  217. unique)) {
  218. return false;
  219. }
  220. if (hasSpent && !writeUri(fp, file->getSpentUris().begin(),
  221. file->getSpentUris().end(),
  222. unique)) {
  223. return false;
  224. }
  225. }
  226. if(fp.write("\n", 1) != 1) {
  227. return false;
  228. }
  229. if(!writeOptionLine(fp, PREF_GID, dr->gid->toHex())) {
  230. return false;
  231. }
  232. } else {
  233. if(metainfoCache.count(mi->getGID()) != 0) {
  234. return true;
  235. } else {
  236. metainfoCache.insert(mi->getGID());
  237. if (fp.write(mi->getUri().c_str(),
  238. mi->getUri().size()) != mi->getUri().size() ||
  239. fp.write("\n", 1) != 1) {
  240. return false;
  241. }
  242. // For downloads generated by metadata (e.g., BitTorrent,
  243. // Metalink), save gid of Metadata download.
  244. if(!writeOptionLine(fp, PREF_GID, GroupId::toHex(mi->getGID()))) {
  245. return false;
  246. }
  247. }
  248. }
  249. return writeOption(fp, dr->option);
  250. }
  251. } // namespace
  252. bool SessionSerializer::save(IOFile& fp) const
  253. {
  254. std::set<a2_gid_t> metainfoCache;
  255. const DownloadResultList& results = rgman_->getDownloadResults();
  256. for(const auto& dr : results) {
  257. if(dr->result == error_code::FINISHED ||
  258. dr->result == error_code::REMOVED) {
  259. if(dr->option->getAsBool(PREF_FORCE_SAVE)) {
  260. if(!writeDownloadResult(fp, metainfoCache, dr)) {
  261. return false;
  262. }
  263. } else {
  264. continue;
  265. }
  266. } else if(dr->result == error_code::IN_PROGRESS) {
  267. if(saveInProgress_) {
  268. if(!writeDownloadResult(fp, metainfoCache, dr)) {
  269. return false;
  270. }
  271. }
  272. } else {
  273. // error download
  274. if(saveError_) {
  275. if(!writeDownloadResult(fp, metainfoCache, dr)) {
  276. return false;
  277. }
  278. }
  279. }
  280. }
  281. {
  282. // Save active downloads.
  283. const RequestGroupList& groups = rgman_->getRequestGroups();
  284. for(const auto& rg : groups) {
  285. std::shared_ptr<DownloadResult> dr = rg->createDownloadResult();
  286. bool stopped = dr->result == error_code::FINISHED ||
  287. dr->result == error_code::REMOVED;
  288. if((!stopped && saveInProgress_) ||
  289. (stopped && dr->option->getAsBool(PREF_FORCE_SAVE))) {
  290. if(!writeDownloadResult(fp, metainfoCache, dr)) {
  291. return false;
  292. }
  293. }
  294. }
  295. }
  296. if(saveWaiting_) {
  297. const RequestGroupList& groups = rgman_->getReservedGroups();
  298. for(const auto& rg : groups) {
  299. std::shared_ptr<DownloadResult> result = rg->createDownloadResult();
  300. if(!writeDownloadResult(fp, metainfoCache, result)) {
  301. return false;
  302. }
  303. // PREF_PAUSE was removed from option, so save it here looking
  304. // property separately.
  305. if(rg->isPauseRequested()) {
  306. if(!writeOptionLine(fp, PREF_PAUSE, A2_V_TRUE)) {
  307. return false;
  308. }
  309. }
  310. }
  311. }
  312. return true;
  313. }
  314. namespace {
  315. // Class to calculate SHA1 hash value for data written into this
  316. // object. No file I/O is done in this class.
  317. class SHA1IOFile : public IOFile {
  318. public:
  319. SHA1IOFile()
  320. : sha1_(MessageDigest::sha1())
  321. {}
  322. std::string digest()
  323. {
  324. return sha1_->digest();
  325. }
  326. protected:
  327. virtual size_t onRead(void* ptr, size_t count) CXX11_OVERRIDE
  328. {
  329. assert(0);
  330. return 0;
  331. }
  332. virtual size_t onWrite(const void* ptr, size_t count) CXX11_OVERRIDE
  333. {
  334. sha1_->update(ptr, count);
  335. return count;
  336. }
  337. virtual char* onGets(char* s, int size) CXX11_OVERRIDE
  338. {
  339. assert(0);
  340. return nullptr;
  341. }
  342. virtual int onVprintf(const char* format, va_list va) CXX11_OVERRIDE
  343. {
  344. assert(0);
  345. return -1;
  346. }
  347. virtual int onFlush() CXX11_OVERRIDE
  348. {
  349. return 0;
  350. }
  351. virtual int onClose() CXX11_OVERRIDE
  352. {
  353. return 0;
  354. }
  355. virtual bool onSupportsColor() CXX11_OVERRIDE
  356. {
  357. return false;
  358. }
  359. virtual bool isError() const CXX11_OVERRIDE
  360. {
  361. return false;
  362. }
  363. virtual bool isEOF() const CXX11_OVERRIDE
  364. {
  365. return false;
  366. }
  367. virtual bool isOpen() const CXX11_OVERRIDE
  368. {
  369. return true;
  370. }
  371. private:
  372. std::unique_ptr<MessageDigest> sha1_;
  373. };
  374. } // namespace
  375. std::string SessionSerializer::calculateHash() const
  376. {
  377. SHA1IOFile sha1io;
  378. auto rv = save(sha1io);
  379. if(!rv) {
  380. return "";
  381. }
  382. return sha1io.digest();
  383. }
  384. } // namespace aria2