123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- /* <!-- copyright */
- /*
- * aria2 - The high speed download utility
- *
- * Copyright (C) 2006 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 "download_helper.h"
- #include <algorithm>
- #include <sstream>
- #include "RequestGroup.h"
- #include "Option.h"
- #include "prefs.h"
- #include "Metalink2RequestGroup.h"
- #include "ProtocolDetector.h"
- #include "paramed_string.h"
- #include "UriListParser.h"
- #include "DownloadContext.h"
- #include "RecoverableException.h"
- #include "DlAbortEx.h"
- #include "message.h"
- #include "fmt.h"
- #include "FileEntry.h"
- #include "LogFactory.h"
- #include "File.h"
- #include "util.h"
- #include "array_fun.h"
- #include "OptionHandler.h"
- #include "ByteArrayDiskWriter.h"
- #include "a2functional.h"
- #include "ByteArrayDiskWriterFactory.h"
- #include "MetadataInfo.h"
- #include "OptionParser.h"
- #include "SegList.h"
- #include "download_handlers.h"
- #include "SimpleRandomizer.h"
- #ifdef ENABLE_BITTORRENT
- #include "bittorrent_helper.h"
- #include "BtConstants.h"
- #include "ValueBaseBencodeParser.h"
- #endif // ENABLE_BITTORRENT
- namespace aria2 {
- namespace {
- void unfoldURI(std::vector<std::string>& result,
- const std::vector<std::string>& args)
- {
- for (const auto& i : args) {
- paramed_string::expand(std::begin(i), std::end(i),
- std::back_inserter(result));
- }
- }
- } // namespace
- namespace {
- template <typename InputIterator>
- void splitURI(std::vector<std::string>& result, InputIterator begin,
- InputIterator end, size_t numSplit, size_t maxIter)
- {
- size_t numURIs = std::distance(begin, end);
- if (numURIs >= numSplit) {
- result.insert(std::end(result), begin, end);
- }
- else if (numURIs > 0) {
- size_t num = std::min(numSplit / numURIs, maxIter);
- for (size_t i = 0; i < num; ++i) {
- result.insert(std::end(result), begin, end);
- }
- if (num < maxIter) {
- result.insert(std::end(result), begin, begin + (numSplit % numURIs));
- }
- }
- }
- } // namespace
- namespace {
- std::shared_ptr<GroupId> getGID(const std::shared_ptr<Option>& option)
- {
- std::shared_ptr<GroupId> gid;
- if (option->defined(PREF_GID)) {
- a2_gid_t n;
- if (GroupId::toNumericId(n, option->get(PREF_GID).c_str()) != 0) {
- throw DL_ABORT_EX(
- fmt("%s is invalid for GID.", option->get(PREF_GID).c_str()));
- }
- gid = GroupId::import(n);
- if (!gid) {
- throw DL_ABORT_EX(
- fmt("GID %s is not unique.", option->get(PREF_GID).c_str()));
- }
- }
- else {
- gid = GroupId::create();
- }
- return gid;
- }
- } // namespace
- namespace {
- std::shared_ptr<RequestGroup>
- createRequestGroup(const std::shared_ptr<Option>& optionTemplate,
- const std::vector<std::string>& uris,
- bool useOutOption = false)
- {
- auto option = util::copy(optionTemplate);
- auto rg = std::make_shared<RequestGroup>(getGID(option), option);
- auto dctx = std::make_shared<DownloadContext>(
- option->getAsInt(PREF_PIECE_LENGTH), 0,
- useOutOption && !option->blank(PREF_OUT)
- ? util::applyDir(option->get(PREF_DIR), option->get(PREF_OUT))
- : A2STR::NIL);
- dctx->getFirstFileEntry()->setUris(uris);
- dctx->getFirstFileEntry()->setMaxConnectionPerServer(
- option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER));
- const std::string& checksum = option->get(PREF_CHECKSUM);
- if (!checksum.empty()) {
- auto p = util::divide(std::begin(checksum), std::end(checksum), '=');
- std::string hashType(p.first.first, p.first.second);
- std::string hexDigest(p.second.first, p.second.second);
- util::lowercase(hashType);
- dctx->setDigest(hashType,
- util::fromHex(std::begin(hexDigest), std::end(hexDigest)));
- }
- rg->setDownloadContext(dctx);
- if (option->getAsBool(PREF_ENABLE_RPC)) {
- rg->setPauseRequested(option->getAsBool(PREF_PAUSE));
- }
- removeOneshotOption(option);
- return rg;
- }
- } // namespace
- #if defined(ENABLE_BITTORRENT) || defined(ENABLE_METALINK)
- namespace {
- std::shared_ptr<MetadataInfo>
- createMetadataInfo(const std::shared_ptr<GroupId>& gid, const std::string& uri)
- {
- return std::make_shared<MetadataInfo>(gid, uri);
- }
- } // namespace
- namespace {
- std::shared_ptr<MetadataInfo> createMetadataInfoDataOnly()
- {
- return std::make_shared<MetadataInfo>();
- }
- } // namespace
- #endif // ENABLE_BITTORRENT || ENABLE_METALINK
- #ifdef ENABLE_BITTORRENT
- namespace {
- std::shared_ptr<RequestGroup>
- createBtRequestGroup(const std::string& metaInfoUri,
- const std::shared_ptr<Option>& optionTemplate,
- const std::vector<std::string>& auxUris,
- const ValueBase* torrent, bool adjustAnnounceUri = true)
- {
- auto option = util::copy(optionTemplate);
- auto gid = getGID(option);
- auto rg = std::make_shared<RequestGroup>(gid, option);
- auto dctx = std::make_shared<DownloadContext>();
- // may throw exception
- bittorrent::loadFromMemory(torrent, dctx, option, auxUris,
- metaInfoUri.empty() ? "default" : metaInfoUri);
- for (auto& fe : dctx->getFileEntries()) {
- auto& uris = fe->getRemainingUris();
- std::shuffle(std::begin(uris), std::end(uris),
- *SimpleRandomizer::getInstance());
- }
- if (metaInfoUri.empty()) {
- rg->setMetadataInfo(createMetadataInfoDataOnly());
- }
- else {
- rg->setMetadataInfo(createMetadataInfo(gid, metaInfoUri));
- }
- if (adjustAnnounceUri) {
- bittorrent::adjustAnnounceUri(bittorrent::getTorrentAttrs(dctx), option);
- }
- auto sgl = util::parseIntSegments(option->get(PREF_SELECT_FILE));
- sgl.normalize();
- dctx->setFileFilter(std::move(sgl));
- std::istringstream indexOutIn(option->get(PREF_INDEX_OUT));
- auto indexPaths = util::createIndexPaths(indexOutIn);
- for (const auto& i : indexPaths) {
- dctx->setFilePathWithIndex(i.first,
- util::applyDir(option->get(PREF_DIR), i.second));
- }
- rg->setDownloadContext(dctx);
- if (option->getAsBool(PREF_ENABLE_RPC)) {
- rg->setPauseRequested(option->getAsBool(PREF_PAUSE));
- }
- // Remove "metalink" from Accept Type list to avoid server from
- // responding Metalink file for web-seeding URIs.
- dctx->setAcceptMetalink(false);
- removeOneshotOption(option);
- return rg;
- }
- } // namespace
- namespace {
- std::shared_ptr<RequestGroup>
- createBtMagnetRequestGroup(const std::string& magnetLink,
- const std::shared_ptr<Option>& optionTemplate)
- {
- auto option = util::copy(optionTemplate);
- auto gid = getGID(option);
- auto rg = std::make_shared<RequestGroup>(gid, option);
- auto dctx = std::make_shared<DownloadContext>(METADATA_PIECE_SIZE, 0);
- // We only know info hash. Total Length is unknown at this moment.
- dctx->markTotalLengthIsUnknown();
- rg->setFileAllocationEnabled(false);
- rg->setPreLocalFileCheckEnabled(false);
- bittorrent::loadMagnet(magnetLink, dctx);
- auto torrentAttrs = bittorrent::getTorrentAttrs(dctx);
- bittorrent::adjustAnnounceUri(torrentAttrs, option);
- // torrentAttrs->name may contain "/", but we use basename of
- // FileEntry::getPath() to print out in-memory download entry.
- // Since "/" is treated as separator, we replace it with "-".
- dctx->getFirstFileEntry()->setPath(
- util::replace(torrentAttrs->name, "/", "-"));
- rg->setDownloadContext(dctx);
- rg->clearPostDownloadHandler();
- rg->addPostDownloadHandler(
- download_handlers::getUTMetadataPostDownloadHandler());
- rg->setDiskWriterFactory(std::make_shared<ByteArrayDiskWriterFactory>());
- rg->setMetadataInfo(createMetadataInfo(gid, magnetLink));
- rg->markInMemoryDownload();
- if (option->getAsBool(PREF_ENABLE_RPC)) {
- rg->setPauseRequested(option->getAsBool(PREF_PAUSE));
- }
- removeOneshotOption(option);
- return rg;
- }
- } // namespace
- void createRequestGroupForBitTorrent(
- std::vector<std::shared_ptr<RequestGroup>>& result,
- const std::shared_ptr<Option>& option, const std::vector<std::string>& uris,
- const std::string& metaInfoUri, const std::string& torrentData,
- bool adjustAnnounceUri)
- {
- std::unique_ptr<ValueBase> torrent;
- bittorrent::ValueBaseBencodeParser parser;
- if (torrentData.empty()) {
- torrent = parseFile(parser, metaInfoUri);
- }
- else {
- ssize_t error;
- torrent = parser.parseFinal(torrentData.c_str(), torrentData.size(), error);
- }
- if (!torrent) {
- throw DL_ABORT_EX2("Bencode decoding failed",
- error_code::BENCODE_PARSE_ERROR);
- }
- createRequestGroupForBitTorrent(result, option, uris, metaInfoUri,
- torrent.get(), adjustAnnounceUri);
- }
- void createRequestGroupForBitTorrent(
- std::vector<std::shared_ptr<RequestGroup>>& result,
- const std::shared_ptr<Option>& option, const std::vector<std::string>& uris,
- const std::string& metaInfoUri, const ValueBase* torrent,
- bool adjustAnnounceUri)
- {
- std::vector<std::string> nargs;
- if (option->get(PREF_PARAMETERIZED_URI) == A2_V_TRUE) {
- unfoldURI(nargs, uris);
- }
- else {
- nargs = uris;
- }
- // we ignore -Z option here
- size_t numSplit = option->getAsInt(PREF_SPLIT);
- auto rg = createBtRequestGroup(metaInfoUri, option, nargs, torrent,
- adjustAnnounceUri);
- rg->setNumConcurrentCommand(numSplit);
- result.push_back(rg);
- }
- #endif // ENABLE_BITTORRENT
- #ifdef ENABLE_METALINK
- void createRequestGroupForMetalink(
- std::vector<std::shared_ptr<RequestGroup>>& result,
- const std::shared_ptr<Option>& option, const std::string& metalinkData)
- {
- if (metalinkData.empty()) {
- Metalink2RequestGroup().generate(result, option->get(PREF_METALINK_FILE),
- option,
- option->get(PREF_METALINK_BASE_URI));
- }
- else {
- auto dw = std::make_shared<ByteArrayDiskWriter>();
- dw->setString(metalinkData);
- Metalink2RequestGroup().generate(result, dw, option,
- option->get(PREF_METALINK_BASE_URI));
- }
- }
- #endif // ENABLE_METALINK
- namespace {
- class AccRequestGroup {
- private:
- std::vector<std::shared_ptr<RequestGroup>>& requestGroups_;
- ProtocolDetector detector_;
- std::shared_ptr<Option> option_;
- bool ignoreLocalPath_;
- bool throwOnError_;
- public:
- AccRequestGroup(std::vector<std::shared_ptr<RequestGroup>>& requestGroups,
- std::shared_ptr<Option> option, bool ignoreLocalPath = false,
- bool throwOnError = false)
- : requestGroups_(requestGroups),
- option_(std::move(option)),
- ignoreLocalPath_(ignoreLocalPath),
- throwOnError_(throwOnError)
- {
- }
- void operator()(const std::string& uri)
- {
- if (detector_.isStreamProtocol(uri)) {
- std::vector<std::string> streamURIs;
- size_t numIter = option_->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
- size_t numSplit = option_->getAsInt(PREF_SPLIT);
- size_t num = std::min(numIter, numSplit);
- for (size_t i = 0; i < num; ++i) {
- streamURIs.push_back(uri);
- }
- auto rg = createRequestGroup(option_, streamURIs);
- rg->setNumConcurrentCommand(numSplit);
- requestGroups_.push_back(rg);
- }
- #ifdef ENABLE_BITTORRENT
- else if (detector_.guessTorrentMagnet(uri)) {
- requestGroups_.push_back(createBtMagnetRequestGroup(uri, option_));
- }
- else if (!ignoreLocalPath_ && detector_.guessTorrentFile(uri)) {
- try {
- bittorrent::ValueBaseBencodeParser parser;
- auto torrent = parseFile(parser, uri);
- if (!torrent) {
- throw DL_ABORT_EX2("Bencode decoding failed",
- error_code::BENCODE_PARSE_ERROR);
- }
- requestGroups_.push_back(
- createBtRequestGroup(uri, option_, {}, torrent.get()));
- }
- catch (RecoverableException& e) {
- if (throwOnError_) {
- throw;
- }
- else {
- // error occurred while parsing torrent file.
- // We simply ignore it.
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
- }
- }
- }
- #endif // ENABLE_BITTORRENT
- #ifdef ENABLE_METALINK
- else if (!ignoreLocalPath_ && detector_.guessMetalinkFile(uri)) {
- try {
- Metalink2RequestGroup().generate(requestGroups_, uri, option_,
- option_->get(PREF_METALINK_BASE_URI));
- }
- catch (RecoverableException& e) {
- if (throwOnError_) {
- throw;
- }
- else {
- // error occurred while parsing metalink file.
- // We simply ignore it.
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
- }
- }
- }
- #endif // ENABLE_METALINK
- else {
- if (throwOnError_) {
- throw DL_ABORT_EX(fmt(MSG_UNRECOGNIZED_URI, uri.c_str()));
- }
- else {
- A2_LOG_ERROR(fmt(MSG_UNRECOGNIZED_URI, uri.c_str()));
- }
- }
- }
- };
- } // namespace
- namespace {
- class StreamProtocolFilter {
- private:
- ProtocolDetector detector_;
- public:
- bool operator()(const std::string& uri)
- {
- return detector_.isStreamProtocol(uri);
- }
- };
- } // namespace
- void createRequestGroupForUri(
- std::vector<std::shared_ptr<RequestGroup>>& result,
- const std::shared_ptr<Option>& option, const std::vector<std::string>& uris,
- bool ignoreForceSequential, bool ignoreLocalPath, bool throwOnError)
- {
- std::vector<std::string> nargs;
- if (option->get(PREF_PARAMETERIZED_URI) == A2_V_TRUE) {
- unfoldURI(nargs, uris);
- }
- else {
- nargs = uris;
- }
- if (!ignoreForceSequential &&
- option->get(PREF_FORCE_SEQUENTIAL) == A2_V_TRUE) {
- std::for_each(
- std::begin(nargs), std::end(nargs),
- AccRequestGroup(result, option, ignoreLocalPath, throwOnError));
- }
- else {
- auto strmProtoEnd = std::stable_partition(
- std::begin(nargs), std::end(nargs), StreamProtocolFilter());
- // let's process http/ftp protocols first.
- if (std::begin(nargs) != strmProtoEnd) {
- size_t numIter = option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
- size_t numSplit = option->getAsInt(PREF_SPLIT);
- std::vector<std::string> streamURIs;
- splitURI(streamURIs, std::begin(nargs), strmProtoEnd, numSplit, numIter);
- try {
- auto rg = createRequestGroup(option, streamURIs, true);
- rg->setNumConcurrentCommand(numSplit);
- result.push_back(rg);
- }
- catch (RecoverableException& e) {
- if (throwOnError) {
- throw;
- }
- else {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
- }
- }
- }
- // process remaining URIs(local metalink, BitTorrent files)
- std::for_each(
- strmProtoEnd, std::end(nargs),
- AccRequestGroup(result, option, ignoreLocalPath, throwOnError));
- }
- }
- bool createRequestGroupFromUriListParser(
- std::vector<std::shared_ptr<RequestGroup>>& result, const Option* option,
- UriListParser* uriListParser)
- {
- // Since result already contains some entries, we cache the size of
- // it. Later, we use this value to determine RequestGroup is
- // actually created.
- size_t num = result.size();
- while (uriListParser->hasNext()) {
- std::vector<std::string> uris;
- Option tempOption;
- uriListParser->parseNext(uris, tempOption);
- if (uris.empty()) {
- continue;
- }
- auto requestOption = std::make_shared<Option>(*option);
- requestOption->remove(PREF_OUT);
- const auto& oparser = OptionParser::getInstance();
- for (size_t i = 1, len = option::countOption(); i < len; ++i) {
- auto pref = option::i2p(i);
- auto h = oparser->find(pref);
- if (h && h->getInitialOption() && tempOption.defined(pref)) {
- requestOption->put(pref, tempOption.get(pref));
- }
- }
- // This does not throw exception because throwOnError = false.
- createRequestGroupForUri(result, requestOption, uris);
- if (num < result.size()) {
- return true;
- }
- }
- return false;
- }
- std::shared_ptr<UriListParser> openUriListParser(const std::string& filename)
- {
- std::string listPath;
- if (filename == "-") {
- listPath = DEV_STDIN;
- }
- else {
- if (!File(filename).isFile()) {
- throw DL_ABORT_EX(fmt(EX_FILE_OPEN, filename.c_str(), "No such file"));
- }
- listPath = filename;
- }
- return std::make_shared<UriListParser>(listPath);
- }
- void createRequestGroupForUriList(
- std::vector<std::shared_ptr<RequestGroup>>& result,
- const std::shared_ptr<Option>& option)
- {
- auto uriListParser = openUriListParser(option->get(PREF_INPUT_FILE));
- while (createRequestGroupFromUriListParser(result, option.get(),
- uriListParser.get()))
- ;
- }
- std::shared_ptr<MetadataInfo> createMetadataInfoFromFirstFileEntry(
- const std::shared_ptr<GroupId>& gid,
- const std::shared_ptr<DownloadContext>& dctx)
- {
- if (dctx->getFileEntries().empty()) {
- return nullptr;
- }
- else {
- auto uris = dctx->getFileEntries()[0]->getUris();
- if (uris.empty()) {
- return nullptr;
- }
- return std::make_shared<MetadataInfo>(gid, uris[0]);
- }
- }
- void removeOneshotOption(const std::shared_ptr<Option>& option)
- {
- option->remove(PREF_PAUSE);
- option->remove(PREF_GID);
- }
- } // namespace aria2
|