Przeglądaj źródła

2010-02-25 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Added Metalink4 support. Currently, name attribute of
	metalink::metaurl is ignored and multi-file torrent cannot be used
	with Metalink4.
	* doc/aria2c.1.txt
	* src/DownloadHandlerConstants.cc
	* src/ExpatMetalinkProcessor.cc
	* src/Makefile.am
	* src/Metalink2RequestGroup.cc
	* src/MetalinkEntry.cc
	* src/MetalinkEntry.h
	* src/MetalinkParserController.cc
	* src/MetalinkParserController.h
	* src/MetalinkParserState.h
	* src/MetalinkParserStateImpl.cc
	* src/MetalinkParserStateImpl.h
	* src/MetalinkParserStateMachine.cc
	* src/MetalinkParserStateMachine.h
	* src/MetalinkParserStateV3Impl.cc
	* src/MetalinkParserStateV3Impl.h
	* src/MetalinkParserStateV4Impl.cc
	* src/MetalinkParserStateV4Impl.h
	* src/MetalinkResource.cc
	* src/MetalinkResource.h
	* src/Metalinker.cc
	* src/RequestGroup.cc
	* src/RequestGroup.h
	* src/XML2SAXMetalinkProcessor.cc
	* src/messageDigest.cc
	* src/util.cc
	* src/util.h
	* test/Makefile.am
	* test/MetalinkEntryTest.cc
	* test/MetalinkParserControllerTest.cc
	* test/MetalinkProcessorTest.cc
	* test/MetalinkerTest.cc
	* test/UtilTest.cc
	* test/metalink4-attrs.xml
	* test/metalink4-dirtraversal.xml
	* test/metalink4.xml
Tatsuhiro Tsujikawa 15 lat temu
rodzic
commit
3880a5f71b

+ 42 - 0
ChangeLog

@@ -1,3 +1,45 @@
+2010-02-25  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added Metalink4 support. Currently, name attribute of
+	metalink::metaurl is ignored and multi-file torrent cannot be used
+	with Metalink4.
+	* doc/aria2c.1.txt
+	* src/DownloadHandlerConstants.cc
+	* src/ExpatMetalinkProcessor.cc
+	* src/Makefile.am
+	* src/Metalink2RequestGroup.cc
+	* src/MetalinkEntry.cc
+	* src/MetalinkEntry.h
+	* src/MetalinkParserController.cc
+	* src/MetalinkParserController.h
+	* src/MetalinkParserState.h
+	* src/MetalinkParserStateImpl.cc
+	* src/MetalinkParserStateImpl.h
+	* src/MetalinkParserStateMachine.cc
+	* src/MetalinkParserStateMachine.h
+	* src/MetalinkParserStateV3Impl.cc
+	* src/MetalinkParserStateV3Impl.h
+	* src/MetalinkParserStateV4Impl.cc
+	* src/MetalinkParserStateV4Impl.h
+	* src/MetalinkResource.cc
+	* src/MetalinkResource.h
+	* src/Metalinker.cc
+	* src/RequestGroup.cc
+	* src/RequestGroup.h
+	* src/XML2SAXMetalinkProcessor.cc
+	* src/messageDigest.cc
+	* src/util.cc
+	* src/util.h
+	* test/Makefile.am
+	* test/MetalinkEntryTest.cc
+	* test/MetalinkParserControllerTest.cc
+	* test/MetalinkProcessorTest.cc
+	* test/MetalinkerTest.cc
+	* test/UtilTest.cc
+	* test/metalink4-attrs.xml
+	* test/metalink4-dirtraversal.xml
+	* test/metalink4.xml
+
 2010-02-23  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Added aiFlags argument to getInterfaceAddress(). Use

+ 4 - 4
doc/aria2c.1

@@ -2,12 +2,12 @@
 .\"     Title: aria2c
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02/20/2010
+.\"      Date: 02/25/2010
 .\"    Manual: Aria2 Manual
 .\"    Source: Aria2 1.9.0a
 .\"  Language: English
 .\"
-.TH "ARIA2C" "1" "02/20/2010" "Aria2 1\&.9\&.0a" "Aria2 Manual"
+.TH "ARIA2C" "1" "02/25/2010" "Aria2 1\&.9\&.0a" "Aria2 Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -796,7 +796,7 @@ The language of the file to download\&.
 \fB\-\-metalink\-location\fR=LOCATION[,\&...]
 .RS 4
 The location of the preferred server\&. A comma\-delimited list of locations is acceptable, for example,
-\fIJP,US\fR\&.
+\fIjp,us\fR\&.
 .RE
 .PP
 \fB\-\-metalink\-os\fR=OS
@@ -2955,7 +2955,7 @@ The index is printed to the console using \-S option\&.
 .RS 4
 .\}
 .nf
-aria2c \-\-metalink\-location=JP,US \-\-metalink\-version=1\&.1 \-\-metalink\-language=en\-US file\&.metalink
+aria2c \-\-metalink\-location=jp,us \-\-metalink\-version=1\&.1 \-\-metalink\-language=en\-US file\&.metalink
 .fi
 .if n \{\
 .RE

+ 3 - 3
doc/aria2c.1.html

@@ -1556,7 +1556,7 @@ writes the piece to the appropriate files.</td>
 <dd>
 <p>
   The location of the preferred server.
-  A comma-delimited list of locations is acceptable, for example, <em>JP,US</em>.
+  A comma-delimited list of locations is acceptable, for example, <em>jp,us</em>.
 </p>
 </dd>
 <dt class="hdlist1">
@@ -3444,7 +3444,7 @@ directory.</td>
 <h4 id="_download_a_file_using_a_local_metalink_file_with_user_preference">Download a file using a local .metalink file with user preference</h4>
 <div class="listingblock">
 <div class="content">
-<pre><tt>aria2c --metalink-location=JP,US --metalink-version=1.1 --metalink-language=en-US file.metalink</tt></pre>
+<pre><tt>aria2c --metalink-location=jp,us --metalink-version=1.1 --metalink-language=en-US file.metalink</tt></pre>
 </div></div>
 <h3 id="_bittorrent_download">BitTorrent Download</h3><div style="clear:left"></div>
 <h4 id="_download_files_from_remote_bittorrent_file">Download files from remote BitTorrent file</h4>
@@ -3722,7 +3722,7 @@ files in the program, then also delete it here.</p></div>
 <div id="footnotes"><hr /></div>
 <div id="footer">
 <div id="footer-text">
-Last updated 2010-02-20 23:22:54 JST
+Last updated 2010-02-23 23:01:01 JST
 </div>
 </div>
 </body>

+ 2 - 2
doc/aria2c.1.txt

@@ -557,7 +557,7 @@ Metalink Specific Options
 
 *--metalink-location*=LOCATION[,...]::
   The location of the preferred server.
-  A comma-delimited list of locations is acceptable, for example, 'JP,US'.
+  A comma-delimited list of locations is acceptable, for example, 'jp,us'.
 
 *--metalink-os*=OS::
   The operating system of the file to download.
@@ -1717,7 +1717,7 @@ The index is printed to the console using -S option.
 Download a file using a local .metalink file with user preference
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ------------------------------------------------------------------------------
-aria2c --metalink-location=JP,US --metalink-version=1.1 --metalink-language=en-US file.metalink
+aria2c --metalink-location=jp,us --metalink-version=1.1 --metalink-language=en-US file.metalink
 ------------------------------------------------------------------------------
 
 BitTorrent Download

+ 6 - 2
src/DownloadHandlerConstants.cc

@@ -37,10 +37,14 @@
 
 namespace aria2 {
 
-const char* DownloadHandlerConstants::METALINK_EXTENSIONS[] = { ".metalink" };
+const char* DownloadHandlerConstants::METALINK_EXTENSIONS[] = {
+  ".metalink", // Metalink3Spec
+  ".meta4" // Metalink4Spec
+};
 
 const char* DownloadHandlerConstants::METALINK_CONTENT_TYPES[] = {
-  "application/metalink+xml"
+  "application/metalink4+xml", // Metalink4Spec
+  "application/metalink+xml" // Metalink3Spec
 };
 
 const char* DownloadHandlerConstants::BT_EXTENSIONS[] = { ".torrent" };

+ 47 - 11
src/ExpatMetalinkProcessor.cc

@@ -40,6 +40,8 @@
 #include "util.h"
 #include "message.h"
 #include "DlAbortEx.h"
+#include "MetalinkParserState.h"
+#include "A2STR.h"
 
 namespace aria2 {
 
@@ -52,37 +54,71 @@ public:
   SessionData(const SharedHandle<MetalinkParserStateMachine>& stm):_stm(stm) {}
 };
 
-static void mlStartElement(void* userData, const char* name, const char** attrs)
+static void splitNsName
+(std::string& localname, std::string& prefix, std::string& nsUri,
+ const std::string& nsName)
+{
+  std::pair<std::string, std::string> nsNamePair;
+  util::split(nsNamePair, nsName, '\t');
+  if(nsNamePair.second.empty()) {
+    localname = nsNamePair.first;
+  } else {
+    nsUri = nsNamePair.first;
+    localname = nsNamePair.second;
+  }
+}
+
+static void mlStartElement(void* userData, const char* nsName, const char** attrs)
 {
   SessionData* sd = reinterpret_cast<SessionData*>(userData);
 
-  std::map<std::string, std::string> attrmap;
+  std::vector<XmlAttr> xmlAttrs;
   if(attrs) {
     const char** p = attrs;
     while(*p != 0) {
-      std::string name = *p++;
+      std::string attrNsName = *p++;
       if(*p == 0) {
         break;
       }
-      std::string value = util::trim(*p++);
-      attrmap[name] = value;
+      std::string value = *p++;
+      std::pair<std::string, std::string> nsNamePair;
+      util::split(nsNamePair, attrNsName, '\t');
+      XmlAttr xa;
+      if(nsNamePair.second.empty()) {
+        xa.localname = nsNamePair.first;
+      } else {
+        xa.nsUri = nsNamePair.first;
+        xa.localname = nsNamePair.second;
+      }
+      xa.value = value;
+      xmlAttrs.push_back(xa);
     }
   }
-  sd->_stm->beginElement(name, attrmap);
+  std::string localname;
+  std::string prefix;
+  std::string nsUri;
+  splitNsName(localname, prefix, nsUri, nsName);
+  
+  sd->_stm->beginElement(localname, prefix, nsUri, xmlAttrs);
   if(sd->_stm->needsCharactersBuffering()) {
-    sd->_charactersStack.push_front(std::string());
+    sd->_charactersStack.push_front(A2STR::NIL);
   }
 }
 
-static void mlEndElement(void* userData, const char* name)
+static void mlEndElement(void* userData, const char* nsName)
 {
+  std::string localname;
+  std::string prefix;
+  std::string nsUri;
+  splitNsName(localname, prefix, nsUri, nsName);
+
   SessionData* sd = reinterpret_cast<SessionData*>(userData);
   std::string characters;
   if(sd->_stm->needsCharactersBuffering()) {
-    characters = util::trim(sd->_charactersStack.front());
+    characters = sd->_charactersStack.front();
     sd->_charactersStack.pop_front();
   }
-  sd->_stm->endElement(name, characters);
+  sd->_stm->endElement(localname, prefix, nsUri, characters);
 }
 
 static void mlCharacters(void* userData, const char* ch, int len)
@@ -110,7 +146,7 @@ MetalinkProcessor::parseFromBinaryStream(const SharedHandle<BinaryStream>& binar
   unsigned char buf[bufSize];
 
   SharedHandle<SessionData> sessionData(new SessionData(_stm));
-  XML_Parser parser = XML_ParserCreate(0);
+  XML_Parser parser = XML_ParserCreateNS(0, static_cast<const XML_Char>('\t'));
   try {
     XML_SetUserData(parser, sessionData.get());
     XML_SetElementHandler(parser, &mlStartElement, &mlEndElement);

+ 2 - 0
src/Makefile.am

@@ -457,6 +457,8 @@ SRCS += Metalinker.cc Metalinker.h\
 	MetalinkParserStateMachine.cc MetalinkParserStateMachine.h\
 	MetalinkParserState.h\
 	MetalinkParserStateImpl.cc MetalinkParserStateImpl.h\
+	MetalinkParserStateV3Impl.cc MetalinkParserStateV3Impl.h\
+	MetalinkParserStateV4Impl.cc MetalinkParserStateV4Impl.h\
 	Metalink2RequestGroup.cc Metalink2RequestGroup.h\
 	MetalinkPostDownloadHandler.cc MetalinkPostDownloadHandler.h\
 	MetalinkHelper.cc MetalinkHelper.h

+ 9 - 1
src/Makefile.in

@@ -258,6 +258,8 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_METALINK_TRUE@	MetalinkParserStateMachine.cc MetalinkParserStateMachine.h\
 @ENABLE_METALINK_TRUE@	MetalinkParserState.h\
 @ENABLE_METALINK_TRUE@	MetalinkParserStateImpl.cc MetalinkParserStateImpl.h\
+@ENABLE_METALINK_TRUE@	MetalinkParserStateV3Impl.cc MetalinkParserStateV3Impl.h\
+@ENABLE_METALINK_TRUE@	MetalinkParserStateV4Impl.cc MetalinkParserStateV4Impl.h\
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroup.cc Metalink2RequestGroup.h\
 @ENABLE_METALINK_TRUE@	MetalinkPostDownloadHandler.cc MetalinkPostDownloadHandler.h\
 @ENABLE_METALINK_TRUE@	MetalinkHelper.cc MetalinkHelper.h
@@ -585,7 +587,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	MetalinkParserController.cc MetalinkParserController.h \
 	MetalinkParserStateMachine.cc MetalinkParserStateMachine.h \
 	MetalinkParserState.h MetalinkParserStateImpl.cc \
-	MetalinkParserStateImpl.h Metalink2RequestGroup.cc \
+	MetalinkParserStateImpl.h MetalinkParserStateV3Impl.cc \
+	MetalinkParserStateV3Impl.h MetalinkParserStateV4Impl.cc \
+	MetalinkParserStateV4Impl.h Metalink2RequestGroup.cc \
 	Metalink2RequestGroup.h MetalinkPostDownloadHandler.cc \
 	MetalinkPostDownloadHandler.h MetalinkHelper.cc \
 	MetalinkHelper.h XML2SAXMetalinkProcessor.cc \
@@ -754,6 +758,8 @@ am__objects_6 =
 @ENABLE_METALINK_TRUE@	MetalinkParserController.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkParserStateMachine.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkParserStateImpl.$(OBJEXT) \
+@ENABLE_METALINK_TRUE@	MetalinkParserStateV3Impl.$(OBJEXT) \
+@ENABLE_METALINK_TRUE@	MetalinkParserStateV4Impl.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroup.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkPostDownloadHandler.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkHelper.$(OBJEXT)
@@ -1491,6 +1497,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkParserController.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkParserStateImpl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkParserStateMachine.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkParserStateV3Impl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkParserStateV4Impl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkPostDownloadHandler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkResource.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Metalinker.Po@am__quote@

+ 22 - 5
src/Metalink2RequestGroup.cc

@@ -53,6 +53,7 @@
 #include "FileEntry.h"
 #include "A2STR.h"
 #include "a2functional.h"
+#include "DownloadHandlerConstants.h"
 #ifdef ENABLE_BITTORRENT
 # include "BtDependency.h"
 # include "download_helper.h"
@@ -120,6 +121,17 @@ Metalink2RequestGroup::generate(std::deque<SharedHandle<RequestGroup> >& groups,
   createRequestGroup(groups, entries, option);
 }
 
+namespace {
+void removeMetalinkContentTypes(const SharedHandle<RequestGroup>& group)
+{
+  for(std::vector<std::string>::const_iterator i =
+	DownloadHandlerConstants::getMetalinkContentTypes().begin();
+      i != DownloadHandlerConstants::getMetalinkContentTypes().end(); ++i) {
+    group->removeAcceptType(*i);
+  }
+}
+}
+
 void
 Metalink2RequestGroup::createRequestGroup
 (std::deque<SharedHandle<RequestGroup> >& groups,
@@ -146,10 +158,15 @@ Metalink2RequestGroup::createRequestGroup
       std::deque<std::string> locations;
       util::split(option->get(PREF_METALINK_LOCATION),
                   std::back_inserter(locations), ",", true);
-      entry->setLocationPreference(locations, 100);
+      std::transform
+        (locations.begin(), locations.end(), locations.begin(), util::toLower);
+      entry->setLocationPriority
+        (locations, -MetalinkResource::getLowestPriority());
     }
     if(option->get(PREF_METALINK_PREFERRED_PROTOCOL) != V_NONE) {
-      entry->setProtocolPreference(option->get(PREF_METALINK_PREFERRED_PROTOCOL), 100);
+      entry->setProtocolPriority
+        (option->get(PREF_METALINK_PREFERRED_PROTOCOL),
+         -MetalinkResource::getLowestPriority());
     }
     if(useIndex) {
       if(std::find(selectIndexes.begin(), selectIndexes.end(), count+1) ==
@@ -186,7 +203,7 @@ Metalink2RequestGroup::createRequestGroup
         torrentRg->clearPostDownloadHandler();
         // remove "metalink" from Accept Type list to avoid loop in
         // tranparent metalink
-        torrentRg->removeAcceptType(RequestGroup::ACCEPT_METALINK);
+        removeMetalinkContentTypes(torrentRg);
         // make it in-memory download
         SharedHandle<PreDownloadHandler> preh
           (new MemoryBufferPreDownloadHandler());
@@ -197,7 +214,7 @@ Metalink2RequestGroup::createRequestGroup
       }
     }
 #endif // ENABLE_BITTORRENT
-    entry->reorderResourcesByPreference();
+    entry->reorderResourcesByPriority();
     std::deque<std::string> uris;
     std::for_each(entry->resources.begin(), entry->resources.end(),
                   AccumulateNonP2PUrl(uris));
@@ -245,7 +262,7 @@ Metalink2RequestGroup::createRequestGroup
                 static_cast<int32_t>(entry->maxConnections)));
     // remove "metalink" from Accept Type list to avoid loop in tranparent
     // metalink
-    rg->removeAcceptType(RequestGroup::ACCEPT_METALINK);
+    removeMetalinkContentTypes(rg);
 
 #ifdef ENABLE_BITTORRENT
     // Inject depenency between rg and torrentRg here if torrentRg.isNull() == false

+ 25 - 23
src/MetalinkEntry.cc

@@ -56,21 +56,22 @@ MetalinkEntry::MetalinkEntry():
 
 MetalinkEntry::~MetalinkEntry() {}
 
-class AddLocationPreference {
+class AddLocationPriority {
 private:
   std::deque<std::string> _locations;
-  int _preferenceToAdd;
+  int _priorityToAdd;
 public:
-  AddLocationPreference(const std::deque<std::string>& locations, int preferenceToAdd):
-    _locations(locations), _preferenceToAdd(preferenceToAdd)
+  AddLocationPriority
+  (const std::deque<std::string>& locations, int priorityToAdd):
+    _locations(locations), _priorityToAdd(priorityToAdd)
   {
-    std::transform(_locations.begin(), _locations.end(), _locations.begin(), util::toUpper);
     std::sort(_locations.begin(), _locations.end());
   }
 
   void operator()(SharedHandle<MetalinkResource>& res) {
-    if(std::binary_search(_locations.begin(), _locations.end(), res->location)) {
-      res->preference += _preferenceToAdd;
+    if(std::binary_search
+       (_locations.begin(), _locations.end(), res->location)) {
+      res->priority += _priorityToAdd;
     }
   }
 };
@@ -80,8 +81,8 @@ MetalinkEntry& MetalinkEntry::operator=(const MetalinkEntry& metalinkEntry)
   if(this != &metalinkEntry) {
     this->file = metalinkEntry.file;
     this->version = metalinkEntry.version;
-    this->language = metalinkEntry.language;
-    this->os = metalinkEntry.os;
+    this->languages = metalinkEntry.languages;
+    this->oses = metalinkEntry.oses;
     this->maxConnections = metalinkEntry.maxConnections;
 #ifdef ENABLE_MESSAGE_DIGEST
     this->checksum = metalinkEntry.checksum;
@@ -102,45 +103,46 @@ uint64_t MetalinkEntry::getLength() const
   return file->getLength();
 }
 
-void MetalinkEntry::setLocationPreference(const std::deque<std::string>& locations,
-                                          int preferenceToAdd)
+void MetalinkEntry::setLocationPriority
+(const std::deque<std::string>& locations, int priorityToAdd)
 {
   std::for_each(resources.begin(), resources.end(),
-                AddLocationPreference(locations, preferenceToAdd));
+                AddLocationPriority(locations, priorityToAdd));
 }
 
-class AddProtocolPreference {
+class AddProtocolPriority {
 private:
   std::string _protocol;
-  int _preferenceToAdd;
+  int _priorityToAdd;
 public:
-  AddProtocolPreference(const std::string& protocol, int prefToAdd):
-    _protocol(protocol), _preferenceToAdd(prefToAdd) {}
+  AddProtocolPriority(const std::string& protocol, int prefToAdd):
+    _protocol(protocol), _priorityToAdd(prefToAdd) {}
 
   void operator()(const SharedHandle<MetalinkResource>& res) const
   {
     if(_protocol == MetalinkResource::getTypeString(res->type)) {
-      res->preference += _preferenceToAdd;
+      res->priority += _priorityToAdd;
     }
   }
 };
 
-void MetalinkEntry::setProtocolPreference(const std::string& protocol,
-                                          int preferenceToAdd)
+void MetalinkEntry::setProtocolPriority(const std::string& protocol,
+                                          int priorityToAdd)
 {
   std::for_each(resources.begin(), resources.end(),
-                AddProtocolPreference(protocol, preferenceToAdd));
+                AddProtocolPriority(protocol, priorityToAdd));
 }
 
 class PrefOrder {
 public:
   bool operator()(const SharedHandle<MetalinkResource>& res1,
-                  const SharedHandle<MetalinkResource>& res2) {
-    return res1->preference > res2->preference;
+                  const SharedHandle<MetalinkResource>& res2)
+  {
+    return res1->priority < res2->priority;
   }
 };
 
-void MetalinkEntry::reorderResourcesByPreference() {
+void MetalinkEntry::reorderResourcesByPriority() {
   std::random_shuffle(resources.begin(), resources.end(),
                       *(SimpleRandomizer::getInstance().get()));
   std::sort(resources.begin(), resources.end(), PrefOrder());

+ 21 - 7
src/MetalinkEntry.h

@@ -39,6 +39,8 @@
 
 #include <string>
 #include <deque>
+#include <vector>
+#include <algorithm>
 
 #include "SharedHandle.h"
 
@@ -56,10 +58,10 @@ class MetalinkEntry {
 public:
   SharedHandle<FileEntry> file;
   std::string version;
-  std::string language;
-  std::string os;
+  std::vector<std::string> languages;
+  std::vector<std::string> oses;
   std::deque<SharedHandle<MetalinkResource> > resources;
-  int maxConnections;
+  int maxConnections; // Metalink3Spec
 #ifdef ENABLE_MESSAGE_DIGEST
   SharedHandle<Checksum> checksum;
   SharedHandle<ChunkChecksum> chunkChecksum;
@@ -84,10 +86,23 @@ public:
 
   void dropUnsupportedResource();
 
-  void reorderResourcesByPreference();
+  void reorderResourcesByPriority();
   
-  void setLocationPreference(const std::deque<std::string>& locations, int preferenceToAdd);
-  void setProtocolPreference(const std::string& protocol, int preferenceToAdd);
+  bool containsLanguage(const std::string& lang) const
+  {
+    return
+      std::find(languages.begin(), languages.end(), lang) != languages.end();
+  }
+
+  bool containsOS(const std::string& os) const
+  {
+    return std::find(oses.begin(), oses.end(), os) != oses.end();
+  }
+
+  void setLocationPriority
+  (const std::deque<std::string>& locations, int priorityToAdd);
+
+  void setProtocolPriority(const std::string& protocol, int priorityToAdd);
 
   static void toFileEntry
   (std::deque<SharedHandle<FileEntry> >& fileEntries,
@@ -99,7 +114,6 @@ public:
   {
     return _signature;
   }
-
 };
 
 } // namespace aria2

+ 96 - 7
src/MetalinkParserController.cc

@@ -53,7 +53,7 @@
 
 namespace aria2 {
 
-const std::string MetalinkParserController::SHA1("sha1");
+const std::string MetalinkParserController::SHA1("sha1");// Metalink3Spec
 
 MetalinkParserController::MetalinkParserController():
   _metalinker(new Metalinker())
@@ -67,6 +67,7 @@ void MetalinkParserController::newEntryTransaction()
   _tResource.reset();
 #ifdef ENABLE_MESSAGE_DIGEST
   _tChecksum.reset();
+  _tChunkChecksumV4.reset();
   _tChunkChecksum.reset();
 #endif // ENABLE_MESSAGE_DIGEST
 }
@@ -112,7 +113,7 @@ void MetalinkParserController::setLanguageOfEntry(const std::string& language)
   if(_tEntry.isNull()) {
     return;
   }
-  _tEntry->language = language;
+  _tEntry->languages.push_back(language);
 }
 
 void MetalinkParserController::setOSOfEntry(const std::string& os)
@@ -120,7 +121,7 @@ void MetalinkParserController::setOSOfEntry(const std::string& os)
   if(_tEntry.isNull()) {
     return;
   }
-  _tEntry->os = os;
+  _tEntry->oses.push_back(os);
 }
 
 void MetalinkParserController::setMaxConnectionsOfEntry(int maxConnections)
@@ -138,6 +139,7 @@ void MetalinkParserController::commitEntryTransaction()
   }
   commitResourceTransaction();
   commitChecksumTransaction();
+  commitChunkChecksumTransactionV4();
   commitChunkChecksumTransaction();
   commitSignatureTransaction();
   _metalinker->entries.push_back(_tEntry);
@@ -148,6 +150,7 @@ void MetalinkParserController::cancelEntryTransaction()
 {
   cancelResourceTransaction();
   cancelChecksumTransaction();
+  cancelChunkChecksumTransactionV4();
   cancelChunkChecksumTransaction();
   cancelSignatureTransaction();
   _tEntry.reset();
@@ -167,6 +170,14 @@ void MetalinkParserController::setURLOfResource(const std::string& url)
     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));
+    }
+  }
 }
 
 void MetalinkParserController::setTypeOfResource(const std::string& type)
@@ -182,6 +193,8 @@ void MetalinkParserController::setTypeOfResource(const std::string& type)
     _tResource->type = MetalinkResource::TYPE_HTTPS;
   } else if(type == MetalinkResource::BITTORRENT) {
     _tResource->type = MetalinkResource::TYPE_BITTORRENT;
+  } else if(type == MetalinkResource::TORRENT) { // Metalink4Spec
+    _tResource->type = MetalinkResource::TYPE_BITTORRENT;
   } else {
     _tResource->type = MetalinkResource::TYPE_NOT_SUPPORTED;
   }
@@ -195,12 +208,12 @@ void MetalinkParserController::setLocationOfResource(const std::string& location
   _tResource->location = location;
 }
 
-void MetalinkParserController::setPreferenceOfResource(int preference)
+void MetalinkParserController::setPriorityOfResource(int priority)
 {
   if(_tResource.isNull()) {
     return;
   }
-  _tResource->preference = preference;
+  _tResource->priority = priority;
 }
 
 void MetalinkParserController::setMaxConnectionsOfResource(int maxConnections)
@@ -266,7 +279,10 @@ void MetalinkParserController::commitChecksumTransaction()
     return;
   }
   if(_tEntry->checksum.isNull() ||
-     _tEntry->checksum->getAlgo() != MetalinkParserController::SHA1) {
+     // Metalink3Spec
+     (_tEntry->checksum->getAlgo() != MetalinkParserController::SHA1 &&
+      // Metalink4Spec
+      _tEntry->checksum->getAlgo() != MessageDigestContext::SHA1)) {
     _tEntry->checksum = _tChecksum;
   }
   _tChecksum.reset();
@@ -279,7 +295,80 @@ void MetalinkParserController::cancelChecksumTransaction()
   _tChecksum.reset();
 #endif // ENABLE_MESSAGE_DIGEST
 }
-  
+
+void MetalinkParserController::newChunkChecksumTransactionV4()
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(_tEntry.isNull()) {
+    return;
+  }
+  _tChunkChecksumV4.reset(new ChunkChecksum());
+  _tempChunkChecksumsV4.clear();
+#endif // ENABLE_MESSAGE_DIGEST
+}
+
+void MetalinkParserController::setTypeOfChunkChecksumV4(const std::string& type)
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(_tChunkChecksumV4.isNull()) {
+    return;
+  }
+  if(MessageDigestContext::supports(type)) {
+    _tChunkChecksumV4->setAlgo(type);
+  } else {
+    cancelChunkChecksumTransactionV4();
+  }
+#endif // ENABLE_MESSAGE_DIGEST
+}
+
+void MetalinkParserController::setLengthOfChunkChecksumV4(size_t length)
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(_tChunkChecksumV4.isNull()) {
+    return;
+  }
+  if(length > 0) {
+    _tChunkChecksumV4->setChecksumLength(length);
+  } else {
+    cancelChunkChecksumTransactionV4();
+  }
+#endif // ENABLE_MESSAGE_DIGEST
+}
+
+void MetalinkParserController::addHashOfChunkChecksumV4(const std::string& md)
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(_tChunkChecksumV4.isNull()) {
+    return;
+  }
+  _tempChunkChecksumsV4.push_back(md);
+#endif // ENABLE_MESSAGE_DIGEST  
+}
+
+void MetalinkParserController::commitChunkChecksumTransactionV4()
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(_tChunkChecksumV4.isNull()) {
+    return;
+  }
+  if(_tEntry->chunkChecksum.isNull() ||
+     _tEntry->chunkChecksum->getAlgo() != MessageDigestContext::SHA1) {
+    std::deque<std::string> checksums(_tempChunkChecksumsV4.begin(),
+				      _tempChunkChecksumsV4.end());
+    _tChunkChecksumV4->setChecksums(checksums);
+    _tEntry->chunkChecksum = _tChunkChecksumV4;
+  }
+  _tChunkChecksumV4.reset();
+#endif // ENABLE_MESSAGE_DIGEST
+}
+
+void MetalinkParserController::cancelChunkChecksumTransactionV4()
+{
+#ifdef ENABLE_MESSAGE_DIGEST
+  _tChunkChecksumV4.reset();
+#endif // ENABLE_MESSAGE_DIGEST
+}
+
 void MetalinkParserController::newChunkChecksumTransaction()
 {
 #ifdef ENABLE_MESSAGE_DIGEST

+ 35 - 16
src/MetalinkParserController.h

@@ -36,11 +36,13 @@
 #define _D_METALINK_PARSER_CONTROLLER_H_
 
 #include "common.h"
-#include "SharedHandle.h"
+
 #include <string>
 #include <utility>
 #include <deque>
 
+#include "SharedHandle.h"
+
 namespace aria2 {
 
 class Metalinker;
@@ -64,11 +66,16 @@ private:
 #ifdef ENABLE_MESSAGE_DIGEST
   SharedHandle<Checksum> _tChecksum;
 
-  SharedHandle<ChunkChecksum> _tChunkChecksum;
+  SharedHandle<ChunkChecksum> _tChunkChecksumV4; // Metalink4Spec
+
+  std::deque<std::string> _tempChunkChecksumsV4; // Metalink4Spec
+
+  SharedHandle<ChunkChecksum> _tChunkChecksum; // Metalink3Spec
+
+  std::deque<std::pair<size_t, std::string> > _tempChunkChecksums;//Metalink3Spec
+
+  std::pair<size_t, std::string> _tempHashPair; // Metalink3Spec
 
-  std::deque<std::pair<size_t, std::string> > _tempChunkChecksums;
-  
-  std::pair<size_t, std::string> _tempHashPair;
 #endif // ENABLE_MESSAGE_DIGEST
 
   SharedHandle<Signature> _tSignature;
@@ -110,7 +117,7 @@ public:
 
   void setLocationOfResource(const std::string& location);
 
-  void setPreferenceOfResource(int preference);
+  void setPriorityOfResource(int priority);
 
   void setMaxConnectionsOfResource(int maxConnections);
 
@@ -127,24 +134,36 @@ public:
   void commitChecksumTransaction();
 
   void cancelChecksumTransaction();
-  
-  void newChunkChecksumTransaction();
 
-  void setTypeOfChunkChecksum(const std::string& type);
+  void newChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void setTypeOfChunkChecksumV4(const std::string& type); // Metalink4Spec
+
+  void setLengthOfChunkChecksumV4(size_t length); // Metalink4Spec
+
+  void addHashOfChunkChecksumV4(const std::string& md); // Metalink4Spec
+
+  void commitChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void cancelChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void newChunkChecksumTransaction(); // Metalink3Spec
+
+  void setTypeOfChunkChecksum(const std::string& type); // Metalink3Spec
 
-  void setLengthOfChunkChecksum(size_t length);
+  void setLengthOfChunkChecksum(size_t length); // Metalink3Spec
 
-  void addHashOfChunkChecksum(size_t order, const std::string& md);
+  void addHashOfChunkChecksum(size_t order, const std::string& md);// Metalink3Spec
 
-  void createNewHashOfChunkChecksum(size_t order);
+  void createNewHashOfChunkChecksum(size_t order); // Metalink3Spec
 
-  void setMessageDigestOfChunkChecksum(const std::string& md);
+  void setMessageDigestOfChunkChecksum(const std::string& md); // Metalink3Spec
 
-  void addHashOfChunkChecksum();
+  void addHashOfChunkChecksum(); // Metalink3Spec
 
-  void commitChunkChecksumTransaction();
+  void commitChunkChecksumTransaction(); // Metalink3Spec
 
-  void cancelChunkChecksumTransaction();
+  void cancelChunkChecksumTransaction(); // Metalink3Spec
 
   void newSignatureTransaction();
 

+ 21 - 5
src/MetalinkParserState.h

@@ -36,13 +36,21 @@
 #define _D_METALINK_PARSER_STATE_H_
 
 #include "common.h"
-#include <map>
+
+#include <vector>
 #include <string>
 
 namespace aria2 {
 
 class MetalinkParserStateMachine;
 
+struct XmlAttr {
+  std::string localname;
+  std::string prefix;
+  std::string nsUri;
+  std::string value;
+};
+
 class MetalinkParserState
 {
 public:
@@ -50,14 +58,22 @@ public:
 
   virtual void beginElement
   (MetalinkParserStateMachine* stm,
-   const std::string& name,
-   const std::map<std::string, std::string>& attrs) = 0;
+   const std::string& localname,
+   const std::string& prefix,
+   const std::string& nsUri,
+   const std::vector<XmlAttr>& attrs) {}
   
   virtual void endElement
   (MetalinkParserStateMachine* stm,
-   const std::string& name, const std::string& characters) = 0;
+   const std::string& localname,
+   const std::string& prefix,
+   const std::string& nsUri,
+   const std::string& characters) {}
 
-  virtual bool needsCharactersBuffering() const = 0;
+  virtual bool needsCharactersBuffering() const
+  {
+    return false;
+  }
 };
 
 } // namespace aria2

+ 14 - 402
src/MetalinkParserStateImpl.cc

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2009 Tatsuhiro Tsujikawa
+ * Copyright (C) 2010 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
@@ -33,422 +33,34 @@
  */
 /* copyright --> */
 #include "MetalinkParserStateImpl.h"
+#include "MetalinkParserStateV3Impl.h"
+#include "MetalinkParserStateV4Impl.h"
 #include "MetalinkParserStateMachine.h"
-#include "RecoverableException.h"
-#include "util.h"
 
 namespace aria2 {
 
-namespace {
-
-const std::string FILE("file");
-const std::string FILES("files");
-const std::string HASH("hash");
-const std::string LANGUAGE("language");
-const std::string LENGTH("length");
-const std::string LOCATION("location");
-const std::string MAXCONNECTIONS("maxconnections");
-const std::string METALINK("metalink");
-// Can't use name VERSION because it is used as a macro.
-const std::string METALINK_VERSION("version");
-const std::string METAURL("metaurl");
-const std::string NAME("name");
-const std::string OS("os");
-const std::string PIECE("piece");
-const std::string PIECES("pieces");
-const std::string PREFERENCE("preference");
-const std::string RESOURCES("resources");
-const std::string SIGNATURE("signature");
-const std::string SIZE("size");
-const std::string TYPE("type");
-const std::string URL("url");
-const std::string VERIFICATION("verification");
-}
-
 void InitialMetalinkParserState::beginElement
 (MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
 {
-  if(name == METALINK) {
+  if(nsUri == METALINK4_NAMESPACE_URI && localname == "metalink") {
+    stm->setMetalinkStateV4();
+  } else if(nsUri == METALINK3_NAMESPACE_URI && localname == "metalink") {
     stm->setMetalinkState();
   } else {
     stm->setSkipTagState();
   }
 }
 
-void MetalinkMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  if(name == FILES) {
-    stm->setFilesState();
-  } else {
-    stm->setSkipTagState();
-  }
-}
-
-void FilesMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  if(name == FILE) {
-    stm->setFileState();
-    std::map<std::string, std::string>::const_iterator itr = attrs.find(NAME);
-    if(itr != attrs.end()) {
-      stm->newEntryTransaction();
-      stm->setFileNameOfEntry((*itr).second);
-    }
-  } else {
-    stm->setSkipTagState();
-  }
-}
-
-void FileMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  if(name == SIZE) {
-    stm->setSizeState();
-  } else if(name == METALINK_VERSION) {
-    stm->setVersionState();
-  } else if(name == LANGUAGE) {
-    stm->setLanguageState();
-  } else if(name == OS) {
-    stm->setOSState();
-  } else if(name == VERIFICATION) {
-    stm->setVerificationState();
-  } else if(name == RESOURCES) {
-    stm->setResourcesState();
-    int maxConnections;
-    {
-      std::map<std::string, std::string>::const_iterator itr =
-        attrs.find(MAXCONNECTIONS);
-      if(itr == attrs.end()) {
-        maxConnections = -1;
-      } else {
-        try {
-          maxConnections = util::parseInt((*itr).second);
-        } catch(RecoverableException& e) {
-          maxConnections = -1;
-        }
-      }
-    }
-    stm->setMaxConnectionsOfEntry(maxConnections);
-  } else {
-    stm->setSkipTagState();
-  }
-}
-
-void FileMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->commitEntryTransaction();
-}
-
-void SizeMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void SizeMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  try {
-    stm->setFileLengthOfEntry(util::parseLLInt(characters));
-  } catch(RecoverableException& e) {
-    // current metalink specification doesn't require size element.
-  }
-}
-
-void VersionMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void VersionMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setVersionOfEntry(characters);
-}
-
-void LanguageMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void LanguageMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setLanguageOfEntry(characters);
-}
-
-void OSMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void OSMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setOSOfEntry(characters);
-}
-
-void VerificationMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-#ifdef ENABLE_MESSAGE_DIGEST
-  if(name == HASH) {
-    stm->setHashState();
-    std::map<std::string, std::string>::const_iterator itr = attrs.find(TYPE);
-    if(itr == attrs.end()) {
-      return;
-    } else {
-      std::string type = (*itr).second;
-      stm->newChecksumTransaction();
-      stm->setTypeOfChecksum(type);
-    }
-  } else if(name == PIECES) {
-    stm->setPiecesState();
-    try {
-      size_t length;
-      {
-        std::map<std::string, std::string>::const_iterator itr =
-          attrs.find(LENGTH);
-        if(itr == attrs.end()) {
-          return;
-        } else {
-          length = util::parseInt((*itr).second);
-        }
-      }
-      std::string type;
-      {
-        std::map<std::string, std::string>::const_iterator itr =
-          attrs.find(TYPE);
-        if(itr == attrs.end()) {
-          return;
-        } else {
-          type = (*itr).second;
-        }
-      }
-      stm->newChunkChecksumTransaction();
-      stm->setLengthOfChunkChecksum(length);
-      stm->setTypeOfChunkChecksum(type);
-    } catch(RecoverableException& e) {
-      stm->cancelChunkChecksumTransaction();
-    }
-  } else
-#endif // ENABLE_MESSAGE_DIGEST
-    if(name == SIGNATURE) {
-      stm->setSignatureState();
-      std::map<std::string, std::string>::const_iterator itr = attrs.find(TYPE);
-      if(itr == attrs.end()) {
-        return;
-      } else {
-        stm->newSignatureTransaction();
-        stm->setTypeOfSignature((*itr).second);
-
-        std::map<std::string, std::string>::const_iterator itr = attrs.find(FILE);
-        if(itr != attrs.end()) {
-          stm->setFileOfSignature((*itr).second);
-        }
-      }
-    } else {
-      stm->setSkipTagState();
-    }
-}
-
-void HashMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void HashMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setHashOfChecksum(characters);
-  stm->commitChecksumTransaction();
-}
-
-void PiecesMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  if(name == HASH) {
-    stm->setPieceHashState();
-    std::map<std::string, std::string>::const_iterator itr = attrs.find(PIECE);
-    if(itr == attrs.end()) {
-      stm->cancelChunkChecksumTransaction();
-    } else {
-      try {
-        stm->createNewHashOfChunkChecksum(util::parseInt((*itr).second));
-      } catch(RecoverableException& e) {
-        stm->cancelChunkChecksumTransaction();
-      }
-    }
-  } else {
-    stm->setSkipTagState();
-  }
-}
-
-void PiecesMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->commitChunkChecksumTransaction();
-}
-
-void PieceHashMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void PieceHashMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setMessageDigestOfChunkChecksum(characters);
-  stm->addHashOfChunkChecksum();
-}
-
-void SignatureMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void SignatureMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setBodyOfSignature(characters);
-  stm->commitSignatureTransaction();
-}
-
-void ResourcesMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  if(name == URL) {
-    stm->setURLState();
-    std::string type;
-    {
-      std::map<std::string, std::string>::const_iterator itr = attrs.find(TYPE);
-      if(itr == attrs.end()) {
-        return;
-      } else {
-        type = (*itr).second;
-      }
-    }
-    std::string location;
-    {
-      std::map<std::string, std::string>::const_iterator itr =
-        attrs.find(LOCATION);
-      if(itr != attrs.end()) {
-        location = util::toUpper((*itr).second);
-      }
-    }
-    int preference;
-    {
-      std::map<std::string, std::string>::const_iterator itr =
-        attrs.find(PREFERENCE);
-      if(itr == attrs.end()) {
-        preference = 0;
-      } else {
-        try {
-          preference = util::parseInt((*itr).second);
-        } catch(RecoverableException& e) {
-          preference = 0;
-        }
-      }
-    }
-    int maxConnections;
-    {
-      std::map<std::string, std::string>::const_iterator itr =
-        attrs.find(MAXCONNECTIONS);
-      if(itr == attrs.end()) {
-        maxConnections = -1;
-      } else {
-        try {
-          maxConnections = util::parseInt((*itr).second);
-        } catch(RecoverableException& e) {
-          maxConnections = -1;
-        }
-      }
-    }
-    stm->newResourceTransaction();
-    stm->setTypeOfResource(type);
-    stm->setLocationOfResource(location);
-    stm->setPreferenceOfResource(preference);
-    stm->setMaxConnectionsOfResource(maxConnections);
-  } else {
-    stm->setSkipTagState();
-  }
-}
-
-void URLMetalinkParserState::beginElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
-{
-  stm->setSkipTagState();
-}
-
-void URLMetalinkParserState::endElement
-(MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::string& characters)
-{
-  stm->setURLOfResource(characters);
-  stm->commitResourceTransaction();
-}
-
 void SkipTagMetalinkParserState::beginElement
 (MetalinkParserStateMachine* stm,
- const std::string& name,
- const std::map<std::string, std::string>& attrs)
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
 {
   stm->setSkipTagState();
 }

+ 11 - 263
src/MetalinkParserStateImpl.h

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2009 Tatsuhiro Tsujikawa
+ * Copyright (C) 2010 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
@@ -39,276 +39,24 @@
 
 namespace aria2 {
 
-class InitialMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-  
-  virtual bool needsCharactersBuffering() const
-  {
-    return false;
-  }
-};
-
-class MetalinkMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return false;
-  }
-};
-
-class FilesMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class FileMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class SizeMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class VersionMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class LanguageMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class OSMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class VerificationMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class HashMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class PiecesMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class PieceHashMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class SignatureMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name, const std::map<std::string,
-                            std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class ResourcesMetalinkParserState:public MetalinkParserState
-{
-public:
-  virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
-};
-
-class URLMetalinkParserState:public MetalinkParserState
+class SkipTagMetalinkParserState:public MetalinkParserState
 {
 public:
   virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters);
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return true;
-  }
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
 };
 
-class SkipTagMetalinkParserState:public MetalinkParserState
+class InitialMetalinkParserState:public MetalinkParserState
 {
 public:
   virtual void beginElement(MetalinkParserStateMachine* stm,
-                            const std::string& name,
-                            const std::map<std::string, std::string>& attrs);
-
-  virtual void endElement(MetalinkParserStateMachine* stm,
-                          const std::string& name,
-                          const std::string& characters) {}
-
-  virtual bool needsCharactersBuffering() const
-  {
-    return false;
-  }
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
 };
 
 } // namespace aria2

+ 126 - 9
src/MetalinkParserStateMachine.cc

@@ -34,6 +34,8 @@
 /* copyright --> */
 #include "MetalinkParserStateMachine.h"
 #include "MetalinkParserStateImpl.h"
+#include "MetalinkParserStateV3Impl.h"
+#include "MetalinkParserStateV4Impl.h"
 #include "Metalinker.h"
 #include "MetalinkEntry.h"
 
@@ -41,6 +43,9 @@ namespace aria2 {
 
 MetalinkParserState* MetalinkParserStateMachine::_initialState =
   new InitialMetalinkParserState();
+MetalinkParserState* MetalinkParserStateMachine::_skipTagState =
+  new SkipTagMetalinkParserState();
+
 MetalinkParserState* MetalinkParserStateMachine::_metalinkState =
   new MetalinkMetalinkParserState();
 MetalinkParserState* MetalinkParserStateMachine::_filesState =
@@ -69,8 +74,29 @@ MetalinkParserState* MetalinkParserStateMachine::_resourcesState =
   new ResourcesMetalinkParserState();
 MetalinkParserState* MetalinkParserStateMachine::_urlState =
   new URLMetalinkParserState();
-MetalinkParserState* MetalinkParserStateMachine::_skipTagState =
-  new SkipTagMetalinkParserState();
+
+MetalinkParserState* MetalinkParserStateMachine::_metalinkStateV4 =
+  new MetalinkMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_fileStateV4 =
+  new FileMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_sizeStateV4 =
+  new SizeMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_versionStateV4 =
+  new VersionMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_languageStateV4 =
+  new LanguageMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_osStateV4 =
+  new OSMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_hashStateV4 =
+  new HashMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_piecesStateV4 =
+  new PiecesMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_pieceHashStateV4 =
+  new PieceHashMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_signatureStateV4 =
+  new SignatureMetalinkParserStateV4();
+MetalinkParserState* MetalinkParserStateMachine::_urlStateV4 =
+  new URLMetalinkParserStateV4();
 
 MetalinkParserStateMachine::MetalinkParserStateMachine():
   _ctrl(new MetalinkParserController())
@@ -148,6 +174,61 @@ void MetalinkParserStateMachine::setURLState()
   _stateStack.push(_urlState);
 }
 
+void MetalinkParserStateMachine::setMetalinkStateV4()
+{
+  _stateStack.push(_metalinkStateV4);
+}
+
+void MetalinkParserStateMachine::setFileStateV4()
+{
+  _stateStack.push(_fileStateV4);
+}
+
+void MetalinkParserStateMachine::setSizeStateV4()
+{
+  _stateStack.push(_sizeStateV4);
+}
+
+void MetalinkParserStateMachine::setVersionStateV4()
+{
+  _stateStack.push(_versionStateV4);
+}
+
+void MetalinkParserStateMachine::setLanguageStateV4()
+{
+  _stateStack.push(_languageStateV4);
+}
+
+void MetalinkParserStateMachine::setOSStateV4()
+{
+  _stateStack.push(_osStateV4);
+}
+
+void MetalinkParserStateMachine::setHashStateV4()
+{
+  _stateStack.push(_hashStateV4);
+}
+
+void MetalinkParserStateMachine::setPiecesStateV4()
+{
+  _stateStack.push(_piecesStateV4);
+}
+
+void MetalinkParserStateMachine::setPieceHashStateV4()
+{
+  _stateStack.push(_pieceHashStateV4);
+}
+
+void MetalinkParserStateMachine::setSignatureStateV4()
+{
+  _stateStack.push(_signatureStateV4);
+}
+
+void MetalinkParserStateMachine::setURLStateV4()
+{
+  _stateStack.push(_urlStateV4);
+}
+
 void MetalinkParserStateMachine::setSkipTagState()
 {
   _stateStack.push(_skipTagState);
@@ -219,9 +300,9 @@ void MetalinkParserStateMachine::setLocationOfResource
   _ctrl->setLocationOfResource(location);
 }
 
-void MetalinkParserStateMachine::setPreferenceOfResource(int preference)
+void MetalinkParserStateMachine::setPriorityOfResource(int priority)
 {
-  _ctrl->setPreferenceOfResource(preference);
+  _ctrl->setPriorityOfResource(priority);
 }
 
 void MetalinkParserStateMachine::setMaxConnectionsOfResource(int maxConnections)
@@ -264,6 +345,37 @@ void MetalinkParserStateMachine::cancelChecksumTransaction()
   _ctrl->cancelChecksumTransaction();
 }
 
+void MetalinkParserStateMachine::newChunkChecksumTransactionV4()
+{
+  _ctrl->newChunkChecksumTransactionV4();
+}
+
+void MetalinkParserStateMachine::setLengthOfChunkChecksumV4(size_t length)
+{
+  _ctrl->setLengthOfChunkChecksumV4(length);
+}
+
+void MetalinkParserStateMachine::setTypeOfChunkChecksumV4
+(const std::string& type)
+{
+  _ctrl->setTypeOfChunkChecksumV4(type);
+}
+
+void MetalinkParserStateMachine::addHashOfChunkChecksumV4(const std::string& md)
+{
+  _ctrl->addHashOfChunkChecksumV4(md);
+}
+
+void MetalinkParserStateMachine::commitChunkChecksumTransactionV4()
+{
+  _ctrl->commitChunkChecksumTransactionV4();
+}
+
+void MetalinkParserStateMachine::cancelChunkChecksumTransactionV4()
+{
+  _ctrl->cancelChunkChecksumTransactionV4();
+}
+
 void MetalinkParserStateMachine::newChunkChecksumTransaction()
 {
   _ctrl->newChunkChecksumTransaction();
@@ -336,16 +448,21 @@ void MetalinkParserStateMachine::cancelSignatureTransaction()
 }
 
 void MetalinkParserStateMachine::beginElement
-(const std::string& name,
- const std::map<std::string, std::string>& attrs)
+(const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
 {
-  _stateStack.top()->beginElement(this, name, attrs);
+  _stateStack.top()->beginElement(this, localname, prefix, nsUri, attrs);
 }
   
 void MetalinkParserStateMachine::endElement
-(const std::string& name, const std::string& characters)
+(const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
 {
-  _stateStack.top()->endElement(this, name, characters);
+  _stateStack.top()->endElement(this, localname, prefix, nsUri, characters);
   _stateStack.pop();
 }
 

+ 74 - 27
src/MetalinkParserStateMachine.h

@@ -37,15 +37,15 @@
 
 #include "common.h"
 #include <string>
-#include <map>
+#include <vector>
 #include <stack>
 
 #include "SharedHandle.h"
 #include "MetalinkParserController.h"
+#include "MetalinkParserState.h"
 
 namespace aria2 {
 
-class MetalinkParserState;
 class Metalinker;
 
 class MetalinkParserStateMachine {
@@ -55,27 +55,44 @@ private:
   std::stack<MetalinkParserState*> _stateStack;
 
   static MetalinkParserState* _initialState;
+  static MetalinkParserState* _skipTagState;
+  
+  // Metalink3
   static MetalinkParserState* _metalinkState;
-  static MetalinkParserState* _filesState;
+  static MetalinkParserState* _filesState; // Metalink3Spec
   static MetalinkParserState* _fileState;
   static MetalinkParserState* _sizeState;
   static MetalinkParserState* _versionState;
   static MetalinkParserState* _languageState;
   static MetalinkParserState* _osState;
-  static MetalinkParserState* _verificationState;
+  static MetalinkParserState* _verificationState; // Metalink3Spec
   static MetalinkParserState* _hashState;
-  static MetalinkParserState* _piecesState;
-  static MetalinkParserState* _pieceHashState;
+  static MetalinkParserState* _piecesState; // Metalink3Spec
+  static MetalinkParserState* _pieceHashState; // Metalink3Spec
   static MetalinkParserState* _signatureState;
-  static MetalinkParserState* _resourcesState;
+  static MetalinkParserState* _resourcesState; // Metalink3Spec
   static MetalinkParserState* _urlState;
-  static MetalinkParserState* _skipTagState;
+
+  // Metalink4
+  static MetalinkParserState* _metalinkStateV4;
+  static MetalinkParserState* _fileStateV4;
+  static MetalinkParserState* _sizeStateV4;
+  static MetalinkParserState* _versionStateV4;
+  static MetalinkParserState* _languageStateV4;
+  static MetalinkParserState* _osStateV4;
+  static MetalinkParserState* _hashStateV4;
+  static MetalinkParserState* _piecesStateV4; // Metalink4Spec
+  static MetalinkParserState* _pieceHashStateV4; // Metalink4Spec
+  static MetalinkParserState* _signatureStateV4;
+  static MetalinkParserState* _urlStateV4;
 public:
   MetalinkParserStateMachine();
 
+  void setSkipTagState();
+
   void setMetalinkState();
 
-  void setFilesState();
+  void setFilesState(); // Metalink3Spec
 
   void setFileState();
 
@@ -87,28 +104,46 @@ public:
   
   void setOSState();
 
-  void setVerificationState();
+  void setVerificationState(); // Metalink3Spec
 
   void setHashState();
 
-  void setPiecesState();
+  void setPiecesState(); // Metalink3Spec
 
-  void setPieceHashState();
+  void setPieceHashState(); // Metalink3Spec
 
   void setSignatureState();
 
-  void setResourcesState();
+  void setResourcesState(); // Metalink3Spec
 
   void setURLState();
 
-  void setSkipTagState();
+  // Metalink4
+  void setMetalinkStateV4();
+  void setFileStateV4();
+  void setSizeStateV4();
+  void setVersionStateV4();
+  void setLanguageStateV4();
+  void setOSStateV4();
+  void setHashStateV4();
+  void setPiecesStateV4(); // Metalink4Spec
+  void setPieceHashStateV4(); // Metalink4Spec
+  void setSignatureStateV4();
+  void setURLStateV4();
 
   bool finished() const;
 
   void beginElement
-  (const std::string& name, const std::map<std::string, std::string>& attrs);
+  (const std::string& localname,
+   const std::string& prefix,
+   const std::string& nsUri,
+   const std::vector<XmlAttr>& attrs);
   
-  void endElement(const std::string& name, const std::string& characters);
+  void endElement
+  (const std::string& localname,
+   const std::string& prefix,
+   const std::string& nsUri,
+   const std::string& characters);
 
   void newEntryTransaction();
 
@@ -122,7 +157,7 @@ public:
 
   void setOSOfEntry(const std::string& os);
 
-  void setMaxConnectionsOfEntry(int maxConnections);
+  void setMaxConnectionsOfEntry(int maxConnections); // Metalink3Spec
 
   void commitEntryTransaction();
 
@@ -134,9 +169,9 @@ public:
 
   void setLocationOfResource(const std::string& location);
 
-  void setPreferenceOfResource(int preference);
+  void setPriorityOfResource(int priority);
 
-  void setMaxConnectionsOfResource(int maxConnections);
+  void setMaxConnectionsOfResource(int maxConnections); // Metalink3Spec
 
   void commitResourceTransaction();
 
@@ -152,21 +187,33 @@ public:
 
   void cancelChecksumTransaction();
 
-  void newChunkChecksumTransaction();
+  void newChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void setLengthOfChunkChecksumV4(size_t length); // Metalink4Spec
+
+  void setTypeOfChunkChecksumV4(const std::string& type); // Metalink4Spec
+
+  void addHashOfChunkChecksumV4(const std::string& md); // Metalink4Spec
+
+  void commitChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void cancelChunkChecksumTransactionV4(); // Metalink4Spec
+
+  void newChunkChecksumTransaction(); // Metalink3Spec
 
-  void setLengthOfChunkChecksum(size_t length);
+  void setLengthOfChunkChecksum(size_t length); // Metalink3Spec
 
-  void setTypeOfChunkChecksum(const std::string& type);
+  void setTypeOfChunkChecksum(const std::string& type); // Metalink3Spec
 
-  void createNewHashOfChunkChecksum(size_t order);
+  void createNewHashOfChunkChecksum(size_t order); // Metalink3Spec
 
-  void setMessageDigestOfChunkChecksum(const std::string& md);
+  void setMessageDigestOfChunkChecksum(const std::string& md); // Metalink3Spec
 
-  void addHashOfChunkChecksum();
+  void addHashOfChunkChecksum(); // Metalink3Spec
 
-  void commitChunkChecksumTransaction();
+  void commitChunkChecksumTransaction(); // Metalink3Spec
 
-  void cancelChunkChecksumTransaction();
+  void cancelChunkChecksumTransaction(); // Metalink3Spec
 
   void newSignatureTransaction();
 

+ 430 - 0
src/MetalinkParserStateV3Impl.cc

@@ -0,0 +1,430 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "MetalinkParserStateV3Impl.h"
+#include "MetalinkParserStateMachine.h"
+#include "RecoverableException.h"
+#include "MetalinkResource.h"
+#include "util.h"
+
+namespace aria2 {
+
+namespace {
+
+const std::string FILE("file");
+const std::string FILES("files");
+const std::string HASH("hash");
+const std::string LANGUAGE("language");
+const std::string LENGTH("length");
+const std::string LOCATION("location");
+const std::string MAXCONNECTIONS("maxconnections");
+// Can't use name VERSION because it is used as a macro.
+const std::string METALINK_VERSION("version");
+const std::string NAME("name");
+const std::string OS("os");
+const std::string PIECE("piece");
+const std::string PIECES("pieces");
+const std::string PREFERENCE("preference");
+const std::string RESOURCES("resources");
+const std::string SIGNATURE("signature");
+const std::string SIZE("size");
+const std::string TYPE("type");
+const std::string URL("url");
+const std::string VERIFICATION("verification");
+}
+
+const std::string METALINK3_NAMESPACE_URI("http://www.metalinker.org/");
+
+namespace {
+class FindAttr {
+private:
+  const std::string& _localname;
+public:
+  FindAttr(const std::string& localname):_localname(localname) {}
+
+  bool operator()(const XmlAttr& attr) const
+  {
+    return attr.localname == _localname &&
+      (attr.nsUri.empty() || attr.nsUri == METALINK3_NAMESPACE_URI);
+  }
+};
+}
+
+template<typename Container>
+static typename Container::const_iterator findAttr
+(const Container& attrs, const std::string& localname)
+{
+  return std::find_if(attrs.begin(), attrs.end(), FindAttr(localname));
+}
+
+void MetalinkMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri == METALINK3_NAMESPACE_URI && localname == FILES) {
+    stm->setFilesState();
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void FilesMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri == METALINK3_NAMESPACE_URI && localname == FILE) {
+    stm->setFileState();
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, NAME);
+    if(itr != attrs.end()) {
+      stm->newEntryTransaction();
+      stm->setFileNameOfEntry(util::trim((*itr).value));
+    }
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void FileMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri != METALINK3_NAMESPACE_URI) {
+    stm->setSkipTagState();
+  } else if(localname == SIZE) {
+    stm->setSizeState();
+  } else if(localname == METALINK_VERSION) {
+    stm->setVersionState();
+  } else if(localname == LANGUAGE) {
+    stm->setLanguageState();
+  } else if(localname == OS) {
+    stm->setOSState();
+  } else if(localname == VERIFICATION) {
+    stm->setVerificationState();
+  } else if(localname == RESOURCES) {
+    stm->setResourcesState();
+    int maxConnections;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs,MAXCONNECTIONS);
+      if(itr == attrs.end()) {
+        maxConnections = -1;
+      } else {
+        try {
+          maxConnections = util::parseInt((*itr).value);
+        } catch(RecoverableException& e) {
+          maxConnections = -1;
+        }
+      }
+    }
+    stm->setMaxConnectionsOfEntry(maxConnections);
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void FileMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->commitEntryTransaction();
+}
+
+void SizeMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  try {
+    stm->setFileLengthOfEntry(util::parseLLInt(characters));
+  } catch(RecoverableException& e) {
+    // current metalink specification doesn't require size element.
+  }
+}
+
+void VersionMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setVersionOfEntry(util::trim(characters));
+}
+
+void LanguageMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setLanguageOfEntry(util::trim(characters));
+}
+
+void OSMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setOSOfEntry(util::trim(characters));
+}
+
+void VerificationMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri != METALINK3_NAMESPACE_URI) {
+    stm->setSkipTagState();
+  } else
+#ifdef ENABLE_MESSAGE_DIGEST
+  if(localname == HASH) {
+    stm->setHashState();
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+    if(itr == attrs.end()) {
+      return;
+    } else {
+      std::string type = util::trim((*itr).value);
+      stm->newChecksumTransaction();
+      stm->setTypeOfChecksum(type);
+    }
+  } else if(localname == PIECES) {
+    stm->setPiecesState();
+    try {
+      size_t length;
+      {
+        std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, LENGTH);
+        if(itr == attrs.end()) {
+          return;
+        } else {
+          length = util::parseInt((*itr).value);
+        }
+      }
+      std::string type;
+      {
+        std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+        if(itr == attrs.end()) {
+          return;
+        } else {
+          type = util::trim((*itr).value);
+        }
+      }
+      stm->newChunkChecksumTransaction();
+      stm->setLengthOfChunkChecksum(length);
+      stm->setTypeOfChunkChecksum(type);
+    } catch(RecoverableException& e) {
+      stm->cancelChunkChecksumTransaction();
+    }
+  } else
+#endif // ENABLE_MESSAGE_DIGEST
+    if(localname == SIGNATURE) {
+      stm->setSignatureState();
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+      if(itr == attrs.end()) {
+        return;
+      } else {
+        stm->newSignatureTransaction();
+        stm->setTypeOfSignature(util::trim((*itr).value));
+        std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, FILE);
+        if(itr != attrs.end()) {
+          stm->setFileOfSignature(util::trim((*itr).value));
+        }
+      }
+    } else {
+      stm->setSkipTagState();
+    }
+}
+
+void HashMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setHashOfChecksum(util::trim(characters));
+  stm->commitChecksumTransaction();
+}
+
+void PiecesMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri == METALINK3_NAMESPACE_URI && localname == HASH) {
+    stm->setPieceHashState();
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, PIECE);
+    if(itr == attrs.end()) {
+      stm->cancelChunkChecksumTransaction();
+    } else {
+      try {
+        stm->createNewHashOfChunkChecksum(util::parseInt((*itr).value));
+      } catch(RecoverableException& e) {
+        stm->cancelChunkChecksumTransaction();
+      }
+    }
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void PiecesMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->commitChunkChecksumTransaction();
+}
+
+void PieceHashMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setMessageDigestOfChunkChecksum(util::trim(characters));
+  stm->addHashOfChunkChecksum();
+}
+
+void SignatureMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setBodyOfSignature(util::trim(characters));
+  stm->commitSignatureTransaction();
+}
+
+void ResourcesMetalinkParserState::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri != METALINK3_NAMESPACE_URI) {
+    stm->setSkipTagState();
+  } else if(localname == URL) {
+    stm->setURLState();
+    std::string type;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+      if(itr == attrs.end()) {
+        return;
+      } else {
+        type = util::trim((*itr).value);
+      }
+    }
+    std::string location;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, LOCATION);
+      if(itr != attrs.end()) {
+        location = util::trim((*itr).value);
+      }
+    }
+    int preference;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, PREFERENCE);
+      if(itr == attrs.end()) {
+        preference = MetalinkResource::getLowestPriority();
+      } else {
+        try {
+          // In Metalink3Spec, highest prefernce value is 100.  We
+          // uses Metalink4Spec priority unit system in which 1 is
+          // higest.
+          preference = 101-util::parseInt((*itr).value);
+        } catch(RecoverableException& e) {
+          preference = MetalinkResource::getLowestPriority();
+        }
+      }
+    }
+    int maxConnections;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs,MAXCONNECTIONS);
+      if(itr == attrs.end()) {
+        maxConnections = -1;
+      } else {
+        try {
+          maxConnections = util::parseInt((*itr).value);
+        } catch(RecoverableException& e) {
+          maxConnections = -1;
+        }
+      }
+    }
+    stm->newResourceTransaction();
+    stm->setTypeOfResource(type);
+    stm->setLocationOfResource(location);
+    stm->setPriorityOfResource(preference);
+    stm->setMaxConnectionsOfResource(maxConnections);
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void URLMetalinkParserState::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setURLOfResource(util::trim(characters));
+  stm->commitResourceTransaction();
+}
+
+} // namespace aria2

+ 264 - 0
src/MetalinkParserStateV3Impl.h

@@ -0,0 +1,264 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_METALINK_PARSER_STATE_V3_IMPL_H_
+#define _D_METALINK_PARSER_STATE_V3_IMPL_H_
+
+#include "MetalinkParserState.h"
+#include "MetalinkParserStateImpl.h"
+
+namespace aria2 {
+
+extern const std::string METALINK3_NAMESPACE_URI;
+
+class MetalinkMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+};
+
+class FilesMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class FileMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class SizeMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class VersionMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class LanguageMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class OSMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class VerificationMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class HashMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class PiecesMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class PieceHashMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+			  const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class SignatureMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class ResourcesMetalinkParserState:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class URLMetalinkParserState:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_METALINK_PARSER_STATE_V3_IMPL_H_

+ 366 - 0
src/MetalinkParserStateV4Impl.cc

@@ -0,0 +1,366 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "MetalinkParserStateV4Impl.h"
+#include "MetalinkParserStateMachine.h"
+#include "RecoverableException.h"
+#include "MetalinkResource.h"
+#include "util.h"
+
+namespace aria2 {
+
+namespace {
+
+const std::string FILE("file");
+const std::string HASH("hash");
+const std::string LANGUAGE("language");
+const std::string LENGTH("length");
+const std::string LOCATION("location");
+const std::string METALINK("metalink");
+// Can't use name VERSION because it is used as a macro.
+const std::string METALINK_VERSION("version");
+const std::string METAURL("metaurl");
+const std::string NAME("name");
+const std::string OS("os");
+const std::string PIECE("piece");
+const std::string PIECES("pieces");
+const std::string PRIORITY("priority");
+const std::string SIGNATURE("signature");
+const std::string SIZE("size");
+const std::string TYPE("type");
+const std::string MEDIATYPE("mediatype");
+const std::string URL("url");
+}
+
+const std::string METALINK4_NAMESPACE_URI("urn:ietf:params:xml:ns:metalink");
+
+namespace {
+class FindAttr {
+private:
+  const std::string& _localname;
+public:
+  FindAttr(const std::string& localname):_localname(localname) {}
+
+  bool operator()(const XmlAttr& attr) const
+  {
+    return attr.localname == _localname &&
+      (attr.nsUri.empty() || attr.nsUri == METALINK4_NAMESPACE_URI);
+  }
+};
+}
+
+template<typename Container>
+static typename Container::const_iterator findAttr
+(const Container& attrs, const std::string& localname)
+{
+  return std::find_if(attrs.begin(), attrs.end(), FindAttr(localname));
+}
+
+void MetalinkMetalinkParserStateV4::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri == METALINK4_NAMESPACE_URI && localname == FILE) {
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, NAME);
+    if(itr != attrs.end()) {
+      // TODO Windows path separator support.
+      if(util::detectDirTraversal((*itr).value)) {
+        stm->setSkipTagState();
+      } else {
+        stm->setFileStateV4();
+        stm->newEntryTransaction();
+        stm->setFileNameOfEntry((*itr).value);
+      }
+    }
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void FileMetalinkParserStateV4::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri != METALINK4_NAMESPACE_URI) {
+    stm->setSkipTagState();
+  } else if(localname == SIZE) {
+    stm->setSizeStateV4();
+  } else if(localname == METALINK_VERSION) {
+    stm->setVersionStateV4();
+  } else if(localname == LANGUAGE) {
+    stm->setLanguageStateV4();
+  } else if(localname == OS) {
+    stm->setOSStateV4();
+  } else if(localname == METAURL) {
+    stm->setURLStateV4();
+    // TODO currently NAME is ignored
+    int priority;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, PRIORITY);
+      if(itr == attrs.end()) {
+        priority = MetalinkResource::getLowestPriority();
+      } else {
+        try {
+          priority = util::parseInt((*itr).value);
+          if(priority < 1 || MetalinkResource::getLowestPriority() < priority) {
+            priority = MetalinkResource::getLowestPriority();
+          }
+        } catch(RecoverableException& e) {
+          priority = MetalinkResource::getLowestPriority();
+        }
+      }
+    }
+    std::string mediatype;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, MEDIATYPE);
+      if(itr == attrs.end()) {
+        return;
+      } else {
+        mediatype = (*itr).value;
+      }
+    }
+    stm->newResourceTransaction();
+    stm->setPriorityOfResource(priority);
+    stm->setTypeOfResource(mediatype);
+  } else if(localname == URL) {
+    stm->setURLStateV4();
+    std::string location;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, LOCATION);
+      if(itr != attrs.end()) {
+        location = (*itr).value;
+      }
+    }
+    int priority;
+    {
+      std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, PRIORITY);
+      if(itr == attrs.end()) {
+        priority = MetalinkResource::getLowestPriority();
+      } else {
+        try {
+          priority = util::parseInt((*itr).value);
+          if(priority < 1 || MetalinkResource::getLowestPriority() < priority) {
+            priority = MetalinkResource::getLowestPriority();
+          }
+        } catch(RecoverableException& e) {
+          priority = MetalinkResource::getLowestPriority();
+        }
+      }
+    }
+    stm->newResourceTransaction();
+    stm->setLocationOfResource(location);
+    stm->setPriorityOfResource(priority);
+  }
+#ifdef ENABLE_MESSAGE_DIGEST
+  else if(localname == HASH) {
+    stm->setHashStateV4();
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+    if(itr == attrs.end()) {
+      return;
+    } else {
+      std::string type = (*itr).value;
+      stm->newChecksumTransaction();
+      stm->setTypeOfChecksum(type);
+    }
+  } else if(localname == PIECES) {
+    stm->setPiecesStateV4();
+    try {
+      size_t length;
+      {
+	std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, LENGTH);
+	if(itr == attrs.end()) {
+	  return;
+	} else {
+	  length = util::parseInt((*itr).value);
+	}
+      }
+      std::string type;
+      {
+	std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, TYPE);
+	if(itr == attrs.end()) {
+	  return;
+	} else {
+	  type = (*itr).value;
+	}
+      }
+      stm->newChunkChecksumTransactionV4();
+      stm->setLengthOfChunkChecksumV4(length);
+      stm->setTypeOfChunkChecksumV4(type);
+    } catch(RecoverableException& e) {
+      stm->cancelChunkChecksumTransactionV4();
+    }
+  }
+#endif // ENABLE_MESSAGE_DIGEST
+  else if(localname == SIGNATURE) {
+    stm->setSignatureStateV4();
+    std::vector<XmlAttr>::const_iterator itr = findAttr(attrs, MEDIATYPE);
+    if(itr == attrs.end()) {
+      return;
+    } else {
+      stm->newSignatureTransaction();
+      stm->setTypeOfSignature((*itr).value);
+    }
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void FileMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->commitEntryTransaction();
+}
+
+void SizeMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  try {
+    stm->setFileLengthOfEntry(util::parseLLInt(characters));
+  } catch(RecoverableException& e) {
+    // current metalink specification doesn't require size element.
+  }
+}
+
+void VersionMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setVersionOfEntry(characters);
+}
+
+void LanguageMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setLanguageOfEntry(characters);
+}
+
+void OSMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setOSOfEntry(characters);
+}
+
+void HashMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setHashOfChecksum(characters);
+  stm->commitChecksumTransaction();
+}
+
+void PiecesMetalinkParserStateV4::beginElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::vector<XmlAttr>& attrs)
+{
+  if(nsUri == METALINK4_NAMESPACE_URI && localname == HASH) {
+    stm->setPieceHashStateV4();
+  } else {
+    stm->setSkipTagState();
+  }
+}
+
+void PiecesMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->commitChunkChecksumTransaction();
+}
+
+void PieceHashMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->addHashOfChunkChecksumV4(characters);
+}
+
+void SignatureMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setBodyOfSignature(characters);
+  stm->commitSignatureTransaction();
+}
+
+void URLMetalinkParserStateV4::endElement
+(MetalinkParserStateMachine* stm,
+ const std::string& localname,
+ const std::string& prefix,
+ const std::string& nsUri,
+ const std::string& characters)
+{
+  stm->setURLOfResource(characters);
+  stm->commitResourceTransaction();
+}
+
+} // namespace aria2

+ 219 - 0
src/MetalinkParserStateV4Impl.h

@@ -0,0 +1,219 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_METALINK_PARSER_STATE_V4_IMPL_H_
+#define _D_METALINK_PARSER_STATE_V4_IMPL_H_
+
+#include "MetalinkParserState.h"
+#include "MetalinkParserStateImpl.h"
+
+namespace aria2 {
+
+extern const std::string METALINK4_NAMESPACE_URI;
+
+class MetalinkMetalinkParserStateV4:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+};
+
+class FileMetalinkParserStateV4:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class SizeMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class VersionMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class LanguageMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class OSMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class HashMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class PiecesMetalinkParserStateV4:public MetalinkParserState
+{
+public:
+  virtual void beginElement(MetalinkParserStateMachine* stm,
+                            const std::string& localname,
+                            const std::string& prefix,
+                            const std::string& nsUri,
+                            const std::vector<XmlAttr>& attrs);
+
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class PieceHashMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+			  const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class SignatureMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+class URLMetalinkParserStateV4:public SkipTagMetalinkParserState
+{
+public:
+  virtual void endElement(MetalinkParserStateMachine* stm,
+                          const std::string& localname,
+                          const std::string& prefix,
+                          const std::string& nsUri,
+                          const std::string& characters);
+
+  virtual bool needsCharactersBuffering() const
+  {
+    return true;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_METALINK_PARSER_STATE_IMPL_H_

+ 5 - 1
src/MetalinkResource.cc

@@ -37,7 +37,7 @@
 namespace aria2 {
 
 std::string MetalinkResource::type2String[] = {
-  "ftp", "http", "https", "bittorrent", "not_supported"
+  "ftp", "http", "https", "bittorrent", "not_supported", "unknown"
 };
 
 const std::string MetalinkResource::HTTP("http");
@@ -48,7 +48,11 @@ const std::string MetalinkResource::FTP("ftp");
 
 const std::string MetalinkResource::BITTORRENT("bittorrent");
 
+const std::string MetalinkResource::TORRENT("torrent");
+
 MetalinkResource::MetalinkResource():
+  type(TYPE_UNKNOWN),
+  priority(getLowestPriority()),
   maxConnections(-1)
 {}
 

+ 12 - 4
src/MetalinkResource.h

@@ -47,7 +47,8 @@ public:
     TYPE_HTTP,
     TYPE_HTTPS,
     TYPE_BITTORRENT,
-    TYPE_NOT_SUPPORTED
+    TYPE_NOT_SUPPORTED,
+    TYPE_UNKNOWN
   };
 
   static std::string type2String[];
@@ -60,12 +61,14 @@ public:
 
   static const std::string BITTORRENT;
 
+  static const std::string TORRENT; // Metalink4Spec
+
 public:
   std::string url;
   TYPE type;
   std::string location;
-  int preference;
-  int maxConnections;
+  int priority;
+  int maxConnections; // Metalink3Spec
 public:
   MetalinkResource();
   ~MetalinkResource();
@@ -75,7 +78,7 @@ public:
       this->url = metalinkResource.url;
       this->type = metalinkResource.type;
       this->location = metalinkResource.location;
-      this->preference = metalinkResource.preference;
+      this->priority = metalinkResource.priority;
       this->maxConnections = metalinkResource.maxConnections;
     }
     return *this;
@@ -85,6 +88,11 @@ public:
   {
     return type2String[type];
   }
+
+  static int getLowestPriority()
+  {
+    return 999999;
+  }
 };
 
 } // namespace aria2

+ 2 - 2
src/Metalinker.cc

@@ -62,12 +62,12 @@ public:
       }
     }
     if(!language.empty()) {
-      if(language != entry->language) {
+      if(!entry->containsLanguage(language)) {
         return false;
       }
     }
     if(!os.empty()) {
-      if(os != entry->os) {
+      if(!entry->containsOS(os)) {
         return false;
       }
     }

+ 3 - 4
src/RequestGroup.cc

@@ -110,8 +110,6 @@ namespace aria2 {
 
 int32_t RequestGroup::_gidCounter = 0;
 
-const std::string RequestGroup::ACCEPT_METALINK = "application/metalink+xml";
-
 RequestGroup::RequestGroup(const SharedHandle<Option>& option):
   _gid(newGID()),
   _option(new Option(*option.get())),
@@ -138,8 +136,9 @@ RequestGroup::RequestGroup(const SharedHandle<Option>& option):
   // Add types to be sent as a Accept header value here.
   // It would be good to put this value in Option so that user can tweak
   // and add this list.
-  // ACCEPT_METALINK is used for `transparent metalink'.
-  addAcceptType(ACCEPT_METALINK);
+  // The mime types of Metalink is used for `transparent metalink'.
+  addAcceptType(DownloadHandlerConstants::getMetalinkContentTypes().begin(),
+		DownloadHandlerConstants::getMetalinkContentTypes().end());
   if(!_option->getAsBool(PREF_DRY_RUN)) {
     initializePreDownloadHandler();
     initializePostDownloadHandler();

+ 12 - 2
src/RequestGroup.h

@@ -39,6 +39,7 @@
 
 #include <string>
 #include <deque>
+#include <algorithm>
 #include <vector>
 
 #include "SharedHandle.h"
@@ -379,9 +380,18 @@ public:
 
   void addAcceptType(const std::string& type);
 
-  void removeAcceptType(const std::string& type);
+  template<typename InputIterator>
+  void addAcceptType(InputIterator first, InputIterator last)
+  {
+    for(; first != last; ++first) {
+      if(std::find(_acceptTypes.begin(), _acceptTypes.end(), *first) ==
+	 _acceptTypes.end()) {
+	_acceptTypes.push_back(*first);
+      }
+    }
+  }
 
-  static const std::string ACCEPT_METALINK;
+  void removeAcceptType(const std::string& type);
 
   void setURISelector(const SharedHandle<URISelector>& uriSelector);
 

+ 62 - 23
src/XML2SAXMetalinkProcessor.cc

@@ -33,6 +33,9 @@
  */
 /* copyright --> */
 #include "XML2SAXMetalinkProcessor.h"
+
+#include <cassert>
+
 #include "BinaryStream.h"
 #include "MetalinkParserStateMachine.h"
 #include "Metalinker.h"
@@ -40,6 +43,7 @@
 #include "util.h"
 #include "message.h"
 #include "DlAbortEx.h"
+#include "A2STR.h"
 
 namespace aria2 {
 
@@ -52,38 +56,73 @@ public:
   SessionData(const SharedHandle<MetalinkParserStateMachine>& stm):_stm(stm) {}
 };
 
-static void mlStartElement(void* userData, const xmlChar* name, const xmlChar** attrs)
+static void mlStartElement
+(void* userData,
+ const xmlChar* srcLocalname,
+ const xmlChar* srcPrefix,
+ const xmlChar* srcNsUri,
+ int numNamespaces,
+ const xmlChar **namespaces,
+ int numAttrs,
+ int numDefaulted,
+ const xmlChar **attrs)
 {
   SessionData* sd = reinterpret_cast<SessionData*>(userData);
-  std::map<std::string, std::string> attrmap;
-  if(attrs) {
-    const xmlChar** p = attrs;
-    while(*p != 0) {
-      std::string name = reinterpret_cast<const char*>(*p);
-      ++p;
-      if(*p == 0) {
-        break;
-      }
-      std::string value = util::trim(reinterpret_cast<const char*>(*p));
-      ++p;
-      attrmap[name] = value;
+  std::vector<XmlAttr> xmlAttrs;
+  size_t index = 0;
+  for(int attrIndex = 0; attrIndex < numAttrs; ++attrIndex, index += 5) {
+    XmlAttr xmlAttr;
+    assert(attrs[index]);
+    xmlAttr.localname = reinterpret_cast<const char*>(attrs[index]);
+    if(attrs[index+1]) {
+      xmlAttr.prefix = reinterpret_cast<const char*>(attrs[index+1]);
+    }
+    if(attrs[index+2]) {
+      xmlAttr.nsUri = reinterpret_cast<const char*>(attrs[index+2]);
     }
+    const char* valueBegin = reinterpret_cast<const char*>(attrs[index+3]);
+    const char* valueEnd = reinterpret_cast<const char*>(attrs[index+4]);
+    xmlAttr.value = std::string(valueBegin, valueEnd);
+    xmlAttrs.push_back(xmlAttr);
   }
-  sd->_stm->beginElement(reinterpret_cast<const char*>(name), attrmap);
+  assert(srcLocalname);
+  std::string localname = reinterpret_cast<const char*>(srcLocalname);
+  std::string prefix;
+  std::string nsUri;
+  if(srcPrefix) {
+    prefix = reinterpret_cast<const char*>(srcPrefix);
+  }
+  if(srcNsUri) {
+    nsUri = reinterpret_cast<const char*>(srcNsUri);
+  }
+  sd->_stm->beginElement(localname, prefix, nsUri, xmlAttrs);
   if(sd->_stm->needsCharactersBuffering()) {
-    sd->_charactersStack.push_front(std::string());
+    sd->_charactersStack.push_front(A2STR::NIL);
   }
 }
 
-static void mlEndElement(void* userData, const xmlChar* name)
+static void mlEndElement
+(void* userData,
+ const xmlChar* srcLocalname,
+ const xmlChar* srcPrefix,
+ const xmlChar* srcNsUri)
 {
   SessionData* sd = reinterpret_cast<SessionData*>(userData);
   std::string characters;
   if(sd->_stm->needsCharactersBuffering()) {
-    characters = util::trim(sd->_charactersStack.front());
+    characters = sd->_charactersStack.front();
     sd->_charactersStack.pop_front();
   }
-  sd->_stm->endElement(reinterpret_cast<const char*>(name), characters);
+  std::string localname = reinterpret_cast<const char*>(srcLocalname);
+  std::string prefix;
+  std::string nsUri;
+  if(srcPrefix) {
+    prefix = reinterpret_cast<const char*>(srcPrefix);
+  }
+  if(srcNsUri) {
+    nsUri = reinterpret_cast<const char*>(srcNsUri);
+  }
+  sd->_stm->endElement(localname, prefix, nsUri, characters);
 }
 
 static void mlCharacters(void* userData, const xmlChar* ch, int len)
@@ -110,8 +149,8 @@ static xmlSAXHandler mySAXHandler =
     0, //   setDocumentLocatorSAXFunc
     0, //   startDocumentSAXFunc
     0, //   endDocumentSAXFunc
-    &mlStartElement, //   startElementSAXFunc
-    &mlEndElement, //   endElementSAXFunc
+    0, //   startElementSAXFunc
+    0, //   endElementSAXFunc
     0, //   referenceSAXFunc
     &mlCharacters, //   charactersSAXFunc
     0, //   ignorableWhitespaceSAXFunc
@@ -123,10 +162,10 @@ static xmlSAXHandler mySAXHandler =
     0, //   getParameterEntitySAXFunc
     0, //   cdataBlockSAXFunc
     0, //   externalSubsetSAXFunc
-    0, //   unsigned int        initialized
+    XML_SAX2_MAGIC, //   unsigned int        initialized
     0, //   void *      _private
-    0, //   startElementNsSAX2Func
-    0, //   endElementNsSAX2Func
+    &mlStartElement, //   startElementNsSAX2Func
+    &mlEndElement, //   endElementNsSAX2Func
     0, //   xmlStructuredErrorFunc
   };
 

+ 7 - 3
src/messageDigest.cc

@@ -37,22 +37,26 @@
 
 namespace aria2 {
 
-const std::string MessageDigestContext::SHA1("sha1");
+const std::string MessageDigestContext::SHA1("sha-1");
 
-const std::string MessageDigestContext::SHA256("sha256");
+const std::string MessageDigestContext::SHA256("sha-256");
 
-const std::string MessageDigestContext::MD5("md5");
+const std::string MessageDigestContext::MD5("md-5");
 
 static MessageDigestContext::DigestAlgoMap::value_type digests[] = {
 #ifdef HAVE_LIBSSL
   MessageDigestContext::DigestAlgoMap::value_type("md5", EVP_md5()),
+  MessageDigestContext::DigestAlgoMap::value_type("sha-1", EVP_sha1()),
   MessageDigestContext::DigestAlgoMap::value_type("sha1", EVP_sha1()),
 # ifdef HAVE_EVP_SHA256
+  MessageDigestContext::DigestAlgoMap::value_type("sha-256", EVP_sha256()),
   MessageDigestContext::DigestAlgoMap::value_type("sha256", EVP_sha256()),
 # endif // HAVE_EVP_SHA256
 #elif HAVE_LIBGCRYPT
   MessageDigestContext::DigestAlgoMap::value_type("md5", GCRY_MD_MD5),
+  MessageDigestContext::DigestAlgoMap::value_type("sha-1", GCRY_MD_SHA1),
   MessageDigestContext::DigestAlgoMap::value_type("sha1", GCRY_MD_SHA1),
+  MessageDigestContext::DigestAlgoMap::value_type("sha-256", GCRY_MD_SHA256),
   MessageDigestContext::DigestAlgoMap::value_type("sha256", GCRY_MD_SHA256),
 #endif // HAVE_LIBGCRYPT
 };

+ 14 - 0
src/util.cc

@@ -1167,6 +1167,20 @@ bool inPrivateAddress(const std::string& ipv4addr)
   return false;
 }
 
+bool detectDirTraversal(const std::string& s)
+{
+  return s == A2STR::DOT_C ||
+    s == ".." ||
+    util::startsWith(s, A2STR::SLASH_C) ||
+    util::startsWith(s, "./") ||
+    util::startsWith(s, "../") ||
+    s.find("/../") != std::string::npos ||
+    s.find("/./") != std::string::npos ||
+    util::endsWith(s, "/") ||
+    util::endsWith(s, "/.") ||
+    util::endsWith(s, "/..");
+}
+
 } // namespace util
 
 } // namespace aria2

+ 4 - 0
src/util.h

@@ -381,6 +381,10 @@ void generateRandomKey(unsigned char* key);
 // Returns true is given numeric ipv4addr is in Private Address Space.
 bool inPrivateAddress(const std::string& ipv4addr);
 
+// Returns true if s contains directory traversal path component such
+// as '..'.
+bool detectDirTraversal(const std::string& s);
+
 } // namespace util
 
 } // namespace aria2

+ 4 - 1
test/Makefile.am

@@ -247,4 +247,7 @@ EXTRA_DIST = 4096chunk.txt\
 	url-list-singleFileEndsWithSlash.torrent\
 	input_uris.txt\
 	2files.metalink\
-	utf8.torrent
+	utf8.torrent\
+	metalink4.xml\
+	metalink4-attrs.xml\
+	metalink4-dirtraversal.xml

+ 4 - 1
test/Makefile.in

@@ -685,7 +685,10 @@ EXTRA_DIST = 4096chunk.txt\
 	url-list-singleFileEndsWithSlash.torrent\
 	input_uris.txt\
 	2files.metalink\
-	utf8.torrent
+	utf8.torrent\
+	metalink4.xml\
+	metalink4-attrs.xml\
+	metalink4-dirtraversal.xml
 
 all: all-am
 

+ 47 - 45
test/MetalinkEntryTest.cc

@@ -1,16 +1,18 @@
 #include "MetalinkEntry.h"
-#include "MetalinkResource.h"
+
 #include <cppunit/extensions/HelperMacros.h>
 
+#include "MetalinkResource.h"
+
 namespace aria2 {
 
 class MetalinkEntryTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(MetalinkEntryTest);
   CPPUNIT_TEST(testDropUnsupportedResource);
-  CPPUNIT_TEST(testReorderResourcesByPreference);
-  CPPUNIT_TEST(testSetLocationPreference);
-  CPPUNIT_TEST(testSetProtocolPreference);
+  CPPUNIT_TEST(testReorderResourcesByPriority);
+  CPPUNIT_TEST(testSetLocationPriority);
+  CPPUNIT_TEST(testSetProtocolPriority);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -21,9 +23,9 @@ public:
   }
 
   void testDropUnsupportedResource();
-  void testReorderResourcesByPreference();
-  void testSetLocationPreference();
-  void testSetProtocolPreference();
+  void testReorderResourcesByPriority();
+  void testSetLocationPriority();
+  void testSetProtocolPriority();
 };
 
 
@@ -34,28 +36,28 @@ SharedHandle<MetalinkEntry> createTestEntry() {
   SharedHandle<MetalinkResource> res1(new MetalinkResource());
   res1->url = "ftp://myhost/aria2.tar.bz2";
   res1->type = MetalinkResource::TYPE_FTP;
-  res1->location = "RO";
-  res1->preference = 50;
+  res1->location = "ro";
+  res1->priority = 50;
   SharedHandle<MetalinkResource> res2(new MetalinkResource());
   res2->url = "http://myhost/aria2.tar.bz2";
   res2->type = MetalinkResource::TYPE_HTTP;
-  res2->location = "AT";
-  res2->preference = 100;
+  res2->location = "at";
+  res2->priority = 1;
   SharedHandle<MetalinkResource> res3(new MetalinkResource());
   res3->url = "http://myhost/aria2.torrent";
   res3->type = MetalinkResource::TYPE_BITTORRENT;
-  res3->location = "AL";
-  res3->preference = 60;
+  res3->location = "al";
+  res3->priority = 40;
   SharedHandle<MetalinkResource> res4(new MetalinkResource());
   res4->url = "http://myhost/aria2.ext";
   res4->type = MetalinkResource::TYPE_NOT_SUPPORTED;
-  res4->location = "AD";
-  res4->preference = 10;
+  res4->location = "ad";
+  res4->priority = 90;
   SharedHandle<MetalinkResource> res5(new MetalinkResource());
   res5->url = "https://myhost/aria2.tar.bz2";
   res5->type = MetalinkResource::TYPE_HTTPS;
-  res5->location = "JP";
-  res5->preference = 90;
+  res5->location = "jp";
+  res5->priority = 10;
 
   entry->resources.push_back(res1);
   entry->resources.push_back(res2);
@@ -93,49 +95,49 @@ void MetalinkEntryTest::testDropUnsupportedResource() {
 #endif // ENABLE_SSL
 }
 
-void MetalinkEntryTest::testReorderResourcesByPreference() {
+void MetalinkEntryTest::testReorderResourcesByPriority() {
   SharedHandle<MetalinkEntry> entry(createTestEntry());
   
-  entry->reorderResourcesByPreference();
+  entry->reorderResourcesByPriority();
 
-  CPPUNIT_ASSERT_EQUAL(100, entry->resources.at(0)->preference);
-  CPPUNIT_ASSERT_EQUAL(90, entry->resources.at(1)->preference);
-  CPPUNIT_ASSERT_EQUAL(60, entry->resources.at(2)->preference);
-  CPPUNIT_ASSERT_EQUAL(50, entry->resources.at(3)->preference);
-  CPPUNIT_ASSERT_EQUAL(10, entry->resources.at(4)->preference);
+  CPPUNIT_ASSERT_EQUAL(1, entry->resources.at(0)->priority);
+  CPPUNIT_ASSERT_EQUAL(10, entry->resources.at(1)->priority);
+  CPPUNIT_ASSERT_EQUAL(40, entry->resources.at(2)->priority);
+  CPPUNIT_ASSERT_EQUAL(50, entry->resources.at(3)->priority);
+  CPPUNIT_ASSERT_EQUAL(90, entry->resources.at(4)->priority);
 }
 
-void MetalinkEntryTest::testSetLocationPreference()
+void MetalinkEntryTest::testSetLocationPriority()
 {
   SharedHandle<MetalinkEntry> entry(createTestEntry());
 
-  const char* locationsSrc[] = { "jp", "al", "RO" };
+  const char* locationsSrc[] = { "jp", "al", "ro" };
 
   std::deque<std::string> locations(&locationsSrc[0], &locationsSrc[3]);
 
-  entry->setLocationPreference(locations, 100);
-
-  CPPUNIT_ASSERT_EQUAL(std::string("RO"), entry->resources[0]->location);
-  CPPUNIT_ASSERT_EQUAL(150, entry->resources[0]->preference);
-  CPPUNIT_ASSERT_EQUAL(std::string("AT"), entry->resources[1]->location);
-  CPPUNIT_ASSERT_EQUAL(100, entry->resources[1]->preference);
-  CPPUNIT_ASSERT_EQUAL(std::string("AL"), entry->resources[2]->location);
-  CPPUNIT_ASSERT_EQUAL(160, entry->resources[2]->preference);
-  CPPUNIT_ASSERT_EQUAL(std::string("AD"), entry->resources[3]->location);
-  CPPUNIT_ASSERT_EQUAL(10, entry->resources[3]->preference);
-  CPPUNIT_ASSERT_EQUAL(std::string("JP"), entry->resources[4]->location);
-  CPPUNIT_ASSERT_EQUAL(190, entry->resources[4]->preference);
+  entry->setLocationPriority(locations, -100);
+
+  CPPUNIT_ASSERT_EQUAL(std::string("ro"), entry->resources[0]->location);
+  CPPUNIT_ASSERT_EQUAL(-50, entry->resources[0]->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("at"), entry->resources[1]->location);
+  CPPUNIT_ASSERT_EQUAL(1, entry->resources[1]->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("al"), entry->resources[2]->location);
+  CPPUNIT_ASSERT_EQUAL(-60, entry->resources[2]->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("ad"), entry->resources[3]->location);
+  CPPUNIT_ASSERT_EQUAL(90, entry->resources[3]->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("jp"), entry->resources[4]->location);
+  CPPUNIT_ASSERT_EQUAL(-90, entry->resources[4]->priority);
 }
 
-void MetalinkEntryTest::testSetProtocolPreference()
+void MetalinkEntryTest::testSetProtocolPriority()
 {
   SharedHandle<MetalinkEntry> entry(createTestEntry());
-  entry->setProtocolPreference("http", 1);
-  CPPUNIT_ASSERT_EQUAL(50, entry->resources[0]->preference); // ftp
-  CPPUNIT_ASSERT_EQUAL(101, entry->resources[1]->preference); // http, +1
-  CPPUNIT_ASSERT_EQUAL(60, entry->resources[2]->preference); // bittorrent
-  CPPUNIT_ASSERT_EQUAL(10, entry->resources[3]->preference); // not supported
-  CPPUNIT_ASSERT_EQUAL(90, entry->resources[4]->preference); // https
+  entry->setProtocolPriority("http", -1);
+  CPPUNIT_ASSERT_EQUAL(50, entry->resources[0]->priority); // ftp
+  CPPUNIT_ASSERT_EQUAL(0, entry->resources[1]->priority); // http, -1
+  CPPUNIT_ASSERT_EQUAL(40, entry->resources[2]->priority); // bittorrent
+  CPPUNIT_ASSERT_EQUAL(90, entry->resources[3]->priority); // not supported
+  CPPUNIT_ASSERT_EQUAL(10, entry->resources[4]->priority); // https
 }
 
 } // namespace aria2

+ 37 - 5
test/MetalinkParserControllerTest.cc

@@ -1,4 +1,7 @@
 #include "MetalinkParserController.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
 #include "Metalinker.h"
 #include "MetalinkEntry.h"
 #include "MetalinkResource.h"
@@ -8,7 +11,6 @@
 # include "ChunkChecksum.h"
 #endif // ENABLE_MESSAGE_DIGEST
 #include "Signature.h"
-#include <cppunit/extensions/HelperMacros.h>
 
 namespace aria2 {
 
@@ -20,6 +22,7 @@ class MetalinkParserControllerTest:public CppUnit::TestFixture {
 #ifdef ENABLE_MESSAGE_DIGEST
   CPPUNIT_TEST(testChecksumTransaction);
   CPPUNIT_TEST(testChunkChecksumTransaction);
+  CPPUNIT_TEST(testChunkChecksumTransactionV4);
 #endif // ENABLE_MESSAGE_DIGEST
   CPPUNIT_TEST(testSignatureTransaction);
 
@@ -36,6 +39,7 @@ public:
 #ifdef ENABLE_MESSAGE_DIGEST
   void testChecksumTransaction();
   void testChunkChecksumTransaction();
+  void testChunkChecksumTransactionV4();
 #endif // ENABLE_MESSAGE_DIGEST
   void testSignatureTransaction();
 };
@@ -62,8 +66,8 @@ void MetalinkParserControllerTest::testEntryTransaction()
     CPPUNIT_ASSERT_EQUAL((uint64_t)(1024*1024ULL), e->file->getLength());
     CPPUNIT_ASSERT_EQUAL((off_t)0, e->file->getOffset());
     CPPUNIT_ASSERT_EQUAL(std::string("1.0"), e->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("ja_JP"), e->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux"), e->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("ja_JP"), e->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux"), e->oses[0]);
   }
   ctrl.newEntryTransaction();
   ctrl.cancelEntryTransaction();
@@ -78,7 +82,7 @@ void MetalinkParserControllerTest::testResourceTransaction()
   ctrl.setURLOfResource("http://mirror/aria2.tar.bz2");
   ctrl.setTypeOfResource("http");
   ctrl.setLocationOfResource("US");
-  ctrl.setPreferenceOfResource(100);
+  ctrl.setPriorityOfResource(100);
   ctrl.setMaxConnectionsOfResource(1);
   ctrl.commitEntryTransaction();
   {
@@ -88,7 +92,7 @@ void MetalinkParserControllerTest::testResourceTransaction()
     CPPUNIT_ASSERT_EQUAL(std::string("http://mirror/aria2.tar.bz2"), res->url);
     CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_HTTP, res->type);
     CPPUNIT_ASSERT_EQUAL(std::string("US"), res->location);
-    CPPUNIT_ASSERT_EQUAL(100, res->preference);
+    CPPUNIT_ASSERT_EQUAL(100, res->priority);
     CPPUNIT_ASSERT_EQUAL(1, res->maxConnections);
   }
   ctrl.newEntryTransaction();
@@ -151,6 +155,34 @@ void MetalinkParserControllerTest::testChunkChecksumTransaction()
   ctrl.commitEntryTransaction();
   CPPUNIT_ASSERT(ctrl.getResult()->entries[1]->chunkChecksum.isNull());
 }
+
+void MetalinkParserControllerTest::testChunkChecksumTransactionV4()
+{
+  MetalinkParserController ctrl;
+  ctrl.newEntryTransaction();
+  ctrl.newChunkChecksumTransactionV4();
+  ctrl.setTypeOfChunkChecksumV4("md5");
+  ctrl.setLengthOfChunkChecksumV4(256*1024);
+  ctrl.addHashOfChunkChecksumV4("hash1");
+  ctrl.addHashOfChunkChecksumV4("hash2");
+  ctrl.addHashOfChunkChecksumV4("hash3");
+  ctrl.commitEntryTransaction();
+  {
+    SharedHandle<Metalinker> m = ctrl.getResult();
+    SharedHandle<ChunkChecksum> md = m->entries.front()->chunkChecksum;
+    CPPUNIT_ASSERT_EQUAL(std::string("md5"), md->getAlgo());
+    CPPUNIT_ASSERT_EQUAL((size_t)256*1024, md->getChecksumLength());
+    CPPUNIT_ASSERT_EQUAL((size_t)3, md->countChecksum());
+    CPPUNIT_ASSERT_EQUAL(std::string("hash1"), md->getChecksums()[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("hash2"), md->getChecksums()[1]);
+    CPPUNIT_ASSERT_EQUAL(std::string("hash3"), md->getChecksums()[2]);
+  }
+  ctrl.newEntryTransaction();
+  ctrl.newChunkChecksumTransactionV4();
+  ctrl.cancelChunkChecksumTransactionV4();
+  ctrl.commitEntryTransaction();
+  CPPUNIT_ASSERT(ctrl.getResult()->entries[1]->chunkChecksum.isNull());
+}
 #endif // ENABLE_MESSAGE_DIGEST
 
 void MetalinkParserControllerTest::testSignatureTransaction()

+ 142 - 29
test/MetalinkProcessorTest.cc

@@ -22,6 +22,9 @@ namespace aria2 {
 class MetalinkProcessorTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(MetalinkProcessorTest);
+  CPPUNIT_TEST(testParseFileV4);
+  CPPUNIT_TEST(testParseFileV4_dirtraversal);
+  CPPUNIT_TEST(testParseFileV4_attrs);
   CPPUNIT_TEST(testParseFile);
   CPPUNIT_TEST(testParseFromBinaryStream);
   CPPUNIT_TEST(testMalformedXML);
@@ -39,10 +42,14 @@ class MetalinkProcessorTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testUnsupportedType_piece);
 #endif // ENABLE_MESSAGE_DIGEST
   CPPUNIT_TEST(testLargeFileSize);
+  CPPUNIT_TEST(testXmlPrefixV3);
   CPPUNIT_TEST_SUITE_END();
 private:
 
 public:
+  void testParseFileV4();
+  void testParseFileV4_dirtraversal();
+  void testParseFileV4_attrs();
   void testParseFile();
   void testParseFromBinaryStream();
   void testMalformedXML();
@@ -60,11 +67,89 @@ public:
   void testUnsupportedType_piece();
 #endif // ENABLE_MESSAGE_DIGEST
   void testLargeFileSize();
+  void testXmlPrefixV3();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION( MetalinkProcessorTest );
 
+void MetalinkProcessorTest::testParseFileV4()
+{
+  MetalinkProcessor proc;
+  SharedHandle<Metalinker> m = proc.parseFile("metalink4.xml");
+
+  SharedHandle<MetalinkEntry> e;
+  SharedHandle<MetalinkResource> r;
+
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->entries.size());
+  e = m->entries[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("example.ext"), e->getPath());
+  CPPUNIT_ASSERT_EQUAL((uint64_t)786430LL, e->getLength());
+  CPPUNIT_ASSERT_EQUAL(-1, e->maxConnections);
+#ifdef ENABLE_MESSAGE_DIGEST
+  CPPUNIT_ASSERT_EQUAL(std::string("80bc95fd391772fa61c91ed68567f0980bb45fd9"),
+		       e->checksum->getMessageDigest());
+  CPPUNIT_ASSERT(!e->checksum.isNull());
+  CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), e->checksum->getAlgo());
+  CPPUNIT_ASSERT(!e->chunkChecksum.isNull());
+  CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), e->chunkChecksum->getAlgo());
+  CPPUNIT_ASSERT_EQUAL((size_t)262144, e->chunkChecksum->getChecksumLength());
+  CPPUNIT_ASSERT_EQUAL((size_t)3, e->chunkChecksum->countChecksum());
+  CPPUNIT_ASSERT_EQUAL(std::string("metalinkhash1"),
+		       e->chunkChecksum->getChecksum(0));
+  CPPUNIT_ASSERT_EQUAL(std::string("metalinkhash2"),
+		       e->chunkChecksum->getChecksum(1));
+  CPPUNIT_ASSERT_EQUAL(std::string("metalinkhash3"),
+		       e->chunkChecksum->getChecksum(2));
+#endif // ENABLE_MESSAGE_DIGEST
+  CPPUNIT_ASSERT(!e->getSignature().isNull());
+  CPPUNIT_ASSERT_EQUAL(std::string("application/pgp-signature"),
+                       e->getSignature()->getType());
+  CPPUNIT_ASSERT_EQUAL(std::string("a signature"),
+		       e->getSignature()->getBody());
+
+  CPPUNIT_ASSERT_EQUAL((size_t)3, e->resources.size());
+  r = e->resources[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("ftp://ftp.example.com/example.ext"),
+		       r->url);
+  CPPUNIT_ASSERT_EQUAL(std::string("de"), r->location);
+  CPPUNIT_ASSERT_EQUAL(1, r->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("ftp"),
+		       MetalinkResource::getTypeString(r->type));
+  CPPUNIT_ASSERT_EQUAL(-1, r->maxConnections);
+
+  r = e->resources[2];
+  CPPUNIT_ASSERT_EQUAL(std::string("http://example.com/example.ext.torrent"),
+		       r->url);
+  CPPUNIT_ASSERT_EQUAL(2, r->priority);
+  CPPUNIT_ASSERT_EQUAL(std::string("bittorrent"),
+		       MetalinkResource::getTypeString(r->type));
+  CPPUNIT_ASSERT_EQUAL(-1, r->maxConnections);
+}
+
+void MetalinkProcessorTest::testParseFileV4_dirtraversal()
+{
+  MetalinkProcessor proc;
+  SharedHandle<Metalinker> m = proc.parseFile("metalink4-dirtraversal.xml");
+  CPPUNIT_ASSERT_EQUAL((size_t)0, m->entries.size());
+}
+
+void MetalinkProcessorTest::testParseFileV4_attrs()
+{
+  MetalinkProcessor proc;
+  SharedHandle<Metalinker> m = proc.parseFile("metalink4-attrs.xml");
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->entries.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)6, m->entries[0]->resources.size());
+  std::deque<SharedHandle<MetalinkResource> > resources =
+    m->entries[0]->resources;
+  CPPUNIT_ASSERT_EQUAL(999999, resources[0]->priority);
+  CPPUNIT_ASSERT_EQUAL(999999, resources[1]->priority);
+  CPPUNIT_ASSERT_EQUAL(999999, resources[2]->priority);
+  CPPUNIT_ASSERT_EQUAL(999999, resources[3]->priority);
+  CPPUNIT_ASSERT_EQUAL(999999, resources[4]->priority);
+  CPPUNIT_ASSERT_EQUAL(999999, resources[5]->priority);
+}
+
 void MetalinkProcessorTest::testParseFile()
 {
   MetalinkProcessor proc;
@@ -77,8 +162,8 @@ void MetalinkProcessorTest::testParseFile()
     CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.5.2.tar.bz2"), entry1->getPath());
     CPPUNIT_ASSERT_EQUAL((uint64_t)0ULL, entry1->getLength());
     CPPUNIT_ASSERT_EQUAL(std::string("0.5.2"), entry1->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), entry1->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), entry1->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), entry1->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), entry1->oses[0]);
     CPPUNIT_ASSERT_EQUAL(1, entry1->maxConnections);
 #ifdef ENABLE_MESSAGE_DIGEST
     CPPUNIT_ASSERT_EQUAL(std::string("a96cf3f0266b91d87d5124cf94326422800b627d"),
@@ -104,8 +189,8 @@ void MetalinkProcessorTest::testParseFile()
     std::deque<SharedHandle<MetalinkResource> >::iterator resourceItr1 = entry1->resources.begin();
     SharedHandle<MetalinkResource> resource1 = *resourceItr1;
     CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_FTP, resource1->type);
-    CPPUNIT_ASSERT_EQUAL(std::string("JP"), resource1->location);
-    CPPUNIT_ASSERT_EQUAL(100, resource1->preference);
+    CPPUNIT_ASSERT_EQUAL(std::string("jp"), resource1->location);
+    CPPUNIT_ASSERT_EQUAL(1, resource1->priority);
     CPPUNIT_ASSERT_EQUAL(std::string("ftp://ftphost/aria2-0.5.2.tar.bz2"),
                          resource1->url);
     CPPUNIT_ASSERT_EQUAL(1, resource1->maxConnections);
@@ -113,8 +198,8 @@ void MetalinkProcessorTest::testParseFile()
     resourceItr1++;
     SharedHandle<MetalinkResource> resource2 = *resourceItr1;
     CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_HTTP, resource2->type);
-    CPPUNIT_ASSERT_EQUAL(std::string("US"), resource2->location);
-    CPPUNIT_ASSERT_EQUAL(100, resource2->preference);
+    CPPUNIT_ASSERT_EQUAL(std::string("us"), resource2->location);
+    CPPUNIT_ASSERT_EQUAL(1, resource2->priority);
     CPPUNIT_ASSERT_EQUAL(std::string("http://httphost/aria2-0.5.2.tar.bz2"),
                          resource2->url);
     CPPUNIT_ASSERT_EQUAL(-1, resource2->maxConnections);
@@ -125,8 +210,8 @@ void MetalinkProcessorTest::testParseFile()
     CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.5.1.tar.bz2"), entry2->getPath());
     CPPUNIT_ASSERT_EQUAL((uint64_t)345689ULL, entry2->getLength());
     CPPUNIT_ASSERT_EQUAL(std::string("0.5.1"), entry2->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("ja-JP"), entry2->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux-m68k"), entry2->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("ja-JP"), entry2->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux-m68k"), entry2->oses[0]);
     CPPUNIT_ASSERT_EQUAL(-1, entry2->maxConnections);
 #ifdef ENABLE_MESSAGE_DIGEST
     CPPUNIT_ASSERT_EQUAL(std::string("4c255b0ed130f5ea880f0aa061c3da0487e251cc"),
@@ -193,7 +278,7 @@ void MetalinkProcessorTest::testMalformedXML()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink><files></file></metalink>");
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\"><files></file></metalink>");
 
   try {
     SharedHandle<Metalinker> m = proc.parseFromBinaryStream(dw);
@@ -207,7 +292,7 @@ void MetalinkProcessorTest::testMalformedXML2()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink><files></files>");
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\"><files></files>");
 
   try {
     SharedHandle<Metalinker> m = proc.parseFromBinaryStream(dw);
@@ -221,7 +306,7 @@ void MetalinkProcessorTest::testBadSize()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2-0.5.2.tar.bz2\">"
                 "  <size>abc</size>"
@@ -240,8 +325,8 @@ void MetalinkProcessorTest::testBadSize()
     CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.5.2.tar.bz2"), e->getPath());
     CPPUNIT_ASSERT_EQUAL((uint64_t)0ULL, e->getLength());
     CPPUNIT_ASSERT_EQUAL(std::string("0.5.2"), e->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), e->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), e->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), e->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), e->oses[0]);
 
   } catch(Exception& e) {
     CPPUNIT_FAIL(e.stackTrace());
@@ -252,7 +337,7 @@ void MetalinkProcessorTest::testBadMaxConn()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2-0.5.2.tar.bz2\">"
                 "  <size>43743838</size>"
@@ -279,7 +364,7 @@ void MetalinkProcessorTest::testNoName()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file>"
                 "  <size>1024</size>"
@@ -311,7 +396,7 @@ void MetalinkProcessorTest::testBadURLPrefs()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2-0.5.2.tar.bz2\">"
                 "  <size>43743838</size>"
@@ -319,7 +404,8 @@ void MetalinkProcessorTest::testBadURLPrefs()
                 "  <language>en-US</language>"
                 "  <os>Linux-x86</os>"
                 "  <resources>"
-                "    <url type=\"ftp\" maxconnections=\"1\" preference=\"xyz\" location=\"JP\">ftp://mirror/</url>"
+                "    <url type=\"ftp\" maxconnections=\"1\" preference=\"xyz\""
+                "         location=\"jp\">ftp://mirror/</url>"
                 "  </resources>"                
                 "</file>"
                 "</files>"
@@ -330,9 +416,9 @@ void MetalinkProcessorTest::testBadURLPrefs()
     SharedHandle<MetalinkEntry> e = m->entries[0];
     SharedHandle<MetalinkResource> r = e->resources[0];
     CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_FTP, r->type);
-    CPPUNIT_ASSERT_EQUAL(0, r->preference);
+    CPPUNIT_ASSERT_EQUAL(MetalinkResource::getLowestPriority(), r->priority);
     CPPUNIT_ASSERT_EQUAL(1, r->maxConnections);
-    CPPUNIT_ASSERT_EQUAL(std::string("JP"), r->location);
+    CPPUNIT_ASSERT_EQUAL(std::string("jp"), r->location);
   } catch(Exception& e) {
     CPPUNIT_FAIL(e.stackTrace());
   }
@@ -342,7 +428,7 @@ void MetalinkProcessorTest::testBadURLMaxConn()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2-0.5.2.tar.bz2\">"
                 "  <size>43743838</size>"
@@ -350,7 +436,9 @@ void MetalinkProcessorTest::testBadURLMaxConn()
                 "  <language>en-US</language>"
                 "  <os>Linux-x86</os>"
                 "  <resources>"
-                "    <url maxconnections=\"xyz\" type=\"ftp\" preference=\"100\" location=\"JP\">ftp://mirror/</url>"
+                "    <url maxconnections=\"xyz\" type=\"ftp\""
+                "         preference=\"100\""
+                "         location=\"jp\">ftp://mirror/</url>"
                 "  </resources>"                
                 "</file>"
                 "</files>"
@@ -361,9 +449,9 @@ void MetalinkProcessorTest::testBadURLMaxConn()
     SharedHandle<MetalinkEntry> e = m->entries[0];
     SharedHandle<MetalinkResource> r = e->resources[0];
     CPPUNIT_ASSERT_EQUAL(MetalinkResource::TYPE_FTP, r->type);
-    CPPUNIT_ASSERT_EQUAL(100, r->preference);
+    CPPUNIT_ASSERT_EQUAL(1, r->priority);
     CPPUNIT_ASSERT_EQUAL(-1, r->maxConnections);
-    CPPUNIT_ASSERT_EQUAL(std::string("JP"), r->location);
+    CPPUNIT_ASSERT_EQUAL(std::string("jp"), r->location);
   } catch(Exception& e) {
     CPPUNIT_FAIL(e.stackTrace());
   }
@@ -374,7 +462,7 @@ void MetalinkProcessorTest::testUnsupportedType()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2-0.5.2.tar.bz2\">"
                 "  <size>43743838</size>"
@@ -409,7 +497,7 @@ void MetalinkProcessorTest::testMultiplePieces()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2.tar.bz2\">"
                 "  <verification>"
@@ -439,7 +527,7 @@ void MetalinkProcessorTest::testBadPieceNo()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2.tar.bz2\">"
                 "  <verification>"
@@ -472,7 +560,7 @@ void MetalinkProcessorTest::testBadPieceLength()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2.tar.bz2\">"
                 "  <verification>"
@@ -504,7 +592,7 @@ void MetalinkProcessorTest::testUnsupportedType_piece()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"aria2.tar.bz2\">"
                 "  <verification>"
@@ -537,7 +625,7 @@ void MetalinkProcessorTest::testLargeFileSize()
 {
   MetalinkProcessor proc;
   SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
-  dw->setString("<metalink>"
+  dw->setString("<metalink version=\"3.0\" xmlns=\"http://www.metalinker.org/\">"
                 "<files>"
                 "<file name=\"dvd.iso\">"
                 "  <size>9223372036854775807</size>"
@@ -557,4 +645,29 @@ void MetalinkProcessorTest::testLargeFileSize()
   }
 }
 
+void MetalinkProcessorTest::testXmlPrefixV3()
+{
+  MetalinkProcessor proc;
+  SharedHandle<ByteArrayDiskWriter> dw(new ByteArrayDiskWriter());
+  dw->setString("<m:metalink version=\"3.0\" xmlns:m=\"http://www.metalinker.org/\">"
+                "<m:files>"
+                "<m:file name=\"dvd.iso\">"
+                "  <m:size>9223372036854775807</m:size>"
+                "  <m:resources>"
+                "    <m:url type=\"http\">ftp://mirror/</m:url>"
+                "  </m:resources>"                
+                "</m:file>"
+                "</m:files>"
+                "</m:metalink>");
+
+  try {
+    SharedHandle<Metalinker> m = proc.parseFromBinaryStream(dw);
+    CPPUNIT_ASSERT_EQUAL((size_t)1, m->entries.size());
+    SharedHandle<MetalinkEntry> e = m->entries[0];
+    CPPUNIT_ASSERT_EQUAL((uint64_t)9223372036854775807ULL, e->getLength());
+  } catch(Exception& e) {
+    CPPUNIT_FAIL(e.stackTrace());
+  }
+}
+
 } // namespace aria2

+ 8 - 8
test/MetalinkerTest.cc

@@ -27,12 +27,12 @@ void MetalinkerTest::testQueryEntry() {
   SharedHandle<Metalinker> metalinker(new Metalinker());
   SharedHandle<MetalinkEntry> entry1(new MetalinkEntry());
   entry1->version = "0.5.2";
-  entry1->language = "en-US";
-  entry1->os = "Linux-x86";
+  entry1->languages.push_back("en-US");
+  entry1->oses.push_back("Linux-x86");
   SharedHandle<MetalinkEntry> entry2(new MetalinkEntry());
   entry2->version = "0.5.1";
-  entry2->language = "ja-JP";
-  entry2->os = "Linux-m68k";
+  entry2->languages.push_back("ja-JP");
+  entry2->oses.push_back("Linux-m68k");
   metalinker->entries.push_back(entry1);
   metalinker->entries.push_back(entry2);
 
@@ -48,8 +48,8 @@ void MetalinkerTest::testQueryEntry() {
     metalinker->queryEntry(result, version, language, os);
     CPPUNIT_ASSERT_EQUAL((size_t)1, result.size());
     CPPUNIT_ASSERT_EQUAL(std::string("0.5.1"), result.at(0)->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("ja-JP"), result.at(0)->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux-m68k"), result.at(0)->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("ja-JP"), result.at(0)->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux-m68k"), result.at(0)->oses[0]);
   }
   version = "0.6.0";
   language = "";
@@ -68,8 +68,8 @@ void MetalinkerTest::testQueryEntry() {
     metalinker->queryEntry(result, version, language, os);
     CPPUNIT_ASSERT_EQUAL((size_t)1, result.size());
     CPPUNIT_ASSERT_EQUAL(std::string("0.5.2"), result.at(0)->version);
-    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), result.at(0)->language);
-    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), result.at(0)->os);
+    CPPUNIT_ASSERT_EQUAL(std::string("en-US"), result.at(0)->languages[0]);
+    CPPUNIT_ASSERT_EQUAL(std::string("Linux-x86"), result.at(0)->oses[0]);
   }
 }
 

+ 19 - 0
test/UtilTest.cc

@@ -61,6 +61,7 @@ class UtilTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testApplyDir);
   CPPUNIT_TEST(testFixTaintedBasename);
   CPPUNIT_TEST(testIsNumericHost);
+  CPPUNIT_TEST(testDetectDirTraversal);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -110,6 +111,7 @@ public:
   void testApplyDir();
   void testFixTaintedBasename();
   void testIsNumericHost();
+  void testDetectDirTraversal();
 };
 
 
@@ -1015,4 +1017,21 @@ void UtilTest::testIsNumericHost()
   CPPUNIT_ASSERT(util::isNumericHost("::1"));
 }
 
+void UtilTest::testDetectDirTraversal()
+{
+  CPPUNIT_ASSERT(util::detectDirTraversal("/foo"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("./foo"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("../foo"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("foo/../bar"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("foo/./bar"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("foo/."));
+  CPPUNIT_ASSERT(util::detectDirTraversal("foo/.."));
+  CPPUNIT_ASSERT(util::detectDirTraversal("."));
+  CPPUNIT_ASSERT(util::detectDirTraversal(".."));
+  CPPUNIT_ASSERT(util::detectDirTraversal("/"));
+  CPPUNIT_ASSERT(util::detectDirTraversal("foo/"));
+  CPPUNIT_ASSERT(!util::detectDirTraversal("foo/bar"));
+  CPPUNIT_ASSERT(!util::detectDirTraversal("foo"));
+}
+
 } // namespace aria2

+ 11 - 0
test/metalink4-attrs.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink xmlns="urn:ietf:params:xml:ns:metalink">
+  <file name="priority/example.ext">
+    <url location="de" priority="0">ftp://ftp.example.com/example.ext</url>
+    <url location="fr" priority="">http://example.com/example.ext</url>
+    <url location="jp" priority="1000000">http://example.org/example.ext</url>
+    <metaurl mediatype="torrent" priority="0">http://example.com/example.ext.torrent</metaurl>
+    <metaurl mediatype="torrent" priority="">http://example.org/example.ext.torrent</metaurl>
+    <metaurl mediatype="torrent" priority="1000000">http://example.net/example.ext.torrent</metaurl>
+  </file>
+</metalink>

+ 24 - 0
test/metalink4-dirtraversal.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink xmlns="urn:ietf:params:xml:ns:metalink">
+  <file name="/example1.ext">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name="./example2.ext">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name="../example3.ext">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name="dir/../example4.ext">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name="example5.ext/..">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name=".">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+  <file name="..">
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+  </file>
+</metalink>

+ 21 - 0
test/metalink4.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metalink xmlns="urn:ietf:params:xml:ns:metalink">
+  <published>2009-05-15T12:23:23Z</published>
+  <file name="example.ext">
+    <size>786430</size>
+    <identity>Example</identity>
+    <version>1.0</version>
+    <language>en</language>
+    <description>A description of the example file for download.</description>
+    <hash type="sha-1">80bc95fd391772fa61c91ed68567f0980bb45fd9</hash>
+    <pieces length="262144" type="sha-1">
+      <hash>metalinkhash1</hash>
+      <hash>metalinkhash2</hash>
+      <hash>metalinkhash3</hash>
+    </pieces>
+    <url location="de" priority="1">ftp://ftp.example.com/example.ext</url>
+    <url location="fr" priority="1">http://example.com/example.ext</url>
+    <metaurl mediatype="torrent" priority="2">http://example.com/example.ext.torrent</metaurl>
+    <signature mediatype="application/pgp-signature">a signature</signature>
+  </file>
+</metalink>