From 66e6191d10ef988089cb292b71e7da00d13e8ccd Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 22 Nov 2009 14:35:35 +0000 Subject: [PATCH] 2009-11-22 Tatsuhiro Tsujikawa 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 --- ChangeLog | 8 +++++ src/bittorrent_helper.cc | 58 ++++++++++++++++++++++++++++++++++++ src/bittorrent_helper.h | 5 ++++ test/BittorrentHelperTest.cc | 20 +++++++++++++ 4 files changed, 91 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1fc92ec3..977f8a7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-11-22 Tatsuhiro Tsujikawa + + 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 Added FromHex() diff --git a/src/bittorrent_helper.cc b/src/bittorrent_helper.cc index 0fa05cc4..a47ed507 100644 --- a/src/bittorrent_helper.cc +++ b/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& dctx) +{ + // magnet:?xt=urn:btih:&dn=&tr= + // 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 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::const_iterator i = queries.begin(); + i != queries.end(); ++i) { + std::pair 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 diff --git a/src/bittorrent_helper.h b/src/bittorrent_helper.h index bf1269a3..5e7a1fde 100644 --- a/src/bittorrent_helper.h +++ b/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:&dn=&tr= +void parseMagnetLink(const std::string& magnetLink, + const SharedHandle& 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 diff --git a/test/BittorrentHelperTest.cc b/test/BittorrentHelperTest.cc index 756ed2de..9c3d1d76 100644 --- a/test/BittorrentHelperTest.cc +++ b/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 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