瀏覽代碼

2009-11-22 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Added parseMagnetLink(). Hex encoded info hash is supported.
	Base32 encoded info hash is not supported yet.
	* src/bittorrent_helper.cc
	* src/bittorrent_helper.h
	* test/BittorrentHelperTest.cc
Tatsuhiro Tsujikawa 16 年之前
父節點
當前提交
66e6191d10
共有 4 個文件被更改,包括 91 次插入0 次删除
  1. 8 0
      ChangeLog
  2. 58 0
      src/bittorrent_helper.cc
  3. 5 0
      src/bittorrent_helper.h
  4. 20 0
      test/BittorrentHelperTest.cc

+ 8 - 0
ChangeLog

@@ -1,3 +1,11 @@
+2009-11-22  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added parseMagnetLink(). Hex encoded info hash is supported.
+	Base32 encoded info hash is not supported yet.
+	* src/bittorrent_helper.cc
+	* src/bittorrent_helper.h
+	* test/BittorrentHelperTest.cc
+
 2009-11-22  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Added FromHex()

+ 58 - 0
src/bittorrent_helper.cc

@@ -847,6 +847,64 @@ void generateRandomKey(unsigned char* key)
   MessageDigestHelper::digest(key, 20, MessageDigestContext::SHA1, bytes, sizeof(bytes));
 }
 
+void parseMagnetLink(const std::string& magnetLink,
+		     const SharedHandle<DownloadContext>& dctx)
+{
+  // magnet:?xt=urn:btih:<info-hash>&dn=<name>&tr=<tracker-url>
+  // <info-hash> comes in 2 flavors: 40bytes hexadecimal ascii info hash,
+  // or 32bytes Base32 encoded info hash.
+  if(!util::startsWith(magnetLink, "magnet:?")) {
+    throw DL_ABORT_EX("Invalid magnet link.");
+  }
+  std::deque<std::string> queries;
+  util::split(std::string(magnetLink.begin()+8, magnetLink.end()),
+	      std::back_inserter(queries), "&");
+  std::string infoHash;
+  std::string name;
+  BDE announceList = BDE::list();
+  announceList << BDE::list();
+  for(std::deque<std::string>::const_iterator i = queries.begin();
+      i != queries.end(); ++i) {
+    std::pair<std::string, std::string> kv;
+    util::split(kv, *i, '=');
+    if(kv.first == "xt") {
+      if(!util::startsWith(kv.second, "urn:btih:")) {
+	throw DL_ABORT_EX("Bad BitTorrent Magnet Link.");
+      }
+      if(infoHash.empty()) {
+	infoHash = kv.second.substr(9);
+      } else {
+	throw DL_ABORT_EX("More than 1 info hash in magnet link.");
+      }
+    } else if(kv.first == "dn") {
+      name = kv.second;
+    } else if(kv.first == "tr") {
+      announceList[0] << kv.second;
+    }
+  }
+  if(infoHash.size() == 32) {
+    // Not yet implemented
+    abort();
+  } else if(infoHash.size() == 40) {
+    std::string rawhash = util::fromHex(infoHash);
+    if(rawhash.empty()) {
+      throw DL_ABORT_EX("Invalid info hash");
+    }
+    infoHash = rawhash;
+  } else {
+    throw DL_ABORT_EX("Invalid magnet link.");
+  }
+  if(name.empty()) {
+    name = util::toHex(infoHash);
+  }
+  BDE attrs = BDE::dict();
+  attrs[INFO_HASH] = infoHash;
+  attrs[NAME] = name;
+  attrs[ANNOUNCE_LIST] = announceList;
+
+  dctx->setAttribute(BITTORRENT, attrs);
+}
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 5 - 0
src/bittorrent_helper.h

@@ -119,6 +119,11 @@ void loadFromMemory(const std::string& context,
 		    const std::string& defaultName,
 		    const std::string& overrideName = "");
 
+// Parses BitTorrent magnet link.
+// magnet:?xt=urn:btih:<info-hash>&dn=<name>&tr=<tracker-url>
+void parseMagnetLink(const std::string& magnetLink,
+		     const SharedHandle<DownloadContext>& ctx);
+
 // Generates Peer ID. BitTorrent specification says Peer ID is 20-byte
 // length.  This function uses peerIdPrefix as a Peer ID and it is
 // less than 20bytes, random bytes are generated and appened to it. If

+ 20 - 0
test/BittorrentHelperTest.cc

@@ -58,6 +58,7 @@ class BittorrentHelperTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCreatecompact);
   CPPUNIT_TEST(testCheckBitfield);
   CPPUNIT_TEST(testMetadata);
+  CPPUNIT_TEST(testParseMagnetLink);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {
@@ -97,6 +98,7 @@ public:
   void testCreatecompact();
   void testCheckBitfield();
   void testMetadata();
+  void testParseMagnetLink();
 };
 
 
@@ -695,6 +697,24 @@ void BittorrentHelperTest::testMetadata() {
 		       (size_t)attrs[bittorrent::METADATA_SIZE].i());
 }
 
+void BittorrentHelperTest::testParseMagnetLink()
+{
+  SharedHandle<DownloadContext> dctx(new DownloadContext());
+  std::string magnet =
+    "magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c&dn=aria2";
+  bittorrent::parseMagnetLink(magnet, dctx);
+  CPPUNIT_ASSERT_EQUAL(std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
+		       bittorrent::getInfoHashString(dctx));
+  BDE attrs = dctx->getAttribute(bittorrent::BITTORRENT);
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2"), attrs[bittorrent::NAME].s());
+
+  magnet = "magnet:?xt=urn:btih:248d0a1cd08284299de78d5c1ed359bb46717d8c";
+  bittorrent::parseMagnetLink(magnet, dctx);
+  attrs = dctx->getAttribute(bittorrent::BITTORRENT);
+  CPPUNIT_ASSERT_EQUAL(std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
+		       attrs[bittorrent::NAME].s());
+}
+
 } // namespace bittorrent
 
 } // namespace aria2