diff --git a/ChangeLog b/ChangeLog index bccbb6d6..b49424f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2010-01-11 Tatsuhiro Tsujikawa + + Replaced '/' and '_' with '_' in HTTP/FTP filename. + * src/A2STR.cc + * src/A2STR.h + * src/FtpNegotiationCommand.cc + * src/HttpResponseCommand.cc + * src/util.cc + * src/util.h + * test/UtilTest.cc + 2010-01-11 Tatsuhiro Tsujikawa Treat --dir="" as --dir="." diff --git a/src/A2STR.cc b/src/A2STR.cc index 61ee926a..dbe3c238 100644 --- a/src/A2STR.cc +++ b/src/A2STR.cc @@ -56,4 +56,8 @@ const std::string A2STR::SEMICOLON_C(";"); const std::string A2STR::EQUAL_C("="); +const std::string A2STR::UNDERSCORE_C("_"); + +const std::string A2STR::BACK_SLASH_C("\\"); + } // namespace aria2 diff --git a/src/A2STR.h b/src/A2STR.h index 66c1b398..ebd00584 100644 --- a/src/A2STR.h +++ b/src/A2STR.h @@ -62,6 +62,10 @@ public: static const std::string SEMICOLON_C; static const std::string EQUAL_C; + + static const std::string UNDERSCORE_C; + + static const std::string BACK_SLASH_C; }; } // namespace aria2 diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc index c1a5e9df..ec78fa75 100644 --- a/src/FtpNegotiationCommand.cc +++ b/src/FtpNegotiationCommand.cc @@ -337,7 +337,8 @@ bool FtpNegotiationCommand::onFileSizeDetermined(uint64_t totalLength) if(_fileEntry->getPath().empty()) { _fileEntry->setPath (util::applyDir - (getDownloadContext()->getDir(), util::urldecode(req->getFile()))); + (getDownloadContext()->getDir(), + util::fixTaintedBasename(util::urldecode(req->getFile())))); } _requestGroup->preDownloadProcessing(); if(e->_requestGroupMan->isSameFileBeingDownloaded(_requestGroup)) { diff --git a/src/HttpResponseCommand.cc b/src/HttpResponseCommand.cc index 2af07bce..207c86e7 100644 --- a/src/HttpResponseCommand.cc +++ b/src/HttpResponseCommand.cc @@ -132,7 +132,8 @@ bool HttpResponseCommand::executeInternal() if(_fileEntry->getPath().empty()) { _fileEntry->setPath (util::applyDir - (getDownloadContext()->getDir(), httpResponse->determinFilename())); + (getDownloadContext()->getDir(), + util::fixTaintedBasename(httpResponse->determinFilename()))); } _fileEntry->setContentType(httpResponse->getContentType()); _requestGroup->preDownloadProcessing(); diff --git a/src/util.cc b/src/util.cc index 515fb6a8..bd0ebc52 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1011,6 +1011,12 @@ std::string applyDir(const std::string& dir, const std::string& relPath) } } +std::string fixTaintedBasename(const std::string& src) +{ + return replace(replace(src, A2STR::SLASH_C, A2STR::UNDERSCORE_C), + A2STR::BACK_SLASH_C, A2STR::UNDERSCORE_C); +} + } // namespace util } // namespace aria2 diff --git a/src/util.h b/src/util.h index 9ac4aea1..83fa0943 100644 --- a/src/util.h +++ b/src/util.h @@ -362,6 +362,15 @@ bool saveAs // dir = "/", relPath = "foo" => "/foo" std::string applyDir(const std::string& dir, const std::string& relPath); +// In HTTP/FTP, file name is file component in URI. In HTTP, filename +// may be a value of Content-Disposition header. They are likely +// percent encoded. If they contains, for example, %2F, when decoded, +// basename contains dir component. This should be avoided. This +// function is created to fix these issues. This function expects src +// should be non-percent-encoded basename. Currently, this function +// replaces '/' and '\' with '_'. +std::string fixTaintedBasename(const std::string& src); + } // namespace util } // namespace aria2 diff --git a/test/UtilTest.cc b/test/UtilTest.cc index 47d13598..3916fb4c 100644 --- a/test/UtilTest.cc +++ b/test/UtilTest.cc @@ -59,6 +59,7 @@ class UtilTest:public CppUnit::TestFixture { CPPUNIT_TEST(testFromHex); CPPUNIT_TEST(testParsePrioritizePieceRange); CPPUNIT_TEST(testApplyDir); + CPPUNIT_TEST(testFixTaintedBasename); CPPUNIT_TEST_SUITE_END(); private: @@ -106,6 +107,7 @@ public: void testFromHex(); void testParsePrioritizePieceRange(); void testApplyDir(); + void testFixTaintedBasename(); }; @@ -910,4 +912,11 @@ void UtilTest::testApplyDir() CPPUNIT_ASSERT_EQUAL(std::string("/dl/pred"), util::applyDir("/dl", "pred")); } +void UtilTest::testFixTaintedBasename() +{ + CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a/b")); + CPPUNIT_ASSERT_EQUAL(std::string("a_b"), util::fixTaintedBasename("a\\b")); + CPPUNIT_ASSERT_EQUAL(std::string("a__b"), util::fixTaintedBasename("a\\/b")); +} + } // namespace aria2