/* */ #include "download_helper.h" #include #include #include #include "RequestGroup.h" #include "Option.h" #include "prefs.h" #include "Metalink2RequestGroup.h" #include "ProtocolDetector.h" #include "ParameterizedStringParser.h" #include "PStringBuildVisitor.h" #include "UriListParser.h" #include "SingleFileDownloadContext.h" #include "RecoverableException.h" #include "FatalException.h" #include "message.h" #include "StringFormat.h" #include "DefaultBtContext.h" #include "FileEntry.h" #include "LogFactory.h" #include "File.h" namespace aria2 { static void unfoldURI (std::deque& result, const std::deque& args) { ParameterizedStringParser p; PStringBuildVisitor v; for(std::deque::const_iterator itr = args.begin(); itr != args.end(); ++itr) { v.reset(); p.parse(*itr)->accept(v); result.insert(result.end(), v.getURIs().begin(), v.getURIs().end()); } } static void splitURI(std::deque& result, std::deque::const_iterator begin, std::deque::const_iterator end, size_t numSplit) { size_t numURIs = std::distance(begin, end); if(numURIs >= numSplit) { result.insert(result.end(), begin, end); } else if(numURIs > 0) { for(size_t i = 0; i < numSplit/numURIs; ++i) { result.insert(result.end(), begin, end); } result.insert(result.end(), begin, begin+(numSplit%numURIs)); } } static SharedHandle createRequestGroup (const Option* op, const std::deque& uris, const Option& requestOption, bool useOutOption = false) { SharedHandle rg(new RequestGroup(op, uris)); SharedHandle dctx (new SingleFileDownloadContext(op->getAsInt(PREF_SEGMENT_SIZE), 0, A2STR::NIL, useOutOption? requestOption.get(PREF_OUT):A2STR::NIL)); dctx->setDir(requestOption.get(PREF_DIR)); rg->setDownloadContext(dctx); return rg; } #ifdef ENABLE_BITTORRENT static SharedHandle createBtRequestGroup(const std::string& torrentFilePath, Option* op, const std::deque& auxUris, const Option& requestOption) { SharedHandle rg(new RequestGroup(op, auxUris)); SharedHandle btContext(new DefaultBtContext()); btContext->load(torrentFilePath);// may throw exception if(op->defined(PREF_PEER_ID_PREFIX)) { btContext->setPeerIdPrefix(op->get(PREF_PEER_ID_PREFIX)); } btContext->setDir(requestOption.get(PREF_DIR)); rg->setDownloadContext(btContext); btContext->setOwnerRequestGroup(rg.get()); return rg; } void createRequestGroupForBitTorrent (std::deque >& result, Option* op, const std::deque& uris) { std::deque nargs; if(op->get(PREF_PARAMETERIZED_URI) == V_TRUE) { unfoldURI(nargs, uris); } else { nargs = uris; } // we ignore -Z option here size_t numSplit = op->getAsInt(PREF_SPLIT); std::deque auxUris; splitURI(auxUris, nargs.begin(), nargs.end(), numSplit); SharedHandle rg = createBtRequestGroup(op->get(PREF_TORRENT_FILE), op, auxUris, *op); rg->setNumConcurrentCommand(numSplit); result.push_back(rg); } #endif // ENABLE_BITTORRENT #ifdef ENABLE_METALINK void createRequestGroupForMetalink (std::deque >& result, Option* op) { Metalink2RequestGroup(op).generate(result, op->get(PREF_METALINK_FILE), *op); if(result.empty()) { throw FatalException(MSG_NO_FILES_TO_DOWNLOAD); } } #endif // ENABLE_METALINK class AccRequestGroup { private: std::deque >& _requestGroups; ProtocolDetector _detector; Option* _op; const Option& _requestOption; public: AccRequestGroup(std::deque >& requestGroups, Option* op, const Option& requestOption): _requestGroups(requestGroups), _op(op), _requestOption(requestOption) {} void operator()(const std::string& uri) { if(_detector.isStreamProtocol(uri)) { std::deque streamURIs; size_t numSplit = _op->getAsInt(PREF_SPLIT); for(size_t i = 0; i < numSplit; ++i) { streamURIs.push_back(uri); } SharedHandle rg = createRequestGroup(_op, streamURIs, _requestOption); rg->setNumConcurrentCommand(numSplit); _requestGroups.push_back(rg); } #ifdef ENABLE_BITTORRENT else if(_detector.guessTorrentFile(uri)) { try { _requestGroups.push_back(createBtRequestGroup(uri, _op, std::deque(), _requestOption)); } catch(RecoverableException& e) { // error occurred while parsing torrent file. // We simply ignore it. LogFactory::getInstance()->error(EX_EXCEPTION_CAUGHT, e); } } #endif // ENABLE_BITTORRENT #ifdef ENABLE_METALINK else if(_detector.guessMetalinkFile(uri)) { try { Metalink2RequestGroup(_op).generate(_requestGroups, uri, _requestOption); } catch(RecoverableException& e) { // error occurred while parsing metalink file. // We simply ignore it. LogFactory::getInstance()->error(EX_EXCEPTION_CAUGHT, e); } } #endif // ENABLE_METALINK else { LogFactory::getInstance()->error(MSG_UNRECOGNIZED_URI, (uri).c_str()); } } }; class StreamProtocolFilter { private: ProtocolDetector _detector; public: bool operator()(const std::string& uri) { return _detector.isStreamProtocol(uri); } }; static void copyIfndef(Option& dest, const Option& src, const std::string& name) { if(!dest.defined(name)) { dest.put(name, src.get(name)); } } static void createRequestGroupForUri (std::deque >& result, Option* op, const std::deque& uris, const Option& requestOption) { std::deque nargs; if(op->get(PREF_PARAMETERIZED_URI) == V_TRUE) { unfoldURI(nargs, uris); } else { nargs = uris; } if(op->get(PREF_FORCE_SEQUENTIAL) == V_TRUE) { std::for_each(nargs.begin(), nargs.end(), AccRequestGroup(result, op, requestOption)); } else { std::deque::iterator strmProtoEnd = std::stable_partition(nargs.begin(), nargs.end(), StreamProtocolFilter()); // let's process http/ftp protocols first. if(nargs.begin() != strmProtoEnd) { size_t numSplit = op->getAsInt(PREF_SPLIT); std::deque streamURIs; splitURI(streamURIs, nargs.begin(), strmProtoEnd, numSplit); SharedHandle rg = createRequestGroup(op, streamURIs, requestOption, true); rg->setNumConcurrentCommand(numSplit); result.push_back(rg); } // process remaining URIs(local metalink, BitTorrent files) std::for_each(strmProtoEnd, nargs.end(), AccRequestGroup(result, op, requestOption)); } } void createRequestGroupForUri (std::deque >& result, Option* op, const std::deque& uris) { createRequestGroupForUri(result, op, uris, *op); } static void createRequestGroupForUriList (std::deque >& result, Option* op, std::istream& in) { UriListParser p(in); while(p.hasNext()) { std::deque uris; Option requestOption; p.parseNext(uris, requestOption); if(uris.empty()) { continue; } copyIfndef(requestOption, *op, PREF_DIR); copyIfndef(requestOption, *op, PREF_OUT); createRequestGroupForUri(result, op, uris, requestOption); } } void createRequestGroupForUriList (std::deque >& result, Option* op) { if(op->get(PREF_INPUT_FILE) == "-") { createRequestGroupForUriList(result, op, std::cin); } else { if(!File(op->get(PREF_INPUT_FILE)).isFile()) { throw FatalException (StringFormat(EX_FILE_OPEN, op->get(PREF_INPUT_FILE).c_str(), "No such file").str()); } std::ifstream f(op->get(PREF_INPUT_FILE).c_str(), std::ios::binary); createRequestGroupForUriList(result, op, f); } } } // namespace aria2