Просмотр исходного кода

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

	Added Base32 encoded BitTorrent Magnet Link support.
	* src/Makefile.am
	* src/base32.cc
	* src/base32.h
	* src/bittorrent_helper.cc
	* test/Base32Test.cc
	* test/BittorrentHelperTest.cc
	* test/Makefile.am
Tatsuhiro Tsujikawa 16 лет назад
Родитель
Сommit
894165171c
10 измененных файлов с 298 добавлено и 29 удалено
  1. 11 0
      ChangeLog
  2. 1 0
      src/Makefile.am
  3. 25 21
      src/Makefile.in
  4. 134 0
      src/base32.cc
  5. 54 0
      src/base32.h
  6. 6 2
      src/bittorrent_helper.cc
  7. 45 0
      test/Base32Test.cc
  8. 14 0
      test/BittorrentHelperTest.cc
  9. 1 0
      test/Makefile.am
  10. 7 6
      test/Makefile.in

+ 11 - 0
ChangeLog

@@ -1,3 +1,14 @@
+2009-11-24  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added Base32 encoded BitTorrent Magnet Link support.
+	* src/Makefile.am
+	* src/base32.cc
+	* src/base32.h
+	* src/bittorrent_helper.cc
+	* test/Base32Test.cc
+	* test/BittorrentHelperTest.cc
+	* test/Makefile.am
+
 2009-11-23  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Added BitTorrent Magnet Link support. Base32 encoded link is not

+ 1 - 0
src/Makefile.am

@@ -50,6 +50,7 @@ SRCS =  Socket.h\
 	File.cc File.h\
 	Option.cc Option.h\
 	Base64.cc Base64.h\
+	base32.cc base32.h\
 	LogFactory.cc LogFactory.h\
 	TimeA2.cc TimeA2.h\
 	SharedHandle.h\

+ 25 - 21
src/Makefile.in

@@ -328,14 +328,15 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	AbstractDiskWriter.cc AbstractDiskWriter.h \
 	DefaultDiskWriter.cc DefaultDiskWriter.h \
 	DefaultDiskWriterFactory.cc DefaultDiskWriterFactory.h File.cc \
-	File.h Option.cc Option.h Base64.cc Base64.h LogFactory.cc \
-	LogFactory.h TimeA2.cc TimeA2.h SharedHandle.h \
-	HandleRegistry.h FeatureConfig.cc FeatureConfig.h \
-	DownloadEngineFactory.cc DownloadEngineFactory.h SpeedCalc.cc \
-	SpeedCalc.h PeerStat.h BitfieldMan.cc BitfieldMan.h \
-	BitfieldManFactory.cc BitfieldManFactory.h Randomizer.h \
-	SimpleRandomizer.cc SimpleRandomizer.h HttpResponse.cc \
-	HttpResponse.h HttpRequest.cc HttpRequest.h Range.h \
+	File.h Option.cc Option.h Base64.cc Base64.h base32.cc \
+	base32.h LogFactory.cc LogFactory.h TimeA2.cc TimeA2.h \
+	SharedHandle.h HandleRegistry.h FeatureConfig.cc \
+	FeatureConfig.h DownloadEngineFactory.cc \
+	DownloadEngineFactory.h SpeedCalc.cc SpeedCalc.h PeerStat.h \
+	BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
+	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
+	SimpleRandomizer.h HttpResponse.cc HttpResponse.h \
+	HttpRequest.cc HttpRequest.h Range.h \
 	AbstractProxyRequestCommand.cc AbstractProxyRequestCommand.h \
 	AbstractProxyResponseCommand.cc AbstractProxyResponseCommand.h \
 	Netrc.cc Netrc.h AuthConfig.cc AuthConfig.h AuthResolver.h \
@@ -769,11 +770,12 @@ am__objects_27 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	SimpleLogger.$(OBJEXT) AbstractDiskWriter.$(OBJEXT) \
 	DefaultDiskWriter.$(OBJEXT) DefaultDiskWriterFactory.$(OBJEXT) \
 	File.$(OBJEXT) Option.$(OBJEXT) Base64.$(OBJEXT) \
-	LogFactory.$(OBJEXT) TimeA2.$(OBJEXT) FeatureConfig.$(OBJEXT) \
-	DownloadEngineFactory.$(OBJEXT) SpeedCalc.$(OBJEXT) \
-	BitfieldMan.$(OBJEXT) BitfieldManFactory.$(OBJEXT) \
-	SimpleRandomizer.$(OBJEXT) HttpResponse.$(OBJEXT) \
-	HttpRequest.$(OBJEXT) AbstractProxyRequestCommand.$(OBJEXT) \
+	base32.$(OBJEXT) LogFactory.$(OBJEXT) TimeA2.$(OBJEXT) \
+	FeatureConfig.$(OBJEXT) DownloadEngineFactory.$(OBJEXT) \
+	SpeedCalc.$(OBJEXT) BitfieldMan.$(OBJEXT) \
+	BitfieldManFactory.$(OBJEXT) SimpleRandomizer.$(OBJEXT) \
+	HttpResponse.$(OBJEXT) HttpRequest.$(OBJEXT) \
+	AbstractProxyRequestCommand.$(OBJEXT) \
 	AbstractProxyResponseCommand.$(OBJEXT) Netrc.$(OBJEXT) \
 	AuthConfig.$(OBJEXT) AbstractAuthResolver.$(OBJEXT) \
 	DefaultAuthResolver.$(OBJEXT) NetrcAuthResolver.$(OBJEXT) \
@@ -1071,14 +1073,15 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \
 	AbstractDiskWriter.cc AbstractDiskWriter.h \
 	DefaultDiskWriter.cc DefaultDiskWriter.h \
 	DefaultDiskWriterFactory.cc DefaultDiskWriterFactory.h File.cc \
-	File.h Option.cc Option.h Base64.cc Base64.h LogFactory.cc \
-	LogFactory.h TimeA2.cc TimeA2.h SharedHandle.h \
-	HandleRegistry.h FeatureConfig.cc FeatureConfig.h \
-	DownloadEngineFactory.cc DownloadEngineFactory.h SpeedCalc.cc \
-	SpeedCalc.h PeerStat.h BitfieldMan.cc BitfieldMan.h \
-	BitfieldManFactory.cc BitfieldManFactory.h Randomizer.h \
-	SimpleRandomizer.cc SimpleRandomizer.h HttpResponse.cc \
-	HttpResponse.h HttpRequest.cc HttpRequest.h Range.h \
+	File.h Option.cc Option.h Base64.cc Base64.h base32.cc \
+	base32.h LogFactory.cc LogFactory.h TimeA2.cc TimeA2.h \
+	SharedHandle.h HandleRegistry.h FeatureConfig.cc \
+	FeatureConfig.h DownloadEngineFactory.cc \
+	DownloadEngineFactory.h SpeedCalc.cc SpeedCalc.h PeerStat.h \
+	BitfieldMan.cc BitfieldMan.h BitfieldManFactory.cc \
+	BitfieldManFactory.h Randomizer.h SimpleRandomizer.cc \
+	SimpleRandomizer.h HttpResponse.cc HttpResponse.h \
+	HttpRequest.cc HttpRequest.h Range.h \
 	AbstractProxyRequestCommand.cc AbstractProxyRequestCommand.h \
 	AbstractProxyResponseCommand.cc AbstractProxyResponseCommand.h \
 	Netrc.cc Netrc.h AuthConfig.cc AuthConfig.h AuthResolver.h \
@@ -1558,6 +1561,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcResponse.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ZeroBtMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asctime_r.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bencode.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitfield.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bittorrent_helper.Po@am__quote@

+ 134 - 0
src/base32.cc

@@ -0,0 +1,134 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2009 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "base32.h"
+#include "util.h"
+
+namespace aria2 {
+
+namespace base32 {
+
+static const char B32TABLE[] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+  'Y', 'Z', '2', '3', '4', '5', '6', '7'
+};
+
+std::string encode(const std::string& src)
+{
+  std::string ret;
+  size_t count = 0;
+  uint64_t buf = 0;
+  for(size_t i = 0; i < src.size(); ++i) {
+    buf <<= 8;
+    buf += src[i]&0xff;
+    ++count;
+    if(count == 5) {
+      char temp[8];
+      for(size_t j = 0; j < 8; ++j) {
+	temp[7-j] = B32TABLE[buf&0x1f];
+	buf >>= 5;
+      }
+      ret += std::string(&temp[0], &temp[8]);
+      count = 0;
+      buf = 0;      
+    }
+  }
+  size_t r = 0;
+  if(count == 1) {
+    buf <<= 2;
+    r = 2;
+  } else if(count == 2) {
+    buf <<= 4;
+    r = 4;
+  } else if(count == 3) {
+    buf <<= 1;
+    r = 5;
+  } else if(count == 4) {
+    buf <<= 3;
+    r = 7;
+  }
+  char temp[7];
+  for(size_t j = 0; j < r; ++j) {
+    temp[r-1-j] = B32TABLE[buf&0x1f];
+    buf >>= 5;
+  }
+  ret += std::string(&temp[0], &temp[r]);
+  if(r) {
+    ret += std::string(8-r, '=');
+  }
+  return ret;
+}
+
+std::string decode(const std::string& src)
+{
+  std::string ret;
+  if(src.size()%8) {
+    return ret;
+  }
+  bool done = false;
+  for(size_t i = 0; i < src.size() && !done; i += 8) {
+    uint64_t buf = 0;
+    size_t bits = 0;
+    for(size_t j = 0; j < 8; ++j) {
+      char ch = src[i+j];
+      unsigned char value;
+      if('A' <= ch && ch <= 'Z') {
+	value = ch-'A';
+      } else if('2' <= ch && ch <= '7') {
+	value = ch-'2'+26;
+      } else if(ch == '=') {
+	done = true;
+	break;
+      } else {
+	ret.clear();
+	return ret;
+      }
+      buf <<= 5;
+      buf += value;
+      bits += 5;
+    }
+    buf >>= (bits%8);
+    bits = bits/8*8;
+    buf = ntoh64(buf);
+    char* p = reinterpret_cast<char*>(&buf);
+    ret += std::string(&p[(64-bits)/8], &p[8]);
+  }
+  return ret;
+}
+
+} // namespace base32
+
+} // namespace aria2

+ 54 - 0
src/base32.h

@@ -0,0 +1,54 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2009 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BASE32_H_
+#define _D_BASE32_H_
+
+#include "common.h"
+
+#include <string>
+
+namespace aria2 {
+
+namespace base32 {
+
+std::string encode(const std::string& src);
+
+std::string decode(const std::string& src);
+
+} // namespace base32
+
+} // namespace aria2
+
+#endif // _D_BASE32_H_

+ 6 - 2
src/bittorrent_helper.cc

@@ -52,6 +52,7 @@
 #include "a2netcompat.h"
 #include "BtConstants.h"
 #include "bitfield.h"
+#include "base32.h"
 
 namespace aria2 {
 
@@ -883,8 +884,11 @@ void parseMagnetLink(const std::string& magnetLink,
     }
   }
   if(infoHash.size() == 32) {
-    // Not yet implemented
-    abort();
+    std::string rawhash = base32::decode(infoHash);
+    if(rawhash.size() != 20) {
+      throw DL_ABORT_EX("Invalid info hash");
+    }
+    infoHash = rawhash;
   } else if(infoHash.size() == 40) {
     std::string rawhash = util::fromHex(infoHash);
     if(rawhash.empty()) {

+ 45 - 0
test/Base32Test.cc

@@ -0,0 +1,45 @@
+#include "base32.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "util.h"
+
+namespace aria2 {
+
+class Base32Test:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(Base32Test);
+  CPPUNIT_TEST(testEncode);
+  CPPUNIT_TEST(testDecode);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testEncode();
+  void testDecode();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( Base32Test );
+
+void Base32Test::testEncode()
+{
+  CPPUNIT_ASSERT_EQUAL(std::string(), base32::encode(""));
+  CPPUNIT_ASSERT_EQUAL(std::string("GE======"), base32::encode("1"));
+  CPPUNIT_ASSERT_EQUAL(std::string("GEZA===="), base32::encode("12"));
+  CPPUNIT_ASSERT_EQUAL(std::string("GEZDG==="), base32::encode("123"));
+  CPPUNIT_ASSERT_EQUAL(std::string("GEZDGNA="), base32::encode("1234"));
+  CPPUNIT_ASSERT_EQUAL(std::string("GEZDGNBV"), base32::encode("12345"));
+  CPPUNIT_ASSERT_EQUAL(std::string("ESGQUHGQQKCA===="),
+		       base32::encode(util::fromHex("248d0a1cd08284")));
+}
+
+void Base32Test::testDecode()
+{
+  CPPUNIT_ASSERT_EQUAL(std::string(), base32::decode(""));
+  CPPUNIT_ASSERT_EQUAL(std::string("1"), base32::decode("GE======"));
+  CPPUNIT_ASSERT_EQUAL(std::string("12"), base32::decode("GEZA===="));
+  CPPUNIT_ASSERT_EQUAL(std::string("123"), base32::decode("GEZDG==="));
+  CPPUNIT_ASSERT_EQUAL(std::string("1234"), base32::decode("GEZDGNA="));
+  CPPUNIT_ASSERT_EQUAL(std::string("12345"), base32::decode("GEZDGNBV"));
+}
+
+} // namespace aria2

+ 14 - 0
test/BittorrentHelperTest.cc

@@ -16,6 +16,7 @@
 #include "a2netcompat.h"
 #include "bencode.h"
 #include "TestUtil.h"
+#include "base32.h"
 
 namespace aria2 {
 
@@ -59,6 +60,7 @@ class BittorrentHelperTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCheckBitfield);
   CPPUNIT_TEST(testMetadata);
   CPPUNIT_TEST(testParseMagnetLink);
+  CPPUNIT_TEST(testParseMagnetLink_base32);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {
@@ -99,6 +101,7 @@ public:
   void testCheckBitfield();
   void testMetadata();
   void testParseMagnetLink();
+  void testParseMagnetLink_base32();
 };
 
 
@@ -715,6 +718,17 @@ void BittorrentHelperTest::testParseMagnetLink()
 		       attrs[bittorrent::NAME].s());
 }
 
+void BittorrentHelperTest::testParseMagnetLink_base32()
+{
+  std::string infoHash = "248d0a1cd08284299de78d5c1ed359bb46717d8c";
+  std::string base32InfoHash = base32::encode(util::fromHex(infoHash));
+  SharedHandle<DownloadContext> dctx(new DownloadContext());
+  std::string magnet = "magnet:?xt=urn:btih:"+base32InfoHash+"&dn=aria2";
+  bittorrent::parseMagnetLink(magnet, dctx);
+  CPPUNIT_ASSERT_EQUAL(std::string("248d0a1cd08284299de78d5c1ed359bb46717d8c"),
+		       bittorrent::getInfoHashString(dctx));
+}
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 1 - 0
test/Makefile.am

@@ -5,6 +5,7 @@ aria2c_SOURCES = AllTest.cc\
 	SocketCoreTest.cc\
 	array_funTest.cc\
 	Base64Test.cc\
+	Base32Test.cc\
 	SequenceTest.cc\
 	a2functionalTest.cc\
 	FileEntryTest.cc\

+ 7 - 6
test/Makefile.in

@@ -177,7 +177,7 @@ CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__EXEEXT_1 = aria2c$(EXEEXT)
 am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
-	SocketCoreTest.cc array_funTest.cc Base64Test.cc \
+	SocketCoreTest.cc array_funTest.cc Base64Test.cc Base32Test.cc \
 	SequenceTest.cc a2functionalTest.cc FileEntryTest.cc \
 	PieceTest.cc SegmentTest.cc GrowSegmentTest.cc \
 	SingleFileAllocationIteratorTest.cc \
@@ -355,10 +355,10 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 @ENABLE_METALINK_TRUE@	MetalinkProcessorTest.$(OBJEXT)
 am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
 	SocketCoreTest.$(OBJEXT) array_funTest.$(OBJEXT) \
-	Base64Test.$(OBJEXT) SequenceTest.$(OBJEXT) \
-	a2functionalTest.$(OBJEXT) FileEntryTest.$(OBJEXT) \
-	PieceTest.$(OBJEXT) SegmentTest.$(OBJEXT) \
-	GrowSegmentTest.$(OBJEXT) \
+	Base64Test.$(OBJEXT) Base32Test.$(OBJEXT) \
+	SequenceTest.$(OBJEXT) a2functionalTest.$(OBJEXT) \
+	FileEntryTest.$(OBJEXT) PieceTest.$(OBJEXT) \
+	SegmentTest.$(OBJEXT) GrowSegmentTest.$(OBJEXT) \
 	SingleFileAllocationIteratorTest.$(OBJEXT) \
 	DefaultBtProgressInfoFileTest.$(OBJEXT) \
 	RequestGroupTest.$(OBJEXT) PStringBuildVisitorTest.$(OBJEXT) \
@@ -596,7 +596,7 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
-	array_funTest.cc Base64Test.cc SequenceTest.cc \
+	array_funTest.cc Base64Test.cc Base32Test.cc SequenceTest.cc \
 	a2functionalTest.cc FileEntryTest.cc PieceTest.cc \
 	SegmentTest.cc GrowSegmentTest.cc \
 	SingleFileAllocationIteratorTest.cc \
@@ -726,6 +726,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthConfigFactoryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BDETest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BNodeTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base32Test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base64Test.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BencodeTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldManTest.Po@am__quote@