From 4f3c526dcdf5fce74dd12be1c882f6c47d1829ec Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 4 Jun 2014 21:45:12 +0900 Subject: [PATCH] Support PREF_DIR change for Metalink files Reworked previous commit adeead6f0396e2f8551d1182972e277728fd6c8b, and now support changing PREF_DIR for Metalink downloads. --- src/FileEntry.cc | 5 +++++ src/FileEntry.h | 10 ++++++++++ src/FtpNegotiationCommand.cc | 10 ++++++---- src/HttpResponseCommand.cc | 7 +++++-- src/Metalink2RequestGroup.cc | 3 +++ src/RpcMethodImpl.cc | 33 +++++++++++++++++++++---------- src/bittorrent_helper.cc | 24 ++++++++++++++-------- src/util.cc | 7 +++++++ src/util.h | 2 ++ test/BittorrentHelperTest.cc | 8 ++++++++ test/Metalink2RequestGroupTest.cc | 2 ++ 11 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/FileEntry.cc b/src/FileEntry.cc index fb4c713e..9fb08d03 100644 --- a/src/FileEntry.cc +++ b/src/FileEntry.cc @@ -572,6 +572,11 @@ void FileEntry::setOriginalName(std::string originalName) originalName_ = std::move(originalName); } +void FileEntry::setSuffixPath(std::string suffixPath) +{ + suffixPath_ = std::move(suffixPath); +} + bool FileEntry::emptyRequestUri() const { return uris_.empty() && inFlightRequests_.empty() && requestPool_.empty(); diff --git a/src/FileEntry.h b/src/FileEntry.h index 9e760a83..5f381d0c 100644 --- a/src/FileEntry.h +++ b/src/FileEntry.h @@ -85,6 +85,9 @@ private: std::string path_; std::string contentType_; std::string originalName_; + // path_ without parent directory component. This is primarily used + // to change directory (PREF_DIR option). + std::string suffixPath_; Timer lastFasterReplace_; int maxConnectionPerServer_; @@ -264,6 +267,13 @@ public: return originalName_; } + void setSuffixPath(std::string suffixPath); + + const std::string& getSuffixPath() const + { + return suffixPath_; + } + bool removeUri(const std::string& uri); bool emptyRequestUri() const; diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc index 16237fe0..062db5e7 100644 --- a/src/FtpNegotiationCommand.cc +++ b/src/FtpNegotiationCommand.cc @@ -361,11 +361,13 @@ bool FtpNegotiationCommand::onFileSizeDetermined(int64_t totalLength) { getFileEntry()->setLength(totalLength); if(getFileEntry()->getPath().empty()) { + auto suffixPath = util::createSafePath + (util::percentDecode(std::begin(getRequest()->getFile()), + std::end(getRequest()->getFile()))); + getFileEntry()->setPath - (util::createSafePath - (getOption()->get(PREF_DIR), - util::percentDecode(getRequest()->getFile().begin(), - getRequest()->getFile().end()))); + (util::applyDir(getOption()->get(PREF_DIR), suffixPath)); + getFileEntry()->setSuffixPath(suffixPath); } getRequestGroup()->preDownloadProcessing(); if(totalLength == 0) { diff --git a/src/HttpResponseCommand.cc b/src/HttpResponseCommand.cc index 34ae1cc8..9e9477ab 100644 --- a/src/HttpResponseCommand.cc +++ b/src/HttpResponseCommand.cc @@ -249,8 +249,11 @@ bool HttpResponseCommand::executeInternal() int64_t totalLength = httpResponse->getEntityLength(); fe->setLength(totalLength); if (fe->getPath().empty()) { - fe->setPath(util::createSafePath(getOption()->get(PREF_DIR), - httpResponse->determineFilename())); + auto suffixPath = + util::createSafePath(httpResponse->determineFilename()); + + fe->setPath(util::applyDir(getOption()->get(PREF_DIR), suffixPath)); + fe->setSuffixPath(suffixPath); } fe->setContentType(httpResponse->getContentType()); grp->preDownloadProcessing(); diff --git a/src/Metalink2RequestGroup.cc b/src/Metalink2RequestGroup.cc index 6463aae6..9eb509b9 100644 --- a/src/Metalink2RequestGroup.cc +++ b/src/Metalink2RequestGroup.cc @@ -265,6 +265,8 @@ Metalink2RequestGroup::createRequestGroup entry->file->getPath())); dctx->getFirstFileEntry()->setUris(uris); dctx->getFirstFileEntry()->setMaxConnectionPerServer(maxConn); + dctx->getFirstFileEntry()->setSuffixPath(entry->file->getPath()); + if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) { dctx->getFirstFileEntry()->setUniqueProtocol(true); } @@ -307,6 +309,7 @@ Metalink2RequestGroup::createRequestGroup fe->setUniqueProtocol(true); } fe->setOriginalName(entry->metaurls[0]->name); + fe->setSuffixPath(entry->file->getPath()); fileEntries.push_back(fe); if(offset > std::numeric_limits::max() - entry->file->getLength()) { diff --git a/src/RpcMethodImpl.cc b/src/RpcMethodImpl.cc index e7ef98c0..375f4e3d 100644 --- a/src/RpcMethodImpl.cc +++ b/src/RpcMethodImpl.cc @@ -1473,22 +1473,35 @@ void changeOption } } if(option.defined(PREF_DIR) || option.defined(PREF_OUT)) { - if(dctx->getFileEntries().size() == 1 -#ifdef ENABLE_BITTORRENT - && !dctx->hasAttribute(CTX_ATTR_BT) -#endif // ENABLE_BITTORRENT - ) { + if(!group->getMetadataInfo()) { + + assert(dctx->getFileEntries().size() == 1); auto& fileEntry = dctx->getFirstFileEntry(); - if(grOption->blank(PREF_OUT)) { - // We need to reset length to 0, so that we pretend that file - // name is unknown and it should be determined at next run. - fileEntry->setLength(0); + if(!grOption->blank(PREF_OUT)) { + fileEntry->setPath + (util::applyDir(grOption->get(PREF_DIR), grOption->get(PREF_OUT))); + fileEntry->setSuffixPath(A2STR::NIL); + } else if(fileEntry->getSuffixPath().empty()) { fileEntry->setPath(A2STR::NIL); } else { fileEntry->setPath - (util::applyDir(grOption->get(PREF_DIR), grOption->get(PREF_OUT))); + (util::applyDir(grOption->get(PREF_DIR), + fileEntry->getSuffixPath())); + } + } else if(group->getMetadataInfo() +#ifdef ENABLE_BITTORRENT + && !dctx->hasAttribute(CTX_ATTR_BT) +#endif // ENABLE_BITTORRENT + ) { + // In case of Metalink + for(auto& fileEntry : dctx->getFileEntries()) { + // PREF_OUT is not applicable to Metalink. We have always + // suffixPath set. + fileEntry->setPath + (util::applyDir(grOption->get(PREF_DIR), + fileEntry->getSuffixPath())); } } } diff --git a/src/bittorrent_helper.cc b/src/bittorrent_helper.cc index 4f2309f8..23cfdbbb 100644 --- a/src/bittorrent_helper.cc +++ b/src/bittorrent_helper.cc @@ -278,11 +278,15 @@ void extractFileEntries (util::percentEncode))); std::vector uris; createUri(urlList.begin(), urlList.end(),std::back_inserter(uris),pePath); - std::shared_ptr fileEntry - (new FileEntry(util::applyDir(option->get(PREF_DIR), - util::escapePath(utf8Path)), - fileLengthData->i(), offset, uris)); + + auto suffixPath = util::escapePath(utf8Path); + + auto fileEntry = + std::make_shared(util::applyDir(option->get(PREF_DIR), + suffixPath), + fileLengthData->i(), offset, uris); fileEntry->setOriginalName(utf8Path); + fileEntry->setSuffixPath(suffixPath); fileEntry->setMaxConnectionPerServer(maxConn); fileEntries.push_back(fileEntry); offset += fileEntry->getLength(); @@ -316,11 +320,15 @@ void extractFileEntries uris.push_back(elem); } } - std::shared_ptr fileEntry - (new FileEntry(util::applyDir(option->get(PREF_DIR), - util::escapePath(utf8Name)), - totalLength, 0, uris)); + + auto suffixPath = util::escapePath(utf8Name); + + auto fileEntry = + std::make_shared(util::applyDir(option->get(PREF_DIR), + suffixPath), + totalLength, 0, uris); fileEntry->setOriginalName(utf8Name); + fileEntry->setSuffixPath(suffixPath); fileEntry->setMaxConnectionPerServer(maxConn); fileEntries.push_back(fileEntry); } diff --git a/src/util.cc b/src/util.cc index 0f8bce55..7ead5964 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1878,6 +1878,13 @@ std::string createSafePath ); } +std::string createSafePath(const std::string& filename) +{ + return util::isUtf8(filename) ? + util::fixTaintedBasename(filename) : + util::escapePath(util::percentEncode(filename)); +} + std::string encodeNonUtf8(const std::string& s) { return util::isUtf8(s)?s:util::percentEncode(s); diff --git a/src/util.h b/src/util.h index 9e6744e5..a3a30c44 100644 --- a/src/util.h +++ b/src/util.h @@ -791,6 +791,8 @@ void executeHookByOptName std::string createSafePath(const std::string& dir, const std::string& filename); +std::string createSafePath(const std::string& filename); + std::string encodeNonUtf8(const std::string& s); // Create string safely. If str is NULL, returns empty string. diff --git a/test/BittorrentHelperTest.cc b/test/BittorrentHelperTest.cc index 2562c933..65603aae 100644 --- a/test/BittorrentHelperTest.cc +++ b/test/BittorrentHelperTest.cc @@ -198,6 +198,8 @@ void BittorrentHelperTest::testGetFileEntriesSingle() { fileEntry1->getPath()); CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.8.2.tar.bz2"), fileEntry1->getOriginalName()); + CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.8.2.tar.bz2"), + fileEntry1->getSuffixPath()); CPPUNIT_ASSERT_EQUAL(10, fileEntry1->getMaxConnectionPerServer()); } @@ -355,6 +357,8 @@ void BittorrentHelperTest::testGetFileEntries_multiFileUrlList() { const std::shared_ptr& fileEntry1 = *itr; CPPUNIT_ASSERT_EQUAL(std::string("./aria2-test@/aria2@/src@/aria2c@"), fileEntry1->getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("aria2-test@/aria2@/src@/aria2c@"), + fileEntry1->getSuffixPath()); const std::deque& uris1 = fileEntry1->getRemainingUris(); CPPUNIT_ASSERT_EQUAL((size_t)2, uris1.size()); CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/dist/aria2-test%40/aria2%40/src%40/aria2c%40"), @@ -434,6 +438,9 @@ void BittorrentHelperTest::testLoadFromMemory_multiFileNonUtf8Path() CPPUNIT_ASSERT_EQUAL (std::string("./%1B%24B%25O%25m%21%3C%1B%28B/path/%90%A2%8AE"), fe->getPath()); + CPPUNIT_ASSERT_EQUAL + (std::string("%1B%24B%25O%25m%21%3C%1B%28B/path/%90%A2%8AE"), + fe->getSuffixPath()); CPPUNIT_ASSERT_EQUAL (std::string("./%1B%24B%25O%25m%21%3C%1B%28B"), dctx->getBasePath()); } @@ -452,6 +459,7 @@ void BittorrentHelperTest::testLoadFromMemory_singleFileNonUtf8Path() const std::shared_ptr& fe = dctx->getFirstFileEntry(); CPPUNIT_ASSERT_EQUAL(std::string("./%90%A2%8AE"), fe->getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("%90%A2%8AE"), fe->getSuffixPath()); } void BittorrentHelperTest::testLoadFromMemory() diff --git a/test/Metalink2RequestGroupTest.cc b/test/Metalink2RequestGroupTest.cc index 29af9a70..6160bdc5 100644 --- a/test/Metalink2RequestGroupTest.cc +++ b/test/Metalink2RequestGroupTest.cc @@ -163,6 +163,8 @@ void Metalink2RequestGroupTest::testGenerate_groupByMetaurl() CPPUNIT_ASSERT_EQUAL((size_t)2, fileEntries.size()); CPPUNIT_ASSERT_EQUAL(std::string("./file1"), fileEntries[0]->getPath()); CPPUNIT_ASSERT_EQUAL(std::string("file1"), fileEntries[0]->getOriginalName()); + CPPUNIT_ASSERT_EQUAL(std::string("file1"), + fileEntries[0]->getSuffixPath()); CPPUNIT_ASSERT_EQUAL((size_t)1, fileEntries[0]->getRemainingUris().size()); CPPUNIT_ASSERT_EQUAL(std::string("http://file1p1"), fileEntries[0]->getRemainingUris()[0]);