From 5cd0108f93d9b91e30b74ec92e66571493a2b997 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> Date: Thu, 8 Apr 2010 12:54:14 +0000 Subject: [PATCH] 2010-04-08 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> Added --save-session=FILE option. This option saves error/unfinished downloads to FILE on exit. You can pass this output file to aria2c with -i option on restart. Please note that downloads added by aria2.addTorrent and aria2.addMetalink XML-RPC method are not saved. * src/BtPostDownloadHandler.cc * src/DownloadResult.h * src/Makefile.am * src/MetadataInfo.cc * src/MetadataInfo.h * src/Metalink2RequestGroup.cc * src/MetalinkPostDownloadHandler.cc * src/MultiUrlRequestInfo.cc * src/OptionHandlerFactory.cc * src/RequestGroup.cc * src/RequestGroup.h * src/SessionSerializer.cc * src/SessionSerializer.h * src/UTMetadataPostDownloadHandler.cc * src/download_helper.cc * src/download_helper.h * src/prefs.cc * src/prefs.h * src/usage_text.h * test/Makefile.am * test/SessionSerializerTest.cc * test/XmlRpcMethodTest.cc * test/serialize_session.meta4 --- ChangeLog | 31 ++++ doc/aria2c.1 | 15 +- doc/aria2c.1.html | 13 +- doc/aria2c.1.txt | 7 + src/BtPostDownloadHandler.cc | 5 + src/DownloadResult.h | 14 +- src/Makefile.am | 4 +- src/Makefile.in | 29 ++-- src/MetadataInfo.cc | 49 +++++++ src/MetadataInfo.h | 76 ++++++++++ src/Metalink2RequestGroup.cc | 17 ++- src/MetalinkPostDownloadHandler.cc | 6 + src/MultiUrlRequestInfo.cc | 11 ++ src/OptionHandlerFactory.cc | 9 ++ src/RequestGroup.cc | 4 +- src/RequestGroup.h | 13 ++ src/SessionSerializer.cc | 203 +++++++++++++++++++++++++++ src/SessionSerializer.h | 65 +++++++++ src/UTMetadataPostDownloadHandler.cc | 5 +- src/download_helper.cc | 29 ++++ src/download_helper.h | 14 ++ src/prefs.cc | 6 +- src/prefs.h | 6 +- src/usage_text.h | 6 + test/Makefile.am | 3 +- test/Makefile.in | 18 ++- test/SessionSerializerTest.cc | 71 ++++++++++ test/XmlRpcMethodTest.cc | 4 +- test/serialize_session.meta4 | 9 ++ 29 files changed, 707 insertions(+), 35 deletions(-) create mode 100644 src/MetadataInfo.cc create mode 100644 src/MetadataInfo.h create mode 100644 src/SessionSerializer.cc create mode 100644 src/SessionSerializer.h create mode 100644 test/SessionSerializerTest.cc create mode 100644 test/serialize_session.meta4 diff --git a/ChangeLog b/ChangeLog index 6fdcad1b..bc92659a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2010-04-08 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> + + Added --save-session=FILE option. This option saves + error/unfinished downloads to FILE on exit. You can pass this + output file to aria2c with -i option on restart. Please note that + downloads added by aria2.addTorrent and aria2.addMetalink XML-RPC + method are not saved. + * src/BtPostDownloadHandler.cc + * src/DownloadResult.h + * src/Makefile.am + * src/MetadataInfo.cc + * src/MetadataInfo.h + * src/Metalink2RequestGroup.cc + * src/MetalinkPostDownloadHandler.cc + * src/MultiUrlRequestInfo.cc + * src/OptionHandlerFactory.cc + * src/RequestGroup.cc + * src/RequestGroup.h + * src/SessionSerializer.cc + * src/SessionSerializer.h + * src/UTMetadataPostDownloadHandler.cc + * src/download_helper.cc + * src/download_helper.h + * src/prefs.cc + * src/prefs.h + * src/usage_text.h + * test/Makefile.am + * test/SessionSerializerTest.cc + * test/XmlRpcMethodTest.cc + * test/serialize_session.meta4 + 2010-04-07 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> Fixed the bug that FTP data connection is not established via diff --git a/doc/aria2c.1 b/doc/aria2c.1 index c56cf6a7..478e928b 100644 --- a/doc/aria2c.1 +++ b/doc/aria2c.1 @@ -2,12 +2,12 @@ .\" Title: aria2c .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/> -.\" Date: 04/03/2010 +.\" Date: 04/08/2010 .\" Manual: Aria2 Manual .\" Source: Aria2 1.9.1a .\" Language: English .\" -.TH "ARIA2C" "1" "04/03/2010" "Aria2 1\&.9\&.1a" "Aria2 Manual" +.TH "ARIA2C" "1" "04/08/2010" "Aria2 1\&.9\&.1a" "Aria2 Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -1354,6 +1354,17 @@ For Metalink downloads, \-C1 is recommended for proxy server which disables resu .sp .5v .RE .PP +\fB\-\-save\-session\fR=FILE +.RS 4 +Save error/unfinished downloads to FILE on exit\&. You can pass this output file to aria2c with +\fB\-i\fR +option on restart\&. Please note that downloads added by +\fBaria2\&.addTorrent\fR +and +\fBaria2\&.addMetalink\fR +XML\-RPC method are not saved\&. +.RE +.PP \fB\-\-stop\fR=SEC .RS 4 Stop application after SEC seconds has passed\&. If diff --git a/doc/aria2c.1.html b/doc/aria2c.1.html index 412e7617..fadabc7b 100644 --- a/doc/aria2c.1.html +++ b/doc/aria2c.1.html @@ -2201,6 +2201,17 @@ connections.</td> </div> <div class="dlist"><dl> <dt class="hdlist1"> +<strong>--save-session</strong>=FILE +</dt> +<dd> +<p> + Save error/unfinished downloads to FILE on exit. You can pass this + output file to aria2c with <strong>-i</strong> option on restart. Please note that + downloads added by <strong>aria2.addTorrent</strong> and <strong>aria2.addMetalink</strong> + XML-RPC method are not saved. +</p> +</dd> +<dt class="hdlist1"> <strong>--stop</strong>=SEC </dt> <dd> @@ -4137,7 +4148,7 @@ files in the program, then also delete it here.</p></div> <div id="footnotes"><hr /></div> <div id="footer"> <div id="footer-text"> -Last updated 2010-04-03 13:10:23 JST +Last updated 2010-04-08 21:48:17 JST </div> </div> </body> diff --git a/doc/aria2c.1.txt b/doc/aria2c.1.txt index 1dba74dd..7bcbad71 100644 --- a/doc/aria2c.1.txt +++ b/doc/aria2c.1.txt @@ -914,6 +914,13 @@ For Metalink downloads, -C1 is recommended for proxy server which disables resume, in order to avoid establishing unnecessary connections. +*--save-session*=FILE:: + + Save error/unfinished downloads to FILE on exit. You can pass this + output file to aria2c with *-i* option on restart. Please note that + downloads added by *aria2.addTorrent* and *aria2.addMetalink* + XML-RPC method are not saved. + *--stop*=SEC:: Stop application after SEC seconds has passed. If '0' is given, this feature is disabled. diff --git a/src/BtPostDownloadHandler.cc b/src/BtPostDownloadHandler.cc index 0281e9f6..2398d7f7 100644 --- a/src/BtPostDownloadHandler.cc +++ b/src/BtPostDownloadHandler.cc @@ -82,6 +82,11 @@ void BtPostDownloadHandler::getNextRequestGroups std::vector<std::string>(), content); requestGroup->followedBy(newRgs.begin(), newRgs.end()); + SharedHandle<MetadataInfo> mi = + createMetadataInfoFromFirstFileEntry(requestGroup->getDownloadContext()); + if(!mi.isNull()) { + setMetadataInfo(newRgs.begin(), newRgs.end(), mi); + } groups.insert(groups.end(), newRgs.begin(), newRgs.end()); } diff --git a/src/DownloadResult.h b/src/DownloadResult.h index 7318d946..54c0f924 100644 --- a/src/DownloadResult.h +++ b/src/DownloadResult.h @@ -45,6 +45,8 @@ #include "SharedHandle.h" #include "DownloadResultCode.h" #include "RequestGroup.h" +#include "Option.h" +#include "MetadataInfo.h" namespace aria2 { @@ -74,6 +76,10 @@ public: // RequestGroup.cc::_belongsToGID. gid_t belongsTo; + SharedHandle<Option> option; + + SharedHandle<MetadataInfo> metadataInfo; + DownloadResult(gid_t gid, const std::vector<SharedHandle<FileEntry> >& fileEntries, bool inMemoryDownload, @@ -81,7 +87,9 @@ public: int64_t sessionTime, downloadresultcode::RESULT result, const std::vector<gid_t> followedBy, - gid_t belongsTo): + gid_t belongsTo, + const SharedHandle<Option>& option, + const SharedHandle<MetadataInfo>& metadataInfo): gid(gid), fileEntries(fileEntries), inMemoryDownload(inMemoryDownload), @@ -89,7 +97,9 @@ public: sessionTime(sessionTime), result(result), followedBy(followedBy), - belongsTo(belongsTo) {} + belongsTo(belongsTo), + option(option), + metadataInfo(metadataInfo) {} }; typedef SharedHandle<DownloadResult> DownloadResultHandle; diff --git a/src/Makefile.am b/src/Makefile.am index b77f82e8..bd527c6f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -198,7 +198,9 @@ SRCS = Socket.h\ CreateRequestCommand.cc CreateRequestCommand.h\ DownloadResultCode.h\ wallclock.h\ - download_helper.cc download_helper.h + download_helper.cc download_helper.h\ + MetadataInfo.cc MetadataInfo.h\ + SessionSerializer.cc SessionSerializer.h if ENABLE_XML_RPC SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\ diff --git a/src/Makefile.in b/src/Makefile.in index c2e33782..aca935f9 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -429,7 +429,8 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ LongestSequencePieceSelector.cc LongestSequencePieceSelector.h \ bitfield.cc bitfield.h BDE.cc BDE.h CreateRequestCommand.cc \ CreateRequestCommand.h DownloadResultCode.h wallclock.h \ - download_helper.cc download_helper.h \ + download_helper.cc download_helper.h MetadataInfo.cc \ + MetadataInfo.h SessionSerializer.cc SessionSerializer.h \ XmlRpcRequestParserController.cc \ XmlRpcRequestParserController.h \ XmlRpcRequestParserStateMachine.cc \ @@ -855,7 +856,8 @@ am__objects_27 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \ URIResult.$(OBJEXT) SelectEventPoll.$(OBJEXT) \ LongestSequencePieceSelector.$(OBJEXT) bitfield.$(OBJEXT) \ BDE.$(OBJEXT) CreateRequestCommand.$(OBJEXT) \ - download_helper.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ + download_helper.$(OBJEXT) MetadataInfo.$(OBJEXT) \ + SessionSerializer.$(OBJEXT) $(am__objects_1) $(am__objects_2) \ $(am__objects_3) $(am__objects_4) $(am__objects_5) \ $(am__objects_6) $(am__objects_7) $(am__objects_8) \ $(am__objects_9) $(am__objects_10) $(am__objects_11) \ @@ -1189,16 +1191,17 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \ LongestSequencePieceSelector.cc LongestSequencePieceSelector.h \ bitfield.cc bitfield.h BDE.cc BDE.h CreateRequestCommand.cc \ CreateRequestCommand.h DownloadResultCode.h wallclock.h \ - download_helper.cc download_helper.h $(am__append_1) \ - $(am__append_2) $(am__append_3) $(am__append_4) \ - $(am__append_5) $(am__append_6) $(am__append_7) \ - $(am__append_8) $(am__append_9) $(am__append_10) \ - $(am__append_11) $(am__append_12) $(am__append_13) \ - $(am__append_14) $(am__append_15) $(am__append_16) \ - $(am__append_17) $(am__append_18) $(am__append_19) \ - $(am__append_20) $(am__append_21) $(am__append_22) \ - $(am__append_23) $(am__append_24) $(am__append_25) \ - $(am__append_26) + download_helper.cc download_helper.h MetadataInfo.cc \ + MetadataInfo.h SessionSerializer.cc SessionSerializer.h \ + $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) $(am__append_6) \ + $(am__append_7) $(am__append_8) $(am__append_9) \ + $(am__append_10) $(am__append_11) $(am__append_12) \ + $(am__append_13) $(am__append_14) $(am__append_15) \ + $(am__append_16) $(am__append_17) $(am__append_18) \ + $(am__append_19) $(am__append_20) $(am__append_21) \ + $(am__append_22) $(am__append_23) $(am__append_24) \ + $(am__append_25) $(am__append_26) noinst_LIBRARIES = libaria2c.a libaria2c_a_SOURCES = $(SRCS) aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\ @@ -1495,6 +1498,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MSEHandshake.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemoryBufferPreDownloadHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MessageDigestHelper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetadataInfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Metalink2RequestGroup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkEntry.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkHelper.Po@am__quote@ @@ -1552,6 +1556,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SelectEventPoll.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerStat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerStatMan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SessionSerializer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Signature.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleBtMessage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleLogger.Po@am__quote@ diff --git a/src/MetadataInfo.cc b/src/MetadataInfo.cc new file mode 100644 index 00000000..dc3bc3c3 --- /dev/null +++ b/src/MetadataInfo.cc @@ -0,0 +1,49 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2010 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 "MetadataInfo.h" + +namespace aria2 { + +int64_t MetadataInfo::_count = 0; + +int64_t MetadataInfo::genId() +{ + if(_count == INT64_MAX) { + _count = 0; + } + return ++_count; +} + +} // namespace aria2 diff --git a/src/MetadataInfo.h b/src/MetadataInfo.h new file mode 100644 index 00000000..4e8293c6 --- /dev/null +++ b/src/MetadataInfo.h @@ -0,0 +1,76 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2010 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_METADATA_INFO_H_ +#define _D_METADATA_INFO_H_ + +#include "common.h" + +#include <string> + +namespace aria2 { + +class MetadataInfo { +private: + int64_t _id; + std::string _uri; + bool _dataOnly; + static int64_t _count; +public: + MetadataInfo(const std::string& uri):_id(genId()), _uri(uri), _dataOnly(false) + {} + + MetadataInfo():_id(genId()), _dataOnly(true) {} + + bool dataOnly() const + { + return _dataOnly; + } + + const std::string& getUri() const + { + return _uri; + } + + int64_t getId() const + { + return _id; + } + + static int64_t genId(); +}; + +} // namespace aria2 + +#endif // _D_METADATA_INFO_H_ diff --git a/src/Metalink2RequestGroup.cc b/src/Metalink2RequestGroup.cc index df275a81..f2413f11 100644 --- a/src/Metalink2RequestGroup.cc +++ b/src/Metalink2RequestGroup.cc @@ -109,7 +109,16 @@ Metalink2RequestGroup::generate { std::vector<SharedHandle<MetalinkEntry> > entries; MetalinkHelper::parseAndQuery(entries, metalinkFile, option.get()); - createRequestGroup(groups, entries, option); + std::vector<SharedHandle<RequestGroup> > tempgroups; + createRequestGroup(tempgroups, entries, option); + SharedHandle<MetadataInfo> mi; + if(metalinkFile == "-") { + mi.reset(new MetadataInfo()); + } else { + mi.reset(new MetadataInfo(metalinkFile)); + } + setMetadataInfo(tempgroups.begin(), tempgroups.end(), mi); + groups.insert(groups.end(), tempgroups.begin(), tempgroups.end()); } void @@ -120,7 +129,11 @@ Metalink2RequestGroup::generate { std::vector<SharedHandle<MetalinkEntry> > entries; MetalinkHelper::parseAndQuery(entries, binaryStream, option.get()); - createRequestGroup(groups, entries, option); + std::vector<SharedHandle<RequestGroup> > tempgroups; + createRequestGroup(tempgroups, entries, option); + SharedHandle<MetadataInfo> mi(new MetadataInfo()); + setMetadataInfo(tempgroups.begin(), tempgroups.end(), mi); + groups.insert(groups.end(), tempgroups.begin(), tempgroups.end()); } void diff --git a/src/MetalinkPostDownloadHandler.cc b/src/MetalinkPostDownloadHandler.cc index 5f43c709..8806aabc 100644 --- a/src/MetalinkPostDownloadHandler.cc +++ b/src/MetalinkPostDownloadHandler.cc @@ -44,6 +44,7 @@ #include "prefs.h" #include "Option.h" #include "DownloadContext.h" +#include "download_helper.h" namespace aria2 { @@ -77,6 +78,11 @@ void MetalinkPostDownloadHandler::getNextRequestGroups Metalink2RequestGroup().generate(newRgs, diskAdaptor, requestGroup->getOption()); requestGroup->followedBy(newRgs.begin(), newRgs.end()); + SharedHandle<MetadataInfo> mi = + createMetadataInfoFromFirstFileEntry(requestGroup->getDownloadContext()); + if(!mi.isNull()) { + setMetadataInfo(newRgs.begin(), newRgs.end(), mi); + } groups.insert(groups.end(), newRgs.begin(), newRgs.end()); diskAdaptor->closeFile(); } catch(Exception& e) { diff --git a/src/MultiUrlRequestInfo.cc b/src/MultiUrlRequestInfo.cc index b70ebfb1..35e1be22 100644 --- a/src/MultiUrlRequestInfo.cc +++ b/src/MultiUrlRequestInfo.cc @@ -55,6 +55,8 @@ #include "Netrc.h" #include "AuthConfigFactory.h" #include "DownloadContext.h" +#include "SessionSerializer.h" +#include "ServerStatMan.h" #ifdef ENABLE_SSL # include "SocketCore.h" # include "TLSContext.h" @@ -193,6 +195,15 @@ downloadresultcode::RESULT MultiUrlRequestInfo::execute() returnValue = s.getLastErrorResult(); } } + SessionSerializer sessionSerializer(e->_requestGroupMan); + // TODO Add option: --save-session-status=error,inprogress,waiting + if(!_option->blank(PREF_SAVE_SESSION)) { + if(sessionSerializer.save(_option->get(PREF_SAVE_SESSION))) { + _logger->notice("Serialized session successfully."); + } else { + _logger->notice("Failed to serialize session."); + } + } } catch(RecoverableException& e) { if(returnValue == downloadresultcode::FINISHED) { returnValue = downloadresultcode::UNKNOWN_ERROR; diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 2dabd7c6..98ad419f 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -442,6 +442,15 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() op->addTag(TAG_ADVANCED); handlers.push_back(op); } + { + SharedHandle<OptionHandler> op(new DefaultOptionHandler + (PREF_SAVE_SESSION, + TEXT_SAVE_SESSION, + NO_DEFAULT_VALUE, + "FILENAME")); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } { SharedHandle<OptionHandler> op(new NumberOptionHandler (PREF_STOP, diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index fef4f88c..76e5a33a 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -1050,7 +1050,9 @@ DownloadResultHandle RequestGroup::createDownloadResult() const _downloadContext->calculateSessionTime(), downloadResult(), _followedByGIDs, - _belongsToGID)); + _belongsToGID, + _option, + _metadataInfo)); } void RequestGroup::reportDownloadFinished() diff --git a/src/RequestGroup.h b/src/RequestGroup.h index 1c940d20..569b1172 100644 --- a/src/RequestGroup.h +++ b/src/RequestGroup.h @@ -46,6 +46,7 @@ #include "TimeA2.h" #include "Request.h" #include "DownloadResultCode.h" +#include "MetadataInfo.h" namespace aria2 { @@ -165,6 +166,8 @@ private: // RequestGroup. gid_t _belongsToGID; + SharedHandle<MetadataInfo> _metadataInfo; + RequestGroupMan* _requestGroupMan; int _resumeFailureCount; @@ -514,6 +517,16 @@ public: bool p2pInvolved() const; + void setMetadataInfo(const SharedHandle<MetadataInfo>& info) + { + _metadataInfo = info; + } + + const SharedHandle<MetadataInfo>& getMetadataInfo() const + { + return _metadataInfo; + } + static void resetGIDCounter() { _gidCounter = 0; } static gid_t newGID(); diff --git a/src/SessionSerializer.cc b/src/SessionSerializer.cc new file mode 100644 index 00000000..45db0e15 --- /dev/null +++ b/src/SessionSerializer.cc @@ -0,0 +1,203 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2010 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 "SessionSerializer.h" + +#include <fstream> +#include <iterator> + +#include "RequestGroupMan.h" +#include "ServerStatMan.h" +#include "a2functional.h" +#include "File.h" +#include "A2STR.h" +#include "download_helper.h" +#include "Option.h" +#include "DownloadResult.h" +#include "FileEntry.h" +#include "prefs.h" +#include "util.h" +#include "array_fun.h" + +namespace aria2 { + +SessionSerializer::SessionSerializer +(const SharedHandle<RequestGroupMan>& requestGroupMan): + _rgman(requestGroupMan), + _saveError(true), + _saveInProgress(true), + _saveWaiting(true) {} + +bool SessionSerializer::save(const std::string& filename) const +{ + std::string tempFilename = strconcat(filename, "__temp"); + std::ofstream out(tempFilename.c_str(), std::ios::binary); + if(!out) { + return false; + } + save(out); + out.flush(); + if(!out) { + return false; + } + return File(tempFilename).renameTo(filename); +} + + +static const std::vector<std::string>& getCumulativeOpts() +{ + static std::string cumulativeOpts[] = { PREF_INDEX_OUT, PREF_HEADER }; + static std::vector<std::string> opts + (vbegin(cumulativeOpts), vend(cumulativeOpts)); + return opts; +} + +static bool inCumulativeOpts(const std::string& opt) +{ + const std::vector<std::string>& cumopts = getCumulativeOpts(); + for(std::vector<std::string>::const_iterator itr = cumopts.begin(), + eoi = cumopts.end(); itr != eoi; ++itr) { + if(opt == *itr) { + return true; + } + } + return false; +} + +static void writeOption(std::ostream& out, const SharedHandle<Option>& op) +{ + const std::set<std::string>& requestOptions = listRequestOptions(); + for(std::set<std::string>::const_iterator itr = requestOptions.begin(), + eoi = requestOptions.end(); itr != eoi; ++itr) { + if(inCumulativeOpts(*itr)) { + continue; + } + if(op->defined(*itr)) { + out << " " << *itr << "=" << op->get(*itr) << "\n"; + } + } + const std::vector<std::string>& cumopts = getCumulativeOpts(); + for(std::vector<std::string>::const_iterator opitr = cumopts.begin(), + eoi = cumopts.end(); opitr != eoi; ++opitr) { + if(op->defined(*opitr)) { + std::vector<std::string> v; + util::split(op->get(*opitr), std::back_inserter(v), "\n", + false, false); + for(std::vector<std::string>::const_iterator i = v.begin(), eoi = v.end(); + i != eoi; ++i) { + out << " " << *opitr << "=" << *i << "\n"; + } + } + } +} + +static void writeDownloadResult +(std::ostream& out, std::set<int64_t>& metainfoCache, + const SharedHandle<DownloadResult>& dr) +{ + const SharedHandle<MetadataInfo>& mi = dr->metadataInfo; + if(dr->belongsTo != 0 || (!mi.isNull() && mi->dataOnly())) { + return; + } + if(mi.isNull()) { + // only save first file entry + if(dr->fileEntries.empty()) { + return; + } + const SharedHandle<FileEntry>& file = dr->fileEntries[0]; + std::vector<std::string> uris; + file->getUris(uris); + if(uris.empty()) { + return; + } + std::copy(uris.begin(), uris.end(), + std::ostream_iterator<std::string>(out, "\t")); + out << "\n"; + } else { + if(metainfoCache.count(mi->getId()) != 0) { + return; + } else { + metainfoCache.insert(mi->getId()); + out << mi->getUri() << "\n"; + } + } + writeOption(out, dr->option); +} + +void SessionSerializer::save(std::ostream& out) const +{ + std::set<int64_t> metainfoCache; + const std::deque<SharedHandle<DownloadResult> >& results = + _rgman->getDownloadResults(); + for(std::deque<SharedHandle<DownloadResult> >::const_iterator itr = + results.begin(), eoi = results.end(); itr != eoi; ++itr) { + if((*itr)->result == downloadresultcode::FINISHED) { + continue; + } else if((*itr)->result == downloadresultcode::IN_PROGRESS) { + if(_saveInProgress) { + writeDownloadResult(out, metainfoCache, *itr); + } + } else { + // error download + if(_saveError) { + writeDownloadResult(out, metainfoCache, *itr); + } + } + } + if(_saveInProgress) { + const std::deque<SharedHandle<RequestGroup> >& groups = + _rgman->getRequestGroups(); + for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr = + groups.begin(), eoi = groups.end(); itr != eoi; ++itr) { + SharedHandle<DownloadResult> result = (*itr)->createDownloadResult(); + if(result->result == downloadresultcode::FINISHED) { + continue; + } + writeDownloadResult(out, metainfoCache, result); + } + } + if(_saveWaiting) { + const std::deque<SharedHandle<RequestGroup> >& groups = + _rgman->getReservedGroups(); + for(std::deque<SharedHandle<RequestGroup> >::const_iterator itr = + groups.begin(), eoi = groups.end(); itr != eoi; ++itr) { + SharedHandle<DownloadResult> result = (*itr)->createDownloadResult(); + writeDownloadResult(out, metainfoCache, result); + } + } +} + +} // namespace aria2 + + diff --git a/src/SessionSerializer.h b/src/SessionSerializer.h new file mode 100644 index 00000000..53ca392b --- /dev/null +++ b/src/SessionSerializer.h @@ -0,0 +1,65 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2010 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_SESSION_SERIALIZER_H_ +#define _D_SESSION_SERIALIZER_H_ + +#include "common.h" + +#include <string> +#include <iosfwd> + +#include "SharedHandle.h" + +namespace aria2 { + +class RequestGroupMan; + +class SessionSerializer { +private: + SharedHandle<RequestGroupMan> _rgman; + bool _saveError; + bool _saveInProgress; + bool _saveWaiting; +public: + SessionSerializer(const SharedHandle<RequestGroupMan>& requestGroupMan); + + bool save(const std::string& filename) const; + + void save(std::ostream& out) const; +}; + +} // namespace aria2 + +#endif // _D_SESSION_SERIALIZER_H_ diff --git a/src/UTMetadataPostDownloadHandler.cc b/src/UTMetadataPostDownloadHandler.cc index 3d8f7f10..672f6995 100644 --- a/src/UTMetadataPostDownloadHandler.cc +++ b/src/UTMetadataPostDownloadHandler.cc @@ -96,8 +96,11 @@ void UTMetadataPostDownloadHandler::getNextRequestGroups std::vector<SharedHandle<RequestGroup> > newRgs; createRequestGroupForBitTorrent(newRgs, requestGroup->getOption(), std::vector<std::string>(), torrent); - requestGroup->followedBy(newRgs.begin(), newRgs.end()); + if(!requestGroup->getMetadataInfo().isNull()) { + setMetadataInfo(newRgs.begin(), newRgs.end(), + requestGroup->getMetadataInfo()); + } groups.insert(groups.end(), newRgs.begin(), newRgs.end()); } } diff --git a/src/download_helper.cc b/src/download_helper.cc index f2f6ecfb..ca43aa3d 100644 --- a/src/download_helper.cc +++ b/src/download_helper.cc @@ -61,6 +61,7 @@ #include "ByteArrayDiskWriter.h" #include "a2functional.h" #include "ByteArrayDiskWriterFactory.h" +#include "MetadataInfo.h" #ifdef ENABLE_BITTORRENT # include "bittorrent_helper.h" # include "BtConstants.h" @@ -212,6 +213,16 @@ static SharedHandle<RequestGroup> createRequestGroup return rg; } +static SharedHandle<MetadataInfo> createMetadataInfo(const std::string& uri) +{ + return SharedHandle<MetadataInfo>(new MetadataInfo(uri)); +} + +static SharedHandle<MetadataInfo> createMetadataInfoDataOnly() +{ + return SharedHandle<MetadataInfo>(new MetadataInfo()); +} + #ifdef ENABLE_BITTORRENT static @@ -226,10 +237,12 @@ createBtRequestGroup(const std::string& torrentFilePath, dctx->setDir(option->get(PREF_DIR)); if(torrentData.empty()) { bittorrent::load(torrentFilePath, dctx, auxUris);// may throw exception + rg->setMetadataInfo(createMetadataInfo(torrentFilePath)); } else { bittorrent::loadFromMemory(torrentData, dctx, auxUris, "default"); // may // throw // exception + rg->setMetadataInfo(createMetadataInfoDataOnly()); } dctx->setFileFilter(util::parseIntRange(option->get(PREF_SELECT_FILE))); std::istringstream indexOutIn(option->get(PREF_INDEX_OUT)); @@ -272,6 +285,7 @@ createBtMagnetRequestGroup(const std::string& magnetLink, (new UTMetadataPostDownloadHandler())); rg->setDiskWriterFactory (SharedHandle<DiskWriterFactory>(new ByteArrayDiskWriterFactory())); + rg->setMetadataInfo(createMetadataInfo(magnetLink)); return rg; } @@ -476,4 +490,19 @@ void createRequestGroupForUriList } } +SharedHandle<MetadataInfo> +createMetadataInfoFromFirstFileEntry(const SharedHandle<DownloadContext>& dctx) +{ + if(dctx->getFileEntries().empty()) { + return SharedHandle<MetadataInfo>(); + } else { + std::vector<std::string> uris; + dctx->getFileEntries()[0]->getUris(uris); + if(uris.empty()) { + return SharedHandle<MetadataInfo>(); + } + return SharedHandle<MetadataInfo>(new MetadataInfo(uris[0])); + } +} + } // namespace aria2 diff --git a/src/download_helper.h b/src/download_helper.h index e6d00dd4..c3f34827 100644 --- a/src/download_helper.h +++ b/src/download_helper.h @@ -47,6 +47,8 @@ namespace aria2 { class RequestGroup; class Option; +class MetadataInfo; +class DownloadContext; const std::set<std::string>& listRequestOptions(); @@ -90,6 +92,18 @@ void createRequestGroupForUri bool ignoreForceSequential = false, bool ignoreLocalPath = false); +template<typename InputIterator> +void setMetadataInfo +(InputIterator first, InputIterator last, const SharedHandle<MetadataInfo>& mi) +{ + for(; first != last; ++first) { + (*first)->setMetadataInfo(mi); + } +} + +SharedHandle<MetadataInfo> +createMetadataInfoFromFirstFileEntry(const SharedHandle<DownloadContext>& dctx); + } // namespace aria2 #endif // _D_DOWNLOAD_HELPER_H_ diff --git a/src/prefs.cc b/src/prefs.cc index 8d82f024..5beeb0e5 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -184,8 +184,8 @@ const std::string PREF_REMOVE_CONTROL_FILE("remove-control-file"); const std::string PREF_ALWAYS_RESUME("always-resume"); // value: 1*digit const std::string PREF_MAX_RESUME_FAILURE_TRIES("max-resume-failure-tries"); -// value: true | false -const std::string PREF_HTTP_ACCEPT_GZIP("http-accept-gzip"); +// value: string that your file system recognizes as a file name. +const std::string PREF_SAVE_SESSION("save-session"); /** * FTP related preferences @@ -234,6 +234,8 @@ const std::string PREF_USE_HEAD("use-head"); const std::string PREF_HTTP_AUTH_CHALLENGE("http-auth-challenge"); // value: true | false const std::string PREF_HTTP_NO_CACHE("http-no-cache"); +// value: true | false +const std::string PREF_HTTP_ACCEPT_GZIP("http-accept-gzip"); /** * Proxy related preferences diff --git a/src/prefs.h b/src/prefs.h index 35a289ed..189bb762 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -188,8 +188,8 @@ extern const std::string PREF_REMOVE_CONTROL_FILE; extern const std::string PREF_ALWAYS_RESUME; // value: 1*digit extern const std::string PREF_MAX_RESUME_FAILURE_TRIES; -// value: true | false -extern const std::string PREF_HTTP_ACCEPT_GZIP; +// value: string that your file system recognizes as a file name. +extern const std::string PREF_SAVE_SESSION; /** * FTP related preferences @@ -238,6 +238,8 @@ extern const std::string PREF_USE_HEAD; extern const std::string PREF_HTTP_AUTH_CHALLENGE; // value: true | false extern const std::string PREF_HTTP_NO_CACHE; +// value: true | false +extern const std::string PREF_HTTP_ACCEPT_GZIP; /**; * Proxy related preferences diff --git a/src/usage_text.h b/src/usage_text.h index deb21ba0..e9d9f2b2 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -672,3 +672,9 @@ " and inflate response if remote server responds\n" \ " with 'Content-Encoding: gzip' or\n" \ " 'Content-Encoding: deflate'.") +#define TEXT_SAVE_SESSION \ + _(" --save-session=FILE Save error/unfinished downloads to FILE on exit.\n" \ + " You can pass this output file to aria2c with -i\n" \ + " option on restart. Please note that downloads\n" \ + " added by aria2.addTorrent and aria2.addMetalink\n" \ + " XML-RPC method are not saved.") diff --git a/test/Makefile.am b/test/Makefile.am index df411531..6f3ff423 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -71,7 +71,8 @@ aria2c_SOURCES = AllTest.cc\ a2algoTest.cc\ bitfieldTest.cc\ BDETest.cc\ - DownloadContextTest.cc + DownloadContextTest.cc\ + SessionSerializerTest.cc if ENABLE_XML_RPC aria2c_SOURCES += XmlRpcRequestParserControllerTest.cc\ diff --git a/test/Makefile.in b/test/Makefile.in index 26777ae6..767ca9ce 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -213,7 +213,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \ RarestPieceSelectorTest.cc PieceStatManTest.cc \ InOrderPieceSelector.h LongestSequencePieceSelectorTest.cc \ a2algoTest.cc bitfieldTest.cc BDETest.cc \ - DownloadContextTest.cc XmlRpcRequestParserControllerTest.cc \ + DownloadContextTest.cc SessionSerializerTest.cc \ + XmlRpcRequestParserControllerTest.cc \ XmlRpcRequestProcessorTest.cc XmlRpcMethodTest.cc \ FallocFileAllocationIteratorTest.cc GZipDecoderTest.cc \ GZipEncoderTest.cc Sqlite3MozCookieParserTest.cc \ @@ -406,9 +407,10 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \ RarestPieceSelectorTest.$(OBJEXT) PieceStatManTest.$(OBJEXT) \ LongestSequencePieceSelectorTest.$(OBJEXT) \ a2algoTest.$(OBJEXT) bitfieldTest.$(OBJEXT) BDETest.$(OBJEXT) \ - DownloadContextTest.$(OBJEXT) $(am__objects_1) \ - $(am__objects_2) $(am__objects_3) $(am__objects_4) \ - $(am__objects_5) $(am__objects_6) $(am__objects_7) + DownloadContextTest.$(OBJEXT) SessionSerializerTest.$(OBJEXT) \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_5) $(am__objects_6) \ + $(am__objects_7) aria2c_OBJECTS = $(am_aria2c_OBJECTS) am__DEPENDENCIES_1 = aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1) @@ -639,9 +641,10 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \ RarestPieceSelectorTest.cc PieceStatManTest.cc \ InOrderPieceSelector.h LongestSequencePieceSelectorTest.cc \ a2algoTest.cc bitfieldTest.cc BDETest.cc \ - DownloadContextTest.cc $(am__append_1) $(am__append_2) \ - $(am__append_3) $(am__append_4) $(am__append_5) \ - $(am__append_6) $(am__append_7) + DownloadContextTest.cc SessionSerializerTest.cc \ + $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) $(am__append_6) \ + $(am__append_7) #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64 #aria2c_LDFLAGS = ${CPPUNIT_LIBS} @@ -870,6 +873,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SequentialPickerTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerStatManTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerStatTest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SessionSerializerTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ShareRatioSeedCriteriaTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SharedHandleTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SignatureTest.Po@am__quote@ diff --git a/test/SessionSerializerTest.cc b/test/SessionSerializerTest.cc new file mode 100644 index 00000000..10b96618 --- /dev/null +++ b/test/SessionSerializerTest.cc @@ -0,0 +1,71 @@ +#include "SessionSerializer.h" + +#include <iostream> +#include <sstream> + +#include <cppunit/extensions/HelperMacros.h> + +#include "RequestGroupMan.h" +#include "ServerStatMan.h" +#include "array_fun.h" +#include "download_helper.h" +#include "FileEntry.h" +#include "prefs.h" + +namespace aria2 { + +class SessionSerializerTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(SessionSerializerTest); + CPPUNIT_TEST(testSave); + CPPUNIT_TEST_SUITE_END(); +public: + void testSave(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(SessionSerializerTest); + +void SessionSerializerTest::testSave() +{ +#if defined(ENABLE_BITTORRENT) && defined(ENABLE_METALINK) + const std::string URIs[] = + { "http://localhost/file", + "http://mirror/file", + "test.torrent", + "serialize_session.meta4", + "magnet:?xt=urn:btih:248D0A1CD08284299DE78D5C1ED359BB46717D8C"}; + std::vector<std::string> uris(vbegin(URIs), vend(URIs)); + std::vector<SharedHandle<RequestGroup> > result; + SharedHandle<Option> option(new Option()); + option->put(PREF_DIR, "/tmp"); + createRequestGroupForUri(result, option, uris); + CPPUNIT_ASSERT_EQUAL((size_t)5, result.size()); + SharedHandle<RequestGroupMan> rgman + (new RequestGroupMan(result, 1, option.get())); + SessionSerializer s(rgman); + std::stringstream ss; + s.save(ss); + std::string line; + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(strconcat(uris[0], "\t", uris[1], "\t"), line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(std::string(" dir=/tmp"), line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(uris[2], line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(std::string(" dir=/tmp"), line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(uris[3], line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(std::string(" dir=/tmp"), line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(uris[4], line); + std::getline(ss, line); + CPPUNIT_ASSERT_EQUAL(std::string(" dir=/tmp"), line); + std::getline(ss, line); + CPPUNIT_ASSERT(!ss); +#endif // defined(ENABLE_BITTORRENT) && defined(ENABLE_METALINK) +} + +} // namespace aria2 diff --git a/test/XmlRpcMethodTest.cc b/test/XmlRpcMethodTest.cc index 84c26474..74aa128e 100644 --- a/test/XmlRpcMethodTest.cc +++ b/test/XmlRpcMethodTest.cc @@ -679,7 +679,9 @@ void XmlRpcMethodTest::testGatherStoppedDownload() 1000, downloadresultcode::FINISHED, followedBy, - 2)); + 2, + SharedHandle<Option>(), + SharedHandle<MetadataInfo>())); BDE entry = BDE::dict(); gatherStoppedDownload(entry, d); diff --git a/test/serialize_session.meta4 b/test/serialize_session.meta4 new file mode 100644 index 00000000..e4d56d52 --- /dev/null +++ b/test/serialize_session.meta4 @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metalink xmlns="urn:ietf:params:xml:ns:metalink"> + <file name="t/README"> + <url>http://example.org/README</url> + </file> + <file name="t/image.iso"> + <url>http://example.org/image.iso</url> + </file> +</metalink>