فهرست منبع

Support relative URI in Metalink file.

If relative URI is found in Metalink file, aria2 resolves its full URI
contatenating the URI from which Metalink file is retrieved and
relative URI in Metalink file. This feature is not available if
Metalink file in local disk is specified in command line.
Tatsuhiro Tsujikawa 14 سال پیش
والد
کامیت
ad5af56c17

+ 6 - 4
src/Metalink2RequestGroup.cc

@@ -110,10 +110,11 @@ void
 Metalink2RequestGroup::generate
 Metalink2RequestGroup::generate
 (std::vector<SharedHandle<RequestGroup> >& groups,
 (std::vector<SharedHandle<RequestGroup> >& groups,
  const std::string& metalinkFile,
  const std::string& metalinkFile,
- const SharedHandle<Option>& option)
+ const SharedHandle<Option>& option,
+ const std::string& baseUri)
 {
 {
   std::vector<SharedHandle<MetalinkEntry> > entries;
   std::vector<SharedHandle<MetalinkEntry> > entries;
-  metalink::parseAndQuery(entries, metalinkFile, option.get());
+  metalink::parseAndQuery(entries, metalinkFile, option.get(), baseUri);
   std::vector<SharedHandle<RequestGroup> > tempgroups;
   std::vector<SharedHandle<RequestGroup> > tempgroups;
   createRequestGroup(tempgroups, entries, option);
   createRequestGroup(tempgroups, entries, option);
   SharedHandle<MetadataInfo> mi;
   SharedHandle<MetadataInfo> mi;
@@ -130,10 +131,11 @@ void
 Metalink2RequestGroup::generate
 Metalink2RequestGroup::generate
 (std::vector<SharedHandle<RequestGroup> >& groups,
 (std::vector<SharedHandle<RequestGroup> >& groups,
  const SharedHandle<BinaryStream>& binaryStream,
  const SharedHandle<BinaryStream>& binaryStream,
- const SharedHandle<Option>& option)
+ const SharedHandle<Option>& option,
+ const std::string& baseUri)
 {
 {
   std::vector<SharedHandle<MetalinkEntry> > entries;
   std::vector<SharedHandle<MetalinkEntry> > entries;
-  metalink::parseAndQuery(entries, binaryStream, option.get());
+  metalink::parseAndQuery(entries, binaryStream, option.get(), baseUri);
   std::vector<SharedHandle<RequestGroup> > tempgroups;
   std::vector<SharedHandle<RequestGroup> > tempgroups;
   createRequestGroup(tempgroups, entries, option);
   createRequestGroup(tempgroups, entries, option);
   SharedHandle<MetadataInfo> mi(new MetadataInfo());
   SharedHandle<MetadataInfo> mi(new MetadataInfo());

+ 7 - 3
src/Metalink2RequestGroup.h

@@ -36,10 +36,12 @@
 #define D_METALINK_2_REQUEST_GROUP_H
 #define D_METALINK_2_REQUEST_GROUP_H
 
 
 #include "common.h"
 #include "common.h"
-#include "SharedHandle.h"
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
+#include "SharedHandle.h"
+#include "A2STR.h"
+
 namespace aria2 {
 namespace aria2 {
 
 
 class Option;
 class Option;
@@ -58,11 +60,13 @@ public:
 
 
   void generate(std::vector<SharedHandle<RequestGroup> >& groups,
   void generate(std::vector<SharedHandle<RequestGroup> >& groups,
                 const std::string& metalinkFile,
                 const std::string& metalinkFile,
-                const SharedHandle<Option>& option);
+                const SharedHandle<Option>& option,
+                const std::string& baseUri = A2STR::NIL);
 
 
   void generate(std::vector<SharedHandle<RequestGroup> >& groups,
   void generate(std::vector<SharedHandle<RequestGroup> >& groups,
                 const SharedHandle<BinaryStream>& binaryStream,
                 const SharedHandle<BinaryStream>& binaryStream,
-                const SharedHandle<Option>& option);
+                const SharedHandle<Option>& option,
+                const std::string& baseUri = A2STR::NIL);
 };
 };
 
 
 } // namespace aria2
 } // namespace aria2

+ 28 - 10
src/MetalinkParserController.cc

@@ -43,13 +43,17 @@
 #include "FileEntry.h"
 #include "FileEntry.h"
 #include "a2functional.h"
 #include "a2functional.h"
 #include "A2STR.h"
 #include "A2STR.h"
+#include "uri.h"
+#include "Signature.h"
+#include "util.h"
 #ifdef ENABLE_MESSAGE_DIGEST
 #ifdef ENABLE_MESSAGE_DIGEST
 # include "Checksum.h"
 # include "Checksum.h"
 # include "ChunkChecksum.h"
 # include "ChunkChecksum.h"
 # include "MessageDigest.h"
 # include "MessageDigest.h"
 #endif // ENABLE_MESSAGE_DIGEST
 #endif // ENABLE_MESSAGE_DIGEST
-#include "Signature.h"
-#include "util.h"
+#ifdef ENABLE_BITTORRENT
+# include "magnet.h"
+#endif // ENABLE_BITTORRENT
 
 
 namespace aria2 {
 namespace aria2 {
 
 
@@ -166,14 +170,15 @@ void MetalinkParserController::setURLOfResource(const std::string& url)
   if(!tResource_) {
   if(!tResource_) {
     return;
     return;
   }
   }
-  tResource_->url = url;
-  // Metalink4Spec
-  if(tResource_->type == MetalinkResource::TYPE_UNKNOWN) {
-    // guess from URI sheme
-    std::string::size_type pos = url.find("://");
-    if(pos != std::string::npos) {
-      setTypeOfResource(url.substr(0, pos));
+  std::string u = uri::joinUri(baseUri_, url);
+  uri::UriStruct us;
+  if(uri::parse(us, u)) {
+    tResource_->url = u;
+    if(tResource_->type == MetalinkResource::TYPE_UNKNOWN) {
+      setTypeOfResource(us.protocol);
     }
     }
+  } else {
+    tResource_->url = url;
   }
   }
 }
 }
 
 
@@ -563,7 +568,20 @@ void MetalinkParserController::setURLOfMetaurl(const std::string& url)
   if(!tMetaurl_) {
   if(!tMetaurl_) {
     return;
     return;
   }
   }
-  tMetaurl_->url = url;
+#ifdef ENABLE_BITTORRENT
+  if(magnet::parse(url)) {
+    tMetaurl_->url = url;
+  } else
+#endif // ENABLE_BITTORRENT
+    {
+      std::string u = uri::joinUri(baseUri_, url);
+      uri::UriStruct us;
+      if(uri::parse(us, u)) {
+        tMetaurl_->url = u;
+      } else {
+        tMetaurl_->url = url;
+      }
+    }
 }
 }
 
 
 void MetalinkParserController::setMediatypeOfMetaurl
 void MetalinkParserController::setMediatypeOfMetaurl

+ 6 - 0
src/MetalinkParserController.h

@@ -81,6 +81,7 @@ private:
 #endif // ENABLE_MESSAGE_DIGEST
 #endif // ENABLE_MESSAGE_DIGEST
 
 
   SharedHandle<Signature> tSignature_;
   SharedHandle<Signature> tSignature_;
+  std::string baseUri_;
 public:
 public:
   MetalinkParserController();
   MetalinkParserController();
 
 
@@ -190,6 +191,11 @@ public:
   void commitMetaurlTransaction();
   void commitMetaurlTransaction();
 
 
   void cancelMetaurlTransaction();
   void cancelMetaurlTransaction();
+
+  void setBaseUri(const std::string& baseUri)
+  {
+    baseUri_ = baseUri;
+  }
 };
 };
 
 
 } // namespace aria2
 } // namespace aria2

+ 4 - 0
src/MetalinkParserStateMachine.cc

@@ -541,5 +541,9 @@ std::string MetalinkParserStateMachine::getErrorString() const
   return error.str();
   return error.str();
 }
 }
 
 
+void MetalinkParserStateMachine::setBaseUri(const std::string& uri)
+{
+  ctrl_->setBaseUri(uri);
+}
 
 
 } // namespace aria2
 } // namespace aria2

+ 2 - 0
src/MetalinkParserStateMachine.h

@@ -266,6 +266,8 @@ public:
   {
   {
     return ctrl_->getResult();
     return ctrl_->getResult();
   }
   }
+
+  void setBaseUri(const std::string& uri);
 };
 };
 
 
 } //  namespace aria2
 } //  namespace aria2

+ 31 - 1
src/MetalinkPostDownloadHandler.cc

@@ -33,6 +33,9 @@
  */
  */
 /* copyright --> */
 /* copyright --> */
 #include "MetalinkPostDownloadHandler.h"
 #include "MetalinkPostDownloadHandler.h"
+
+#include <deque>
+
 #include "RequestGroup.h"
 #include "RequestGroup.h"
 #include "Metalink2RequestGroup.h"
 #include "Metalink2RequestGroup.h"
 #include "Logger.h"
 #include "Logger.h"
@@ -47,6 +50,7 @@
 #include "DownloadContext.h"
 #include "DownloadContext.h"
 #include "download_helper.h"
 #include "download_helper.h"
 #include "fmt.h"
 #include "fmt.h"
+#include "FileEntry.h"
 
 
 namespace aria2 {
 namespace aria2 {
 
 
@@ -63,6 +67,31 @@ MetalinkPostDownloadHandler::MetalinkPostDownloadHandler()
 
 
 MetalinkPostDownloadHandler::~MetalinkPostDownloadHandler() {}
 MetalinkPostDownloadHandler::~MetalinkPostDownloadHandler() {}
 
 
+namespace {
+const std::string& getBaseUri(RequestGroup* requestGroup)
+{
+  const SharedHandle<DownloadContext>& dctx =
+    requestGroup->getDownloadContext();
+  if(dctx->getFileEntries().empty()) {
+    return A2STR::NIL;
+  } else {
+    // TODO Check download result for each URI
+    const SharedHandle<FileEntry>& entry = dctx->getFirstFileEntry();
+    const std::deque<std::string>& spentUris = entry->getSpentUris();
+    if(spentUris.empty()) {
+      const std::deque<std::string>& remainingUris = entry->getRemainingUris();
+      if(remainingUris.empty()) {
+        return A2STR::NIL;
+      } else {
+        return remainingUris.front();
+      }
+    } else {
+      return spentUris.back();
+    }
+  }
+}
+} // namespace
+
 void MetalinkPostDownloadHandler::getNextRequestGroups
 void MetalinkPostDownloadHandler::getNextRequestGroups
 (std::vector<SharedHandle<RequestGroup> >& groups,
 (std::vector<SharedHandle<RequestGroup> >& groups,
  RequestGroup* requestGroup)
  RequestGroup* requestGroup)
@@ -74,9 +103,10 @@ void MetalinkPostDownloadHandler::getNextRequestGroups
   try {
   try {
     diskAdaptor->openExistingFile();
     diskAdaptor->openExistingFile();
     //requestOption.put(PREF_DIR, requestGroup->getDownloadContext()->getDir());
     //requestOption.put(PREF_DIR, requestGroup->getDownloadContext()->getDir());
+    const std::string& baseUri = getBaseUri(requestGroup);
     std::vector<SharedHandle<RequestGroup> > newRgs;
     std::vector<SharedHandle<RequestGroup> > newRgs;
     Metalink2RequestGroup().generate(newRgs, diskAdaptor,
     Metalink2RequestGroup().generate(newRgs, diskAdaptor,
-                                     requestGroup->getOption());
+                                     requestGroup->getOption(), baseUri);
     requestGroup->followedBy(newRgs.begin(), newRgs.end());
     requestGroup->followedBy(newRgs.begin(), newRgs.end());
     SharedHandle<MetadataInfo> mi =
     SharedHandle<MetadataInfo> mi =
       createMetadataInfoFromFirstFileEntry(requestGroup->getDownloadContext());
       createMetadataInfoFromFirstFileEntry(requestGroup->getDownloadContext());

+ 8 - 2
src/XML2SAXMetalinkProcessor.cc

@@ -185,9 +185,12 @@ MetalinkProcessor::MetalinkProcessor() {}
 MetalinkProcessor::~MetalinkProcessor() {}
 MetalinkProcessor::~MetalinkProcessor() {}
 
 
 SharedHandle<Metalinker>
 SharedHandle<Metalinker>
-MetalinkProcessor::parseFile(const std::string& filename)
+MetalinkProcessor::parseFile
+(const std::string& filename,
+ const std::string& baseUri)
 {
 {
   stm_.reset(new MetalinkParserStateMachine());
   stm_.reset(new MetalinkParserStateMachine());
+  stm_->setBaseUri(baseUri);
   SharedHandle<SessionData> sessionData(new SessionData(stm_));
   SharedHandle<SessionData> sessionData(new SessionData(stm_));
   // Old libxml2(at least 2.7.6, Ubuntu 10.04LTS) does not read stdin
   // Old libxml2(at least 2.7.6, Ubuntu 10.04LTS) does not read stdin
   // when "/dev/stdin" is passed as filename while 2.7.7 does. So we
   // when "/dev/stdin" is passed as filename while 2.7.7 does. So we
@@ -216,9 +219,12 @@ MetalinkProcessor::parseFile(const std::string& filename)
 }
 }
          
          
 SharedHandle<Metalinker>
 SharedHandle<Metalinker>
-MetalinkProcessor::parseFromBinaryStream(const SharedHandle<BinaryStream>& binaryStream)
+MetalinkProcessor::parseFromBinaryStream
+(const SharedHandle<BinaryStream>& binaryStream,
+ const std::string& baseUri)
 {
 {
   stm_.reset(new MetalinkParserStateMachine());
   stm_.reset(new MetalinkParserStateMachine());
+  stm_->setBaseUri(baseUri);
   size_t bufSize = 4096;
   size_t bufSize = 4096;
   unsigned char buf[bufSize];
   unsigned char buf[bufSize];
 
 

+ 6 - 2
src/XML2SAXMetalinkProcessor.h

@@ -43,6 +43,7 @@
 #include <libxml/xpath.h>
 #include <libxml/xpath.h>
 
 
 #include "SharedHandle.h"
 #include "SharedHandle.h"
+#include "A2STR.h"
 
 
 namespace aria2 {
 namespace aria2 {
 
 
@@ -58,10 +59,13 @@ public:
 
 
   ~MetalinkProcessor();
   ~MetalinkProcessor();
 
 
-  SharedHandle<Metalinker> parseFile(const std::string& filename);
+  SharedHandle<Metalinker> parseFile
+  (const std::string& filename,
+   const std::string& baseUri = A2STR::NIL);
 
 
   SharedHandle<Metalinker> parseFromBinaryStream
   SharedHandle<Metalinker> parseFromBinaryStream
-  (const SharedHandle<BinaryStream>& binaryStream);
+  (const SharedHandle<BinaryStream>& binaryStream,
+   const std::string& baseUri = A2STR::NIL);
 };
 };
 
 
 } // namespace aria2
 } // namespace aria2

+ 7 - 4
src/metalink_helper.cc

@@ -65,20 +65,23 @@ void query
 void parseAndQuery
 void parseAndQuery
 (std::vector<SharedHandle<MetalinkEntry> >& result,
 (std::vector<SharedHandle<MetalinkEntry> >& result,
  const std::string& filename,
  const std::string& filename,
- const Option* option)
+ const Option* option,
+ const std::string& baseUri)
 {
 {
   MetalinkProcessor proc;
   MetalinkProcessor proc;
-  SharedHandle<Metalinker> metalinker = proc.parseFile(filename);
+  SharedHandle<Metalinker> metalinker = proc.parseFile(filename, baseUri);
   query(result, metalinker, option);
   query(result, metalinker, option);
 }
 }
 
 
 void parseAndQuery
 void parseAndQuery
 (std::vector<SharedHandle<MetalinkEntry> >& result,
 (std::vector<SharedHandle<MetalinkEntry> >& result,
  const SharedHandle<BinaryStream>& binaryStream,
  const SharedHandle<BinaryStream>& binaryStream,
- const Option* option)
+ const Option* option,
+ const std::string& baseUri)
 {
 {
   MetalinkProcessor proc;
   MetalinkProcessor proc;
-  SharedHandle<Metalinker> metalinker =proc.parseFromBinaryStream(binaryStream);
+  SharedHandle<Metalinker> metalinker =
+    proc.parseFromBinaryStream(binaryStream, baseUri);
   query(result, metalinker, option);
   query(result, metalinker, option);
 }
 }
 
 

+ 5 - 2
src/metalink_helper.h

@@ -41,6 +41,7 @@
 #include <vector>
 #include <vector>
 
 
 #include "SharedHandle.h"
 #include "SharedHandle.h"
+#include "A2STR.h"
 
 
 namespace aria2 {
 namespace aria2 {
 
 
@@ -54,12 +55,14 @@ namespace metalink {
 void parseAndQuery
 void parseAndQuery
 (std::vector<SharedHandle<MetalinkEntry> >& result,
 (std::vector<SharedHandle<MetalinkEntry> >& result,
  const std::string& filename,
  const std::string& filename,
- const Option* option);
+ const Option* option,
+ const std::string& baseUri = A2STR::NIL);
 
 
 void parseAndQuery
 void parseAndQuery
 (std::vector<SharedHandle<MetalinkEntry> >& result,
 (std::vector<SharedHandle<MetalinkEntry> >& result,
  const SharedHandle<BinaryStream>& binaryStream,
  const SharedHandle<BinaryStream>& binaryStream,
- const Option* option);
+ const Option* option,
+ const std::string& baseUri = A2STR::NIL);
 
 
 void groupEntryByMetaurlName
 void groupEntryByMetaurlName
 (std::vector<
 (std::vector<

+ 42 - 0
test/MetalinkParserControllerTest.cc

@@ -20,6 +20,7 @@ class MetalinkParserControllerTest:public CppUnit::TestFixture {
   CPPUNIT_TEST_SUITE(MetalinkParserControllerTest);
   CPPUNIT_TEST_SUITE(MetalinkParserControllerTest);
   CPPUNIT_TEST(testEntryTransaction);
   CPPUNIT_TEST(testEntryTransaction);
   CPPUNIT_TEST(testResourceTransaction);
   CPPUNIT_TEST(testResourceTransaction);
+  CPPUNIT_TEST(testResourceTransaction_withBaseUri);
   CPPUNIT_TEST(testMetaurlTransaction);
   CPPUNIT_TEST(testMetaurlTransaction);
 #ifdef ENABLE_MESSAGE_DIGEST
 #ifdef ENABLE_MESSAGE_DIGEST
   CPPUNIT_TEST(testChecksumTransaction);
   CPPUNIT_TEST(testChecksumTransaction);
@@ -38,6 +39,7 @@ public:
 
 
   void testEntryTransaction();
   void testEntryTransaction();
   void testResourceTransaction();
   void testResourceTransaction();
+  void testResourceTransaction_withBaseUri();
   void testMetaurlTransaction();
   void testMetaurlTransaction();
 #ifdef ENABLE_MESSAGE_DIGEST
 #ifdef ENABLE_MESSAGE_DIGEST
   void testChecksumTransaction();
   void testChecksumTransaction();
@@ -110,6 +112,46 @@ void MetalinkParserControllerTest::testResourceTransaction()
   }
   }
 }
 }
 
 
+void MetalinkParserControllerTest::testResourceTransaction_withBaseUri()
+{
+  MetalinkParserController ctrl;
+  ctrl.setBaseUri("http://base/dir/file");
+  ctrl.newEntryTransaction();
+  ctrl.newResourceTransaction();
+  ctrl.setURLOfResource("aria2.tar.bz2");
+  ctrl.commitResourceTransaction();
+#ifdef ENABLE_BITTORRENT
+  ctrl.newMetaurlTransaction();
+  ctrl.setURLOfMetaurl("/meta/aria2.tar.bz2.torrent");
+  ctrl.setMediatypeOfMetaurl("torrent");
+  ctrl.commitMetaurlTransaction();
+  ctrl.newMetaurlTransaction();
+  ctrl.setURLOfMetaurl("magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c");
+  ctrl.setMediatypeOfMetaurl("torrent");
+  ctrl.commitMetaurlTransaction();
+#endif // ENABLE_BITTORRENT
+  ctrl.commitEntryTransaction();
+  {
+    SharedHandle<Metalinker> m = ctrl.getResult();
+    CPPUNIT_ASSERT_EQUAL((size_t)1, m->getEntries()[0]->resources.size());
+    SharedHandle<MetalinkResource> res = m->getEntries()[0]->resources[0];
+    CPPUNIT_ASSERT_EQUAL(std::string("http://base/dir/aria2.tar.bz2"),
+                         res->url);
+    CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_HTTP, res->type);
+
+#ifdef ENABLE_BITTORRENT
+    CPPUNIT_ASSERT_EQUAL((size_t)2, m->getEntries()[0]->metaurls.size());
+    SharedHandle<MetalinkMetaurl> metaurl = m->getEntries()[0]->metaurls[0];
+    CPPUNIT_ASSERT_EQUAL(std::string("http://base/meta/aria2.tar.bz2.torrent"),
+                         metaurl->url);
+
+    metaurl = m->getEntries()[0]->metaurls[1];
+    CPPUNIT_ASSERT_EQUAL(std::string("magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c"),
+                         metaurl->url);
+#endif // ENABLE_BITTORRENT
+  }
+}
+
 void MetalinkParserControllerTest::testMetaurlTransaction()
 void MetalinkParserControllerTest::testMetaurlTransaction()
 {
 {
   MetalinkParserController ctrl;
   MetalinkParserController ctrl;

+ 21 - 0
test/MetalinkPostDownloadHandlerTest.cc

@@ -17,6 +17,7 @@ class MetalinkPostDownloadHandlerTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCanHandle_extension);
   CPPUNIT_TEST(testCanHandle_extension);
   CPPUNIT_TEST(testCanHandle_contentType);
   CPPUNIT_TEST(testCanHandle_contentType);
   CPPUNIT_TEST(testGetNextRequestGroups);
   CPPUNIT_TEST(testGetNextRequestGroups);
+  CPPUNIT_TEST(testGetNextRequestGroups_withBaseUri);
   CPPUNIT_TEST_SUITE_END();
   CPPUNIT_TEST_SUITE_END();
 private:
 private:
   SharedHandle<Option> option_;
   SharedHandle<Option> option_;
@@ -29,6 +30,7 @@ public:
   void testCanHandle_extension();
   void testCanHandle_extension();
   void testCanHandle_contentType();
   void testCanHandle_contentType();
   void testGetNextRequestGroups();
   void testGetNextRequestGroups();
+  void testGetNextRequestGroups_withBaseUri();
 };
 };
 
 
 
 
@@ -83,4 +85,23 @@ void MetalinkPostDownloadHandlerTest::testGetNextRequestGroups()
 #endif // ENABLE_BITTORRENT
 #endif // ENABLE_BITTORRENT
 }
 }
 
 
+void MetalinkPostDownloadHandlerTest::testGetNextRequestGroups_withBaseUri()
+{
+  SharedHandle<DownloadContext> dctx
+    (new DownloadContext(1024, 0, A2_TEST_DIR"/base_uri.xml"));
+  dctx->getFirstFileEntry()->addUri("http://base/dir/base_uri.xml");
+  RequestGroup rg(option_);
+  rg.setDownloadContext(dctx);
+  rg.initPieceStorage();
+  rg.getPieceStorage()->getDiskAdaptor()->enableReadOnly();
+
+  MetalinkPostDownloadHandler handler;
+  std::vector<SharedHandle<RequestGroup> > groups;
+  handler.getNextRequestGroups(groups, &rg);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, groups.size());
+  CPPUNIT_ASSERT_EQUAL(std::string("http://base/dir/example.ext"),
+                       groups[0]->getDownloadContext()->
+                       getFirstFileEntry()->getRemainingUris()[0]);
+}
+
 } // namespace aria2
 } // namespace aria2

+ 6 - 0
test/base_uri.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink xmlns="urn:ietf:params:xml:ns:metalink">
+  <file name="example.ext">
+    <url>example.ext</url>
+  </file>
+</metalink>