Bladeren bron

2006-07-03 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	To add Metalink support(http/ftp only):

	* src/AbstractCommand.h
	(tryReserved): New function.
	* src/AbstractCommand.cc
	(execute): Call tryReserved().
	(tryReserved): New function.
	* src/Request.h
	(Requests): New type definition.
	* src/SegmentMan.h
	(reserved): New variable.
	* src/Util.h
	(fileChecksum): New function.
	(toUpper): New function.
	(toLower): New function.
	* src/Util.cc
	(messageDigest.h): Included.
	(trim): Trim \r\n\t.
	(fileChecksum): New function.
	(toUpper): New function.
	(toLower): New function.
	* src/main.cc
	(normalDownload): New function.
	(main): Added 2 command-line options: metalink-file,
	metalink-connection. Their usage has not been written yet.
	* src/MetalinkProcessor.h: New class.
	* src/Xml2MetalinkProcessor.h: New class.
	* src/Xml2MetalinkProcessor.cc: New class.
	* src/MetalinkEntry.h: New class.
	* src/MetalinkEntry.cc: New class.
	* src/MetalinkResource.h: New class.
	* src/MetalinkResource.cc: New class.
	
	To add md5 message digest checking:

	* src/messageDigest.h: Rewritten.
	* src/MultiDiskWriter.cc: Updated according to the changes in
	messageDigest.h.
	* src/ShaVisitor.cc: Updated according to the changes in
	messageDigest.h.
	* src/Util.cc: Updated according to the changes in 
messageDigest.h.
	* src/AbstractDiskWriter.cc: Updated according to the changes in
	messageDigest.h.
	
	To fix a bug that causes segfault when the payload length in 
peer
	message is less than 0:

	* src/PeerConnection.cc:
	(receiveMessage): Fixed the bug.
	* src/PeerMessageUtil.cc
	(checkLength): Throw an exception if length is less than or 
equals to
	0.
	
	To add new interfaces to Base64 encoding/decoding:

	* src/Base64.h
	(part_encode): Changed the method signature.
	(encode): New function(overload).
	(decode): New function(overload).
	* src/Base64.cc
	(part_encode): Rewritten.
	(encode): Rewritten.
	(encode): New function(overload).

	To prevent a peer to download same piece if there is an error in
	checksum:

	* src/PieceMessage.cc
	(receivedAction): Call peerInteraction->abortPiece().
Tatsuhiro Tsujikawa 19 jaren geleden
bovenliggende
commit
78eff23254

+ 72 - 0
ChangeLog

@@ -1,3 +1,75 @@
+2006-07-03  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	To add Metalink support(http/ftp only):
+
+	* src/AbstractCommand.h
+	(tryReserved): New function.
+	* src/AbstractCommand.cc
+	(execute): Call tryReserved().
+	(tryReserved): New function.
+	* src/Request.h
+	(Requests): New type definition.
+	* src/SegmentMan.h
+	(reserved): New variable.
+	* src/Util.h
+	(fileChecksum): New function.
+	(toUpper): New function.
+	(toLower): New function.
+	* src/Util.cc
+	(messageDigest.h): Included.
+	(trim): Trim \r\n\t.
+	(fileChecksum): New function.
+	(toUpper): New function.
+	(toLower): New function.
+	* src/main.cc
+	(normalDownload): New function.
+	(main): Added 2 command-line options: metalink-file,
+	metalink-connection. Their usage has not been written yet.
+	* src/MetalinkProcessor.h: New class.
+	* src/Xml2MetalinkProcessor.h: New class.
+	* src/Xml2MetalinkProcessor.cc: New class.
+	* src/MetalinkEntry.h: New class.
+	* src/MetalinkEntry.cc: New class.
+	* src/MetalinkResource.h: New class.
+	* src/MetalinkResource.cc: New class.
+	
+	To add md5 message digest checking:
+
+	* src/messageDigest.h: Rewritten.
+	* src/MultiDiskWriter.cc: Updated according to the changes in
+	messageDigest.h.
+	* src/ShaVisitor.cc: Updated according to the changes in
+	messageDigest.h.
+	* src/Util.cc: Updated according to the changes in messageDigest.h.
+	* src/AbstractDiskWriter.cc: Updated according to the changes in
+	messageDigest.h.
+	
+	To fix a bug that causes segfault when the payload length in peer
+	message is less than 0:
+
+	* src/PeerConnection.cc:
+	(receiveMessage): Fixed the bug.
+	* src/PeerMessageUtil.cc
+	(checkLength): Throw an exception if length is less than or equals to
+	0.
+	
+	To add new interfaces to Base64 encoding/decoding:
+
+	* src/Base64.h
+	(part_encode): Changed the method signature.
+	(encode): New function(overload).
+	(decode): New function(overload).
+	* src/Base64.cc
+	(part_encode): Rewritten.
+	(encode): Rewritten.
+	(encode): New function(overload).
+
+	To prevent a peer to download same piece if there is an error in
+	checksum:
+
+	* src/PieceMessage.cc
+	(receivedAction): Call peerInteraction->abortPiece().
+	
 2006-06-25  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
         To fix the bug that causes same have message is sent many times to

+ 3 - 0
Makefile.in

@@ -163,6 +163,9 @@ USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
 USE_NLS = @USE_NLS@
 VERSION = @VERSION@
 XGETTEXT = @XGETTEXT@
+XML2_CONFIG = @XML2_CONFIG@
+XML_CPPFLAGS = @XML_CPPFLAGS@
+XML_LIBS = @XML_LIBS@
 YACC = @YACC@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@

+ 3 - 0
TODO

@@ -14,3 +14,6 @@
 * Add Mainline-compatible DHT support
 * Add Message stream encryption support
 * Refacturing HttpConnection and FtpConnection
+* Use EXIT_SUCCESS and EXIT_FAILURE
+* Query resource by location
+* Conditional compilation based on ENABLE_LIBXML2

+ 189 - 0
aclocal.m4

@@ -362,6 +362,195 @@ main ()
 
 dnl *-*wedit:notab*-*  Please keep this as the last line.
 
+# Configure paths for LIBXML2
+# Mike Hommey 2004-06-19
+# use CPPFLAGS instead of CFLAGS
+# Toshio Kuratomi 2001-04-21
+# Adapted from:
+# Configure paths for GLIB
+# Owen Taylor     97-11-3
+
+dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS
+dnl
+AC_DEFUN([AM_PATH_XML2],[ 
+AC_ARG_WITH(xml-prefix,
+            [  --with-xml-prefix=PFX   Prefix where libxml is installed (optional)],
+            xml_config_prefix="$withval", xml_config_prefix="")
+AC_ARG_WITH(xml-exec-prefix,
+            [  --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
+            xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
+AC_ARG_ENABLE(xmltest,
+              [  --disable-xmltest       Do not try to compile and run a test LIBXML program],,
+              enable_xmltest=yes)
+
+  if test x$xml_config_exec_prefix != x ; then
+     xml_config_args="$xml_config_args"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
+     fi
+  fi
+  if test x$xml_config_prefix != x ; then
+     xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_prefix/bin/xml2-config
+     fi
+  fi
+
+  AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
+  min_xml_version=ifelse([$1], ,2.0.0,[$1])
+  AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
+  no_xml=""
+  if test "$XML2_CONFIG" = "no" ; then
+    no_xml=yes
+  else
+    XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
+    XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
+    xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_xmltest" = "xyes" ; then
+      ac_save_CPPFLAGS="$CPPFLAGS"
+      ac_save_LIBS="$LIBS"
+      CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
+      LIBS="$XML_LIBS $LIBS"
+dnl
+dnl Now check if the installed libxml is sufficiently new.
+dnl (Also sanity checks the results of xml2-config to some extent)
+dnl
+      rm -f conf.xmltest
+      AC_TRY_RUN([
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlversion.h>
+
+int 
+main()
+{
+  int xml_major_version, xml_minor_version, xml_micro_version;
+  int major, minor, micro;
+  char *tmp_version;
+
+  system("touch conf.xmltest");
+
+  /* Capture xml2-config output via autoconf/configure variables */
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = (char *)strdup("$min_xml_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string from xml2-config\n", "$min_xml_version");
+     exit(1);
+   }
+   free(tmp_version);
+
+   /* Capture the version information from the header files */
+   tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
+   if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
+     printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
+     exit(1);
+   }
+   free(tmp_version);
+
+ /* Compare xml2-config output to the libxml headers */
+  if ((xml_major_version != $xml_config_major_version) ||
+      (xml_minor_version != $xml_config_minor_version) ||
+      (xml_micro_version != $xml_config_micro_version))
+    {
+      printf("*** libxml header files (version %d.%d.%d) do not match\n",
+         xml_major_version, xml_minor_version, xml_micro_version);
+      printf("*** xml2-config (version %d.%d.%d)\n",
+         $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
+      return 1;
+    } 
+/* Compare the headers to the library to make sure we match */
+  /* Less than ideal -- doesn't provide us with return value feedback, 
+   * only exits if there's a serious mismatch between header and library.
+   */
+    LIBXML_TEST_VERSION;
+
+    /* Test that the library is greater than our minimum version */
+    if ((xml_major_version > major) ||
+        ((xml_major_version == major) && (xml_minor_version > minor)) ||
+        ((xml_major_version == major) && (xml_minor_version == minor) &&
+        (xml_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
+               xml_major_version, xml_minor_version, xml_micro_version);
+        printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
+           major, minor, micro);
+        printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
+        printf("*** correct copy of xml2-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+    }
+  return 1;
+}
+],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CPPFLAGS="$ac_save_CPPFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+
+  if test "x$no_xml" = x ; then
+     AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$XML2_CONFIG" = "no" ; then
+       echo "*** The xml2-config script installed by LIBXML could not be found"
+       echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the XML2_CONFIG environment variable to the"
+       echo "*** full path to xml2-config."
+     else
+       if test -f conf.xmltest ; then
+        :
+       else
+          echo "*** Could not run libxml test program, checking why..."
+          CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
+          LIBS="$LIBS $XML_LIBS"
+          AC_TRY_LINK([
+#include <libxml/xmlversion.h>
+#include <stdio.h>
+],      [ LIBXML_TEST_VERSION; return 0;],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
+          echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+          echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
+          echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
+          echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
+          CPPFLAGS="$ac_save_CPPFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+
+     XML_CPPFLAGS=""
+     XML_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(XML_CPPFLAGS)
+  AC_SUBST(XML_LIBS)
+  rm -f conf.xmltest
+])
+
 # Copyright (C) 2002, 2003, 2005  Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation

+ 6 - 0
config.h.in

@@ -11,6 +11,9 @@
 /* Define to 1 if BitTorrent support is enabled. */
 #undef ENABLE_BITTORRENT
 
+/* Define to 1 if Metalink support is enabled. */
+#undef ENABLE_METALINK
+
 /* Define to 1 if translation of program messages to the user's native
    language is requested. */
 #undef ENABLE_NLS
@@ -114,6 +117,9 @@
 /* Define to 1 if you have openssl. */
 #undef HAVE_LIBSSL
 
+/* Define to 1 if you have libxml2. */
+#undef HAVE_LIBXML2
+
 /* Define to 1 if you have the <limits.h> header file. */
 #undef HAVE_LIMITS_H
 

+ 338 - 2
configure

@@ -311,7 +311,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CPPUNIT_CONFIG CPPUNIT_CFLAGS CPPUNIT_LIBS localedir CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB YACC LIBGNUTLS_CONFIG LIBGNUTLS_CFLAGS LIBGNUTLS_LIBS LIBGCRYPT_CONFIG LIBGCRYPT_CFLAGS LIBGCRYPT_LIBS OPENSSL_LIBS OPENSSL_CFLAGS ALLOCA CPP EGREP MKINSTALLDIRS USE_NLS MSGFMT GMSGFMT XGETTEXT MSGMERGE build build_cpu build_vendor build_os host host_cpu host_vendor host_os GLIBC21 LIBICONV LTLIBICONV INTLBISON BUILD_INCLUDED_LIBINTL USE_INCLUDED_LIBINTL CATOBJEXT DATADIRNAME INSTOBJEXT GENCAT INTLOBJS INTL_LIBTOOL_SUFFIX_PREFIX INTLLIBS LIBINTL LTLIBINTL POSUB LIBOBJS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CPPUNIT_CONFIG CPPUNIT_CFLAGS CPPUNIT_LIBS localedir CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB YACC XML2_CONFIG XML_CPPFLAGS XML_LIBS LIBGNUTLS_CONFIG LIBGNUTLS_CFLAGS LIBGNUTLS_LIBS LIBGCRYPT_CONFIG LIBGCRYPT_CFLAGS LIBGCRYPT_LIBS OPENSSL_LIBS OPENSSL_CFLAGS ALLOCA CPP EGREP MKINSTALLDIRS USE_NLS MSGFMT GMSGFMT XGETTEXT MSGMERGE build build_cpu build_vendor build_os host host_cpu host_vendor host_os GLIBC21 LIBICONV LTLIBICONV INTLBISON BUILD_INCLUDED_LIBINTL USE_INCLUDED_LIBINTL CATOBJEXT DATADIRNAME INSTOBJEXT GENCAT INTLOBJS INTL_LIBTOOL_SUFFIX_PREFIX INTLLIBS LIBINTL LTLIBINTL POSUB LIBOBJS LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -863,6 +863,7 @@ Optional Features:
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
   --disable-dependency-tracking  speeds up one-time build
   --enable-dependency-tracking   do not reject slow dependency extractors
+  --disable-xmltest       Do not try to compile and run a test LIBXML program
   --disable-nls           do not use Native Language Support
   --disable-rpath         do not hardcode runtime library paths
 
@@ -873,6 +874,9 @@ Optional Packages:
   --with-cppunit-exec-prefix=PFX  Exec prefix where CppUnit is installed (optional)
   --with-gnutls              use gnutls library if installed. Default: yes
   --with-openssl             use openssl library if installed. Default: yes
+  --with-libxml2             use libxml2 library if installed. Default: yes
+  --with-xml-prefix=PFX   Prefix where libxml is installed (optional)
+  --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)
   --with-libgnutls-prefix=PFX   Prefix where libgnutls is installed (optional)
   --with-libgcrypt-prefix=PFX
                           prefix where LIBGCRYPT is installed (optional)
@@ -1934,6 +1938,14 @@ else
   with_openssl=yes
 fi;
 
+# Check whether --with-libxml2 or --without-libxml2 was given.
+if test "${with_libxml2+set}" = set; then
+  withval="$with_libxml2"
+  with_libxml2=$enableval
+else
+  with_libxml2=yes
+fi;
+
 
 # Checks for programs.
 ac_ext=cc
@@ -3696,8 +3708,321 @@ test -n "$YACC" || YACC="yacc"
 
 
 # Checks for libraries.
-if test "x$with_gnutls" = "xyes"; then
+if test "x$with_libxml2" = "xyes"; then
+
+
+
+# Check whether --with-xml-prefix or --without-xml-prefix was given.
+if test "${with_xml_prefix+set}" = set; then
+  withval="$with_xml_prefix"
+  xml_config_prefix="$withval"
+else
+  xml_config_prefix=""
+fi;
+
+# Check whether --with-xml-exec-prefix or --without-xml-exec-prefix was given.
+if test "${with_xml_exec_prefix+set}" = set; then
+  withval="$with_xml_exec_prefix"
+  xml_config_exec_prefix="$withval"
+else
+  xml_config_exec_prefix=""
+fi;
+# Check whether --enable-xmltest or --disable-xmltest was given.
+if test "${enable_xmltest+set}" = set; then
+  enableval="$enable_xmltest"
+
+else
+  enable_xmltest=yes
+fi;
+
+  if test x$xml_config_exec_prefix != x ; then
+     xml_config_args="$xml_config_args"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
+     fi
+  fi
+  if test x$xml_config_prefix != x ; then
+     xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_prefix/bin/xml2-config
+     fi
+  fi
+
+  # Extract the first word of "xml2-config", so it can be a program name with args.
+set dummy xml2-config; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_XML2_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $XML2_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_XML2_CONFIG="$XML2_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_XML2_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_path_XML2_CONFIG" && ac_cv_path_XML2_CONFIG="no"
+  ;;
+esac
+fi
+XML2_CONFIG=$ac_cv_path_XML2_CONFIG
+
+if test -n "$XML2_CONFIG"; then
+  echo "$as_me:$LINENO: result: $XML2_CONFIG" >&5
+echo "${ECHO_T}$XML2_CONFIG" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  min_xml_version=2.6.26
+  echo "$as_me:$LINENO: checking for libxml - version >= $min_xml_version" >&5
+echo $ECHO_N "checking for libxml - version >= $min_xml_version... $ECHO_C" >&6
+  no_xml=""
+  if test "$XML2_CONFIG" = "no" ; then
+    no_xml=yes
+  else
+    XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
+    XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
+    xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'`
+    xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'`
+    xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'`
+    if test "x$enable_xmltest" = "xyes" ; then
+      ac_save_CPPFLAGS="$CPPFLAGS"
+      ac_save_LIBS="$LIBS"
+      CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
+      LIBS="$XML_LIBS $LIBS"
+      rm -f conf.xmltest
+      if test "$cross_compiling" = yes; then
+  echo $ac_n "cross compiling; assumed OK... $ac_c"
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlversion.h>
+
+int
+main()
+{
+  int xml_major_version, xml_minor_version, xml_micro_version;
+  int major, minor, micro;
+  char *tmp_version;
+
+  system("touch conf.xmltest");
+
+  /* Capture xml2-config output via autoconf/configure variables */
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = (char *)strdup("$min_xml_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string from xml2-config\n", "$min_xml_version");
+     exit(1);
+   }
+   free(tmp_version);
+
+   /* Capture the version information from the header files */
+   tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
+   if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
+     printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
+     exit(1);
+   }
+   free(tmp_version);
+
+ /* Compare xml2-config output to the libxml headers */
+  if ((xml_major_version != $xml_config_major_version) ||
+      (xml_minor_version != $xml_config_minor_version) ||
+      (xml_micro_version != $xml_config_micro_version))
+    {
+      printf("*** libxml header files (version %d.%d.%d) do not match\n",
+         xml_major_version, xml_minor_version, xml_micro_version);
+      printf("*** xml2-config (version %d.%d.%d)\n",
+         $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
+      return 1;
+    }
+/* Compare the headers to the library to make sure we match */
+  /* Less than ideal -- doesn't provide us with return value feedback,
+   * only exits if there's a serious mismatch between header and library.
+   */
+    LIBXML_TEST_VERSION;
+
+    /* Test that the library is greater than our minimum version */
+    if ((xml_major_version > major) ||
+        ((xml_major_version == major) && (xml_minor_version > minor)) ||
+        ((xml_major_version == major) && (xml_minor_version == minor) &&
+        (xml_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
+               xml_major_version, xml_minor_version, xml_micro_version);
+        printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
+           major, minor, micro);
+        printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
+        printf("*** correct copy of xml2-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+    }
+  return 1;
+}
+
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+no_xml=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+       CPPFLAGS="$ac_save_CPPFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+
+  if test "x$no_xml" = x ; then
+     echo "$as_me:$LINENO: result: yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&5
+echo "${ECHO_T}yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&6
+     have_libxml2=yes
+  else
+     echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+     if test "$XML2_CONFIG" = "no" ; then
+       echo "*** The xml2-config script installed by LIBXML could not be found"
+       echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the XML2_CONFIG environment variable to the"
+       echo "*** full path to xml2-config."
+     else
+       if test -f conf.xmltest ; then
+        :
+       else
+          echo "*** Could not run libxml test program, checking why..."
+          CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS"
+          LIBS="$LIBS $XML_LIBS"
+          cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
 
+#include <libxml/xmlversion.h>
+#include <stdio.h>
+
+int
+main ()
+{
+ LIBXML_TEST_VERSION; return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+   echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
+          echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+          echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
+          echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
+          echo "*** may want to edit the xml2-config script: $XML2_CONFIG"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+          CPPFLAGS="$ac_save_CPPFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+
+     XML_CPPFLAGS=""
+     XML_LIBS=""
+     :
+  fi
+
+
+  rm -f conf.xmltest
+
+   if test "x$have_libxml2" = "xyes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBXML2 1
+_ACEOF
+
+   fi
+fi
+
+if test "x$with_gnutls" = "xyes"; then
 
 # Check whether --with-libgnutls-prefix or --without-libgnutls-prefix was given.
 if test "${with_libgnutls_prefix+set}" = set; then
@@ -4292,6 +4617,14 @@ CPPFLAGS=$CPPFLAGS_save
 
 fi
 
+if test "x$have_libxml2" = "xyes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define ENABLE_METALINK 1
+_ACEOF
+
+fi
+
 if test "x$have_libgnutls" = "xyes" || test "x$have_openssl" = "xyes"; then
 
 cat >>confdefs.h <<\_ACEOF
@@ -11853,6 +12186,9 @@ s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
 s,@RANLIB@,$RANLIB,;t t
 s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
 s,@YACC@,$YACC,;t t
+s,@XML2_CONFIG@,$XML2_CONFIG,;t t
+s,@XML_CPPFLAGS@,$XML_CPPFLAGS,;t t
+s,@XML_LIBS@,$XML_LIBS,;t t
 s,@LIBGNUTLS_CONFIG@,$LIBGNUTLS_CONFIG,;t t
 s,@LIBGNUTLS_CFLAGS@,$LIBGNUTLS_CFLAGS,;t t
 s,@LIBGNUTLS_LIBS@,$LIBGNUTLS_LIBS,;t t

+ 12 - 0
configure.ac

@@ -15,6 +15,7 @@ AC_SUBST(localedir)
 # Checks for arguments.
 AC_ARG_WITH([gnutls],  [  --with-gnutls              use gnutls library if installed. Default: yes], [with_gnutls=$enableval], [with_gnutls=yes])
 AC_ARG_WITH([openssl], [  --with-openssl             use openssl library if installed. Default: yes], [with_openssl=$enableval], [with_openssl=yes])
+AC_ARG_WITH([libxml2], [  --with-libxml2             use libxml2 library if installed. Default: yes], [with_libxml2=$enableval], [with_libxml2=yes])
 
 
 # Checks for programs.
@@ -25,6 +26,13 @@ AC_PROG_RANLIB
 AC_PROG_YACC
 
 # Checks for libraries.
+if test "x$with_libxml2" = "xyes"; then
+   AM_PATH_XML2([2.6.26], [have_libxml2=yes])
+   if test "x$have_libxml2" = "xyes"; then
+      AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have libxml2.])
+   fi
+fi
+
 if test "x$with_gnutls" = "xyes"; then
   AM_PATH_LIBGNUTLS([1.2.9], [have_libgnutls=yes])
   if test "x$have_libgnutls" = "xyes"; then
@@ -51,6 +59,10 @@ if test "x$with_openssl" = "xyes" && test "x$have_libgnutls" != "xyes"; then
   AM_PATH_OPENSSL
 fi
 
+if test "x$have_libxml2" = "xyes"; then
+  AC_DEFINE([ENABLE_METALINK], [1], [Define to 1 if Metalink support is enabled.])
+fi
+
 if test "x$have_libgnutls" = "xyes" || test "x$have_openssl" = "xyes"; then
   AC_DEFINE([ENABLE_SSL], [1], [Define to 1 if ssl support is enabled.])
 fi

+ 3 - 0
m4/Makefile.in

@@ -137,6 +137,9 @@ USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
 USE_NLS = @USE_NLS@
 VERSION = @VERSION@
 XGETTEXT = @XGETTEXT@
+XML2_CONFIG = @XML2_CONFIG@
+XML_CPPFLAGS = @XML_CPPFLAGS@
+XML_LIBS = @XML_LIBS@
 YACC = @YACC@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@

+ 11 - 0
src/AbstractCommand.cc

@@ -77,6 +77,7 @@ bool AbstractCommand::execute() {
     delete(err);
     req->resetUrl();
     e->segmentMan->errors++;
+    tryReserved();
     return true;
   } catch(DlRetryEx* err) {
     logger->error(MSG_RESTARTING_DOWNLOAD, err, cuid);
@@ -90,6 +91,7 @@ bool AbstractCommand::execute() {
     if(isAbort) {
       logger->error(MSG_MAX_TRY, cuid, req->getTryCount());
       e->segmentMan->errors++;
+      tryReserved();
       return true;
     } else {
       return prepareForRetry(e->option->getAsInt(PREF_RETRY_WAIT));
@@ -97,6 +99,15 @@ bool AbstractCommand::execute() {
   }
 }
 
+void AbstractCommand::tryReserved() {
+  if(!e->segmentMan->reserved.empty()) {
+    Request* req = e->segmentMan->reserved.front();
+    e->segmentMan->reserved.pop_front();
+    Command* command = InitiateConnectionCommandFactory::createInitiateConnectionCommand(cuid, req, e);
+    e->commands.push_back(command);
+  }
+}
+
 bool AbstractCommand::prepareForRetry(int wait) {
   Command* command = InitiateConnectionCommandFactory::createInitiateConnectionCommand(cuid, req, e);
   if(wait == 0) {

+ 1 - 0
src/AbstractCommand.h

@@ -37,6 +37,7 @@ protected:
   DownloadEngine* e;
   Socket* socket;
 
+  void tryReserved();
   virtual bool prepareForRetry(int wait);
   virtual void onAbort(Exception* ex);
   virtual bool executeInternal(Segment segment) = 0;

+ 7 - 6
src/AbstractDiskWriter.cc

@@ -32,14 +32,15 @@
 
 AbstractDiskWriter::AbstractDiskWriter():fd(0) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestInit(ctx);
+  ctx.setAlgo(MessageDigestContext::ALGO_SHA1);
+  digestInit(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
 AbstractDiskWriter::~AbstractDiskWriter() {
   closeFile();
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestFree(ctx);
+  digestFree(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
@@ -99,7 +100,7 @@ int AbstractDiskWriter::readDataInternal(char* data, int len) {
 
 string AbstractDiskWriter::sha1Sum(long long int offset, long long int length) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestReset(ctx);
+  digestReset(ctx);
   try {
     int BUFSIZE = 16*1024;
     char buf[BUFSIZE];
@@ -107,7 +108,7 @@ string AbstractDiskWriter::sha1Sum(long long int offset, long long int length) {
       if(BUFSIZE != readData(buf, BUFSIZE, offset)) {
 	throw string("error");
       }
-      sha1DigestUpdate(ctx, buf, BUFSIZE);
+      digestUpdate(ctx, buf, BUFSIZE);
       offset += BUFSIZE;
     }
     int r = length%BUFSIZE;
@@ -115,10 +116,10 @@ string AbstractDiskWriter::sha1Sum(long long int offset, long long int length) {
       if(r != readData(buf, r, offset)) {
 	throw string("error");
       }
-      sha1DigestUpdate(ctx, buf, r);
+      digestUpdate(ctx, buf, r);
     }
     unsigned char hashValue[20];
-    sha1DigestFinal(ctx, hashValue);
+    digestFinal(ctx, hashValue);
     return Util::toHex(hashValue, 20);
   } catch(string ex) {
     throw new DlAbortEx(EX_FILE_SHA1SUM, filename.c_str(), strerror(errno));

+ 51 - 44
src/Base64.cc

@@ -21,62 +21,69 @@
 /* copyright --> */
 #include "Base64.h"
 
-string Base64::part_encode(const string& subplain)
-{
-  static char base64_table[64] = {
-    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
-    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
-    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
-    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
-    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
-    'w', 'x', 'y', 'z', '0', '1', '2', '3',
-    '4', '5', '6', '7', '8', '9', '+', '/',
-  };
+static char base64_table[64] = {
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+  'w', 'x', 'y', 'z', '0', '1', '2', '3',
+  '4', '5', '6', '7', '8', '9', '+', '/',
+};
 
+void Base64::part_encode(const unsigned char* sub, int subLength,
+			 unsigned char* buf)
+{
   int shift = 2;
-  char carry = 0;
-  bool ignore_flag = false;
-  string crypted;
-
-  for(unsigned int index = 0; index < subplain.size(); ++index) {
-    if(ignore_flag) {
-      crypted += '=';
-    } else {
-      char cur = subplain.at(index) >> shift | carry;
-      if(subplain.at(index) == 0) ignore_flag = true;
-      carry = (subplain.at(index) << (6-shift)) & 0x3F;
-      shift += 2;
-      crypted += base64_table[(unsigned int)cur];
-    }
+  unsigned char carry = 0;
+  int index;
+  for(index = 0; index < subLength; index++) {
+    unsigned char cur = sub[index] >> shift | carry;
+    carry = (sub[index] << (6-shift)) & 0x3f;
+    shift += 2;
+    buf[index] = base64_table[(unsigned int)cur];
   }
-  if(subplain.at(subplain.size()-1) == 0) {
-    crypted += '=';
+  if(subLength == 1) {
+    buf[index] = base64_table[(unsigned int)carry];
+    buf[index+1] = buf[index+2] = '=';
+  } else if(subLength == 2) {
+    buf[index] = base64_table[(unsigned int)carry];
+    buf[index+1] = '=';
   } else {
-    char cur = subplain.at(subplain.size()-1) & 0x3F;
-    crypted += base64_table[(unsigned int)cur];
+    unsigned char cur = sub[subLength-1] & 0x3f;
+    buf[index] = base64_table[(unsigned int)cur];
   }
-
-  return crypted;
 }
 
 string Base64::encode(const string& plainSrc)
 {
-  string plain = plainSrc;
-  int remainder = plain.size() % 3;
-  if( remainder ) remainder = 3-remainder;
-  for(int i = 0; i < remainder; ++i) plain += (char)0;
-  string crypted;
-  int start_pos = 0;
-  for(unsigned int index = 0; plain.size() > index; index += 3) {
-    string subplain = plain.substr(start_pos, 3);
-    string subcrypted = part_encode(subplain);
-    start_pos += 3;
-    crypted += subcrypted;
+  unsigned char* result = 0;
+  int resultLength = 0;
+
+  encode((const unsigned char*)plainSrc.c_str(), plainSrc.size(),
+	 result, resultLength);
+  string encoded(&result[0], &result[resultLength]);
+  delete [] result;
+  return encoded;
+}
+
+void Base64::encode(const unsigned char* src, int srcLength,
+		    unsigned char*& result, int& resultLength) {
+  resultLength = (srcLength+(srcLength%3 == 0 ? 0 : 3-srcLength%3))/3*4;
+  result = new unsigned char[resultLength];
+  unsigned char* tail = result;
+  for(int index = 0; srcLength > index; index += 3) {
+    unsigned char temp[4];
+    part_encode(&src[index],
+		srcLength >= index+3 ? 3 : srcLength-index,
+		temp);
+    memcpy(tail, temp, sizeof(temp)); 
+    tail += sizeof(temp);
   }
-  return crypted;
 }
 
+
 char Base64::getValue(char ch)
 {
   char retch;

+ 9 - 0
src/Base64.h

@@ -28,12 +28,21 @@ using namespace std;
 class Base64
 {
 private:
+  static void part_encode(const unsigned char* sub, int subLength,
+			  unsigned char* buf);
+
   static string part_encode(const string& subplain);
   static string part_decode(const string& subCrypted);
   static char getValue(char ch);
 public:
   static string encode(const string& plain);
+  // caller must deallocate the memory used by result.
+  static void encode(const unsigned char* src, int srcLength,
+		     unsigned char*& result, int& resultLength);
   static string decode(const string& crypted);
+  // caller must deallocate the memory used by result.
+  static void decode(const unsigned char* src, int srcLength,
+		     unsigned char*& result, int& resultLength);
 };
 
 #endif // _BASE64_H_

+ 8 - 3
src/Makefile.am

@@ -102,12 +102,17 @@ SRCS =  Socket.cc Socket.h\
 	SuggestPieceMessage.cc SuggestPieceMessage.h\
 	SimplePeerMessage.cc SimplePeerMessage.h\
 	NullLogger.h\
-	Time.cc Time.h
+	Time.cc Time.h\
+	Metalinker.cc Metalinker.h\
+	MetalinkEntry.cc MetalinkEntry.h\
+	MetalinkResource.cc MetalinkResource.h\
+	MetalinkProcessor.h\
+	Xml2MetalinkProcessor.cc Xml2MetalinkProcessor.h
 noinst_LIBRARIES = libaria2c.a
 libaria2c_a_SOURCES = $(SRCS)
 aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\
-	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@
+	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@ @XML_LIBS@
 AM_CPPFLAGS =  -Wall\
 	-I../lib -I../intl -I$(top_srcdir)/intl\
-	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@\
+	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@ @XML_CPPFLAGS@\
 	-D_FILE_OFFSET_BITS=64 -DLOCALEDIR=\"$(localedir)\" @DEFS@

+ 18 - 4
src/Makefile.in

@@ -109,7 +109,9 @@ am__objects_1 = Socket.$(OBJEXT) SocketCore.$(OBJEXT) \
 	PortMessage.$(OBJEXT) HaveAllMessage.$(OBJEXT) \
 	HaveNoneMessage.$(OBJEXT) RejectMessage.$(OBJEXT) \
 	AllowedFastMessage.$(OBJEXT) SuggestPieceMessage.$(OBJEXT) \
-	SimplePeerMessage.$(OBJEXT) Time.$(OBJEXT)
+	SimplePeerMessage.$(OBJEXT) Time.$(OBJEXT) \
+	Metalinker.$(OBJEXT) MetalinkEntry.$(OBJEXT) \
+	MetalinkResource.$(OBJEXT) Xml2MetalinkProcessor.$(OBJEXT)
 am_libaria2c_a_OBJECTS = $(am__objects_1)
 libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)"
@@ -215,6 +217,9 @@ USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
 USE_NLS = @USE_NLS@
 VERSION = @VERSION@
 XGETTEXT = @XGETTEXT@
+XML2_CONFIG = @XML2_CONFIG@
+XML_CPPFLAGS = @XML_CPPFLAGS@
+XML_LIBS = @XML_LIBS@
 YACC = @YACC@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
@@ -361,16 +366,21 @@ SRCS = Socket.cc Socket.h\
 	SuggestPieceMessage.cc SuggestPieceMessage.h\
 	SimplePeerMessage.cc SimplePeerMessage.h\
 	NullLogger.h\
-	Time.cc Time.h
+	Time.cc Time.h\
+	Metalinker.cc Metalinker.h\
+	MetalinkEntry.cc MetalinkEntry.h\
+	MetalinkResource.cc MetalinkResource.h\
+	MetalinkProcessor.h\
+	Xml2MetalinkProcessor.cc Xml2MetalinkProcessor.h
 
 noinst_LIBRARIES = libaria2c.a
 libaria2c_a_SOURCES = $(SRCS)
 aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\
-	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@
+	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@ @XML_LIBS@
 
 AM_CPPFLAGS = -Wall\
 	-I../lib -I../intl -I$(top_srcdir)/intl\
-	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@\
+	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@ @XML_CPPFLAGS@\
 	-D_FILE_OFFSET_BITS=64 -DLOCALEDIR=\"$(localedir)\" @DEFS@
 
 all: all-am
@@ -493,6 +503,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/List.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LogFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkEntry.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@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskAdaptor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriter.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NotInterestedMessage.Po@am__quote@
@@ -535,6 +548,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TrackerWatcherCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnchokeMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Xml2MetalinkProcessor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
 
 .cc.o:

+ 81 - 0
src/MetalinkEntry.cc

@@ -0,0 +1,81 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "MetalinkEntry.h"
+#include "Util.h"
+#include <algorithm>
+
+MetalinkEntry::MetalinkEntry() {}
+
+MetalinkEntry::~MetalinkEntry() {
+  for_each(resources.begin(), resources.end(), Deleter());
+}
+
+bool MetalinkEntry::check(const string& filename) const {
+  unsigned char buf[20];
+  int digestLength;
+  const string* digestPtr;
+  MessageDigestContext::HashAlgo algo;
+  if(!sha1.empty()) {
+    digestLength = 20;
+    algo = MessageDigestContext::ALGO_SHA1;
+    digestPtr = &sha1;
+  } else if(!md5.empty()) {
+    digestLength = 16;
+    algo = MessageDigestContext::ALGO_MD5;
+    digestPtr = &md5;
+  } else {
+    return true;
+  }
+  Util::fileChecksum(filename, buf, algo);
+  return *digestPtr == Util::toHex(buf, digestLength);
+}
+
+class PrefOrder {
+public:
+  bool operator()(const MetalinkResource* res1, const MetalinkResource* res2) {
+    return res1->preference > res2->preference;
+  }
+};
+
+void MetalinkEntry::reorderResourcesByPreference() {
+  random_shuffle(resources.begin(), resources.end());
+  sort(resources.begin(), resources.end(), PrefOrder());
+}
+
+class Supported {
+public:
+  bool operator()(const MetalinkResource* res) {
+    switch(res->type) {
+    case MetalinkResource::TYPE_FTP:
+    case MetalinkResource::TYPE_HTTP:
+      return true;
+    default:
+      return false;
+    }
+  }
+};
+
+void MetalinkEntry::dropUnsupportedResource() {
+  MetalinkResources::iterator split =
+    partition(resources.begin(), resources.end(), Supported());
+  resources.erase(split, resources.end());
+}

+ 55 - 0
src/MetalinkEntry.h

@@ -0,0 +1,55 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_METALINK_ENTRY_H_
+#define _D_METALINK_ENTRY_H_
+
+#include "common.h"
+#include "MetalinkResource.h"
+#include <deque>
+
+typedef deque<MetalinkResource*> MetalinkResources;
+
+class MetalinkEntry {
+public:
+  string filename;
+  string version;
+  string language;
+  string os;
+  long long int size;
+  string  md5;
+  string  sha1;
+public:
+  MetalinkResources resources;
+public:
+  MetalinkEntry();
+  ~MetalinkEntry();
+
+  MetalinkEntry& operator=(const MetalinkEntry& metalinkEntry);
+
+  bool check(const string& filename) const;
+
+  void dropUnsupportedResource();
+
+  void reorderResourcesByPreference();
+};
+
+#endif // _D_METALINK_ENTRY_H_

+ 35 - 0
src/MetalinkProcessor.h

@@ -0,0 +1,35 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_METALINK_PROCESSOR_H_
+#define _D_METALINK_PROCESSOR_H_
+
+#include "Metalinker.h"
+#include "common.h"
+
+class MetalinkProcessor {
+public:
+  virtual ~MetalinkProcessor() {}
+
+  virtual Metalinker* parseFile(const string& filename) = 0;
+};
+
+#endif // _D_METALINK_PROCESSOR_H_

+ 26 - 0
src/MetalinkResource.cc

@@ -0,0 +1,26 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "MetalinkResource.h"
+
+MetalinkResource::MetalinkResource() {}
+
+MetalinkResource::~MetalinkResource() {}

+ 47 - 0
src/MetalinkResource.h

@@ -0,0 +1,47 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_METALINK_RESOURCE_H_
+#define _D_METALINK_RESOURCE_H_
+
+#include "common.h"
+
+class MetalinkResource {
+public:
+  enum TYPE {
+    TYPE_FTP,
+    TYPE_HTTP,
+    TYPE_BITTORRENT,
+    TYPE_NOT_SUPPORTED
+  };
+public:
+  string url;
+  int type;
+  string location;
+  int preference;
+public:
+  MetalinkResource();
+  ~MetalinkResource();
+
+  MetalinkResource& operator=(const MetalinkResource& metalinkResource);
+};
+
+#endif // _D_METALINK_RESOURCE_H_

+ 74 - 0
src/Metalinker.cc

@@ -0,0 +1,74 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "Metalinker.h"
+#include <algorithm>
+
+Metalinker::Metalinker() {
+}
+
+Metalinker::~Metalinker() {
+  for_each(entries.begin(), entries.end(), Deleter());
+}
+
+class EntryQuery {
+private:
+  string version;
+  string language;
+  string os;
+public:
+  EntryQuery(const string& version,
+	     const string& language,
+	     const string& os):version(version),
+			       language(language),
+			       os(os) {}
+  bool operator()(const MetalinkEntry* entry) {
+    if(!version.empty()) {
+      if(version != entry->version) {
+	return false;
+      }
+    }
+    if(!language.empty()) {
+      if(language != entry->language) {
+	return false;
+      }
+    }
+    if(!os.empty()) {
+      if(os != entry->os) {
+	return false;
+      }
+    }
+    return true;
+  }
+};
+
+MetalinkEntry* Metalinker::queryEntry(const string& version,
+				      const string& language,
+				      const string& os) const {
+  MetalinkEntries::const_iterator itr =
+    find_if(entries.begin(), entries.end(),
+	    EntryQuery(version, language, os));
+  if(itr == entries.end()) {
+    return NULL;
+  } else {
+    return *itr;
+  }
+}

+ 46 - 0
src/Metalinker.h

@@ -0,0 +1,46 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_METALINKER_H_
+#define _D_METALINKER_H_
+
+#include "common.h"
+#include "MetalinkEntry.h"
+#include <deque>
+#include <libxml/xpath.h>
+
+typedef deque<MetalinkEntry*> MetalinkEntries;
+
+class Metalinker {
+public:
+  MetalinkEntries entries;
+
+public:
+  Metalinker();
+  ~Metalinker();
+
+  Metalinker& operator=(const Metalinker& metalinker);
+
+  MetalinkEntry* queryEntry(const string& version, const string& language,
+			    const string& os) const;
+};
+
+#endif // _D_METALINKER_H_

+ 7 - 6
src/MultiDiskWriter.cc

@@ -27,14 +27,15 @@
 
 MultiDiskWriter::MultiDiskWriter(int pieceLength):pieceLength(pieceLength) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestInit(ctx);
+  ctx.setAlgo(MessageDigestContext::ALGO_SHA1);
+  digestInit(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
 MultiDiskWriter::~MultiDiskWriter() {
   clearEntries();
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestFree(ctx);
+  digestFree(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
@@ -150,7 +151,7 @@ void MultiDiskWriter::hashUpdate(DiskWriterEntry* entry, long long int offset, l
     if(BUFSIZE != entry->diskWriter->readData(buf, BUFSIZE, offset)) {
       throw string("error");
     }
-    sha1DigestUpdate(ctx, buf, BUFSIZE);
+    digestUpdate(ctx, buf, BUFSIZE);
     offset += BUFSIZE;
   }
   int r = length%BUFSIZE;
@@ -158,7 +159,7 @@ void MultiDiskWriter::hashUpdate(DiskWriterEntry* entry, long long int offset, l
     if(r != entry->diskWriter->readData(buf, r, offset)) {
       throw string("error");
     }
-    sha1DigestUpdate(ctx, buf, r);
+    digestUpdate(ctx, buf, r);
   }
 }
 #endif // ENABLE_SHA1DIGEST
@@ -168,7 +169,7 @@ string MultiDiskWriter::sha1Sum(long long int offset, long long int length) {
   long long int fileOffset = offset;
   bool reading = false;
   int rem = length;
-  sha1DigestReset(ctx);
+  digestReset(ctx);
   try {
     for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
 	itr != diskWriterEntries.end() && rem != 0; itr++) {
@@ -186,7 +187,7 @@ string MultiDiskWriter::sha1Sum(long long int offset, long long int length) {
       throw new DlAbortEx(EX_FILE_OFFSET_OUT_OF_RANGE, offset);
     }
     unsigned char hashValue[20];
-    sha1DigestFinal(ctx, hashValue);
+    digestFinal(ctx, hashValue);
     return Util::toHex(hashValue, 20);
   } catch(string ex) {
     throw new DlAbortEx(EX_FILE_SHA1SUM, "", strerror(errno));

+ 2 - 2
src/PeerConnection.cc

@@ -71,8 +71,8 @@ bool PeerConnection::receiveMessage(char* msg, int& length) {
     }
     //payloadLen = ntohl(nPayloadLen);
     int payloadLength = ntohl(*((int*)lenbuf));
-    if(payloadLength > MAX_PAYLOAD_LEN) {
-      throw new DlAbortEx("max payload length exceeded. length = %d",
+    if(payloadLength > MAX_PAYLOAD_LEN || payloadLength < 0) {
+      throw new DlAbortEx("max payload length exceeded or invalid. length = %d",
 			  payloadLength);
     }
     currentPayloadLength = payloadLength;

+ 3 - 0
src/PeerMessageUtil.cc

@@ -57,6 +57,9 @@ void PeerMessageUtil::checkLength(int length) {
     throw new DlAbortEx("too large length %d > %dKB", length,
 			MAX_BLOCK_LENGTH/1024);
   }
+  if(length <= 0) {
+    throw new DlAbortEx("invalid length %d", length);
+  }
   if(!Util::isPowerOf(length, 2)) {
     throw new DlAbortEx("invalid length %d, which is not power of 2",
 			length);

+ 1 - 0
src/PieceMessage.cc

@@ -81,6 +81,7 @@ void PieceMessage::receivedAction() {
 	onGotNewPiece(piece);
       } else {
 	onGotWrongPiece(piece);
+	peerInteraction->abortPiece(piece);
       }
     }
   }

+ 2 - 0
src/Request.h

@@ -103,4 +103,6 @@ public:
 
 };
 
+typedef deque<Request*> Requests;
+
 #endif // _D_REQUEST_H_

+ 2 - 0
src/SegmentMan.h

@@ -28,6 +28,7 @@
 #include "Option.h"
 #include "SegmentSplitter.h"
 #include "DiskWriter.h"
+#include "Request.h"
 
 using namespace std;
 
@@ -92,6 +93,7 @@ public:
   const Option* option;
   SegmentSplitter* splitter;
   DiskWriter* diskWriter;
+  Requests reserved;
 
   SegmentMan();
   ~SegmentMan();

+ 17 - 16
src/ShaVisitor.cc

@@ -24,56 +24,57 @@
 
 ShaVisitor::ShaVisitor() {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestInit(ctx);
-  sha1DigestReset(ctx);
+  ctx.setAlgo(MessageDigestContext::ALGO_SHA1);
+  digestInit(ctx);
+  digestReset(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
 ShaVisitor::~ShaVisitor() {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestFree(ctx);
+  digestFree(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
 void ShaVisitor::visit(const Data* d) {
 #ifdef ENABLE_SHA1DIGEST
   if(d->isNumber()) {
-    sha1DigestUpdate(ctx, "i", 1);
+    digestUpdate(ctx, "i", 1);
   } else {
     string lenStr = Util::llitos(d->getLen());
-    sha1DigestUpdate(ctx, lenStr.c_str(), lenStr.size());
-    sha1DigestUpdate(ctx, ":", 1);
+    digestUpdate(ctx, lenStr.c_str(), lenStr.size());
+    digestUpdate(ctx, ":", 1);
   }
-  sha1DigestUpdate(ctx, d->getData(), d->getLen());
+  digestUpdate(ctx, d->getData(), d->getLen());
   if(d->isNumber()) {
-    sha1DigestUpdate(ctx, "e", 1);
+    digestUpdate(ctx, "e", 1);
   }
 #endif // ENABLE_SHA1DIGEST
 }
 
 void ShaVisitor::visit(const Dictionary* d) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestUpdate(ctx, "d", 1);
+  digestUpdate(ctx, "d", 1);
   const Order& v = d->getOrder();
   for(Order::const_iterator itr = v.begin(); itr != v.end(); itr++) {
     string lenStr = Util::llitos(itr->size());
-    sha1DigestUpdate(ctx, lenStr.c_str(), lenStr.size());
-    sha1DigestUpdate(ctx, ":", 1);
-    sha1DigestUpdate(ctx, itr->c_str(), itr->size());
+    digestUpdate(ctx, lenStr.c_str(), lenStr.size());
+    digestUpdate(ctx, ":", 1);
+    digestUpdate(ctx, itr->c_str(), itr->size());
     const MetaEntry* e = d->get(*itr);
     this->visit(e);
   }
-  sha1DigestUpdate(ctx, "e", 1);
+  digestUpdate(ctx, "e", 1);
 #endif // ENABLE_SHA1DIGEST
 }
 
 void ShaVisitor::visit(const List* l) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestUpdate(ctx, "l", 1);
+  digestUpdate(ctx, "l", 1);
   for(MetaList::const_iterator itr = l->getList().begin(); itr != l->getList().end(); itr++) {
     this->visit(*itr);
   }
-  sha1DigestUpdate(ctx, "e", 1);
+  digestUpdate(ctx, "e", 1);
 #endif // ENABLE_SHA1DIGEST
 }
 
@@ -89,7 +90,7 @@ void ShaVisitor::visit(const MetaEntry* e) {
 
 void ShaVisitor::getHash(unsigned char* hashValue, int& len) {
 #ifdef ENABLE_SHA1DIGEST
-  sha1DigestFinal(ctx, hashValue);
+  digestFinal(ctx, hashValue);
   len = 20;
 #endif // ENABLE_SHA1DIGEST
 }

+ 69 - 11
src/Util.cc

@@ -23,9 +23,6 @@
 #include "DlAbortEx.h"
 #include "File.h"
 #include "message.h"
-#ifdef ENABLE_SHA1DIGEST
-#include "messageDigest.h"
-#endif // ENABLE_SHA1DIGEST
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -68,8 +65,8 @@ string Util::llitos(long long int value, bool comma)
 }
 
 string Util::trim(const string& src) {
-  string::size_type sp = src.find_first_not_of(" ");
-  string::size_type ep = src.find_last_not_of(" ");
+  string::size_type sp = src.find_first_not_of("\r\n\t ");
+  string::size_type ep = src.find_last_not_of("\r\n\t ");
   if(sp == string::npos || ep == string::npos) {
     return "";
   } else {
@@ -365,12 +362,47 @@ string Util::getContentDispositionFilename(const string& header) {
 
 void Util::sha1Sum(unsigned char* digest, const void* data, int dataLength) {
 #ifdef ENABLE_SHA1DIGEST
-  MessageDigestContext ctx;
-  sha1DigestInit(ctx);
-  sha1DigestReset(ctx);
-  sha1DigestUpdate(ctx, data, dataLength);
-  sha1DigestFinal(ctx, digest);
-  sha1DigestFree(ctx);
+  MessageDigestContext ctx(MessageDigestContext::ALGO_SHA1);
+  digestInit(ctx);
+  digestReset(ctx);
+  digestUpdate(ctx, data, dataLength);
+  digestFinal(ctx, digest);
+  digestFree(ctx);
+#endif // ENABLE_SHA1DIGEST
+}
+
+void Util::fileChecksum(const string& filename, unsigned char* digest,
+			MessageDigestContext::HashAlgo algo) {
+#ifdef ENABLE_SHA1DIGEST
+  MessageDigestContext ctx(algo);
+  digestInit(ctx);
+  digestReset(ctx);
+
+  int BUFLEN = 4096;
+  char buf[BUFLEN];
+
+  int fd;
+  if((fd = open(filename.c_str(), O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
+    throw new DlAbortEx(EX_FILE_OPEN, filename.c_str(), strerror(errno));
+  }
+  while(1) {
+    int size = read(fd, buf, BUFLEN);
+    if(size == -1) {
+      if(errno == EINTR) {
+	continue;
+      } else {
+	close(fd);
+	throw new DlAbortEx(EX_FILE_READ, filename.c_str(), strerror(errno));
+      }
+    } else if(size > 0) {
+      digestUpdate(ctx, buf, size);
+    }
+    if(size < BUFLEN) {
+      break;
+    }
+  }
+  digestFinal(ctx, digest);
+  digestFree(ctx);
 #endif // ENABLE_SHA1DIGEST
 }
 
@@ -471,3 +503,29 @@ string Util::randomAlpha(int length) {
   }
   return str;
 }
+
+class UpperCase {
+public:
+  void operator()(char& ch) {
+    ch = toupper(ch);
+  }
+};
+
+string Util::toUpper(const string& src) {
+  string temp = src;
+  for_each(temp.begin(), temp.end(), UpperCase());
+  return temp;
+}
+
+class LowerCase {
+public:
+  void operator()(char& ch) {
+    ch = tolower(ch);
+  }
+};
+
+string Util::toLower(const string& src) {
+  string temp = src;
+  for_each(temp.begin(), temp.end(), LowerCase());
+  return temp;
+}

+ 12 - 0
src/Util.h

@@ -23,6 +23,9 @@
 #define _D_UTIL_H_
 
 #include "common.h"
+#ifdef ENABLE_SHA1DIGEST
+#include "messageDigest.h"
+#endif // ENABLE_SHA1DIGEST
 #include <string>
 #include <utility>
 #include <deque>
@@ -82,12 +85,21 @@ public:
   // digest must be at least 20 bytes long.
   static void sha1Sum(unsigned char* digest, const void* data, int dataLength);
 
+  // Before call this method, allocate enough memory to the parameter "digest".
+  // For sha1, you need 20 bytes. For md5, 16 bytes.
+  static void fileChecksum(const string& filename, unsigned char* digest,
+			   MessageDigestContext::HashAlgo algo);
+
   static Integers computeFastSet(string ipaddr, const unsigned char* infoHash,
 				int pieces, int fastSetSize);
 
   static int countBit(unsigned int);
 
   static string randomAlpha(int length);
+
+  static string toUpper(const string& src);
+
+  static string toLower(const string& src);
 };
 
 #endif // _D_UTIL_H_

+ 188 - 0
src/Xml2MetalinkProcessor.cc

@@ -0,0 +1,188 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "Xml2MetalinkProcessor.h"
+#include "DlAbortEx.h"
+#include "Util.h"
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+Xml2MetalinkProcessor::Xml2MetalinkProcessor():doc(NULL), context(NULL) {}
+
+Xml2MetalinkProcessor::~Xml2MetalinkProcessor() {
+  release();
+}
+
+void Xml2MetalinkProcessor::release() {
+  if(context) {
+    xmlXPathFreeContext(context);
+    context = NULL;
+  }
+  if(doc) {
+    xmlFreeDoc(doc);
+    doc = NULL;
+  }
+}
+
+Metalinker* Xml2MetalinkProcessor::parseFile(const string& filename) {
+  release();
+  doc = xmlParseFile(filename.c_str());
+  if(doc == NULL) {
+    throw new DlAbortEx("Cannot parse metalink file %s", filename.c_str());
+  }
+  context = xmlXPathNewContext(doc);
+  if(context == NULL) {
+    throw new DlAbortEx("Cannot create new xpath context");
+  }
+  string defaultNamespace = "http://www.metalinker.org/";
+  if(xmlXPathRegisterNs(context, (xmlChar*)"m",
+			(xmlChar*)defaultNamespace.c_str()) != 0) {
+    throw new DlAbortEx("Cannot register namespace %s", defaultNamespace.c_str());
+  }
+  
+  string xpath = "/m:metalink/m:files/m:file";
+  Metalinker* metalinker = new Metalinker();
+  try {
+    for(int index = 1; 1; index++) {
+      MetalinkEntry* entry = getEntry(xpath+"["+Util::itos(index)+"]");
+      if(entry == NULL) {
+	break;
+      } else {
+	metalinker->entries.push_back(entry);
+      }
+    }
+  } catch(Exception* e) {
+    delete metalinker;
+    throw;
+  }
+  return metalinker;
+}
+
+MetalinkEntry* Xml2MetalinkProcessor::getEntry(const string& xpath) {
+  xmlXPathObjectPtr result = xpathEvaluation(xpath);
+  if(result == NULL) {
+    return NULL;
+  }
+  xmlXPathFreeObject(result);
+  MetalinkEntry* entry = new MetalinkEntry();
+  try {
+    entry->version = Util::trim(xpathContent(xpath+"/m:version"));
+    entry->language = Util::trim(xpathContent(xpath+"/m:language"));
+    entry->os = Util::trim(xpathContent(xpath+"/m:os"));
+    entry->md5 = Util::trim(xpathContent(xpath+"/m:verification/m:hash[@type=\"md5\"]"));
+    entry->sha1 = Util::trim(xpathContent(xpath+"/m:verification/m:hash[@type=\"sha1\"]"));
+    for(int index = 1; 1; index++) {
+      MetalinkResource* resource =
+	getResource(xpath+"/m:resources/m:url["+Util::itos(index)+"]");
+      if(resource == NULL) {
+	break;
+      } else {
+	entry->resources.push_back(resource);
+      }
+    }
+  } catch(Exception* e) {
+    delete entry;
+    throw;
+  }
+
+  return entry;
+}
+
+MetalinkResource* Xml2MetalinkProcessor::getResource(const string& xpath) {
+  xmlXPathObjectPtr result = xpathEvaluation(xpath);
+  if(result == NULL) {
+    return NULL;
+  }
+  MetalinkResource* resource = new MetalinkResource();
+  try {
+    xmlNodeSetPtr nodeSet = result->nodesetval;
+    xmlNodePtr node = nodeSet->nodeTab[0];
+    string type = Util::trim(xmlAttribute(node, "type"));
+    if(type == "ftp") {
+      resource->type = MetalinkResource::TYPE_FTP;
+    } else if(type == "http") {
+      resource->type = MetalinkResource::TYPE_HTTP;
+    } else if(type == "bittorrent") {
+      resource->type = MetalinkResource::TYPE_BITTORRENT;
+    } else {
+      resource->type = MetalinkResource::TYPE_NOT_SUPPORTED;
+    }
+    string pref = Util::trim(xmlAttribute(node, "preference"));
+    if(pref.empty()) {
+      resource->preference = 100;
+    } else {
+      resource->preference = STRTOLL(pref.c_str());
+    }
+    resource->url = Util::trim(xmlContent(node));
+  } catch(Exception* e) {
+    delete resource;
+    throw e;
+  }
+  return resource;
+}
+
+xmlXPathObjectPtr Xml2MetalinkProcessor::xpathEvaluation(const string& xpath) {
+  xmlXPathObjectPtr result = xmlXPathEvalExpression((xmlChar*)xpath.c_str(),
+						    context);
+  if(result == NULL) {
+    throw new DlAbortEx("Cannot evaluate xpath %s", xpath.c_str());
+  }
+  if(xmlXPathNodeSetIsEmpty(result->nodesetval)) {
+    xmlXPathFreeObject(result);
+    return NULL;
+  }
+  return result;
+}
+
+string Xml2MetalinkProcessor::xmlAttribute(xmlNodePtr node, const string& attrName) {
+  xmlChar* temp = xmlGetNoNsProp(node, (xmlChar*)attrName.c_str());
+  if(temp == NULL) {
+    return "";
+  } else {
+    string attr = (char*)temp;
+    xmlFree(temp);
+    return attr;
+  }
+}
+
+string Xml2MetalinkProcessor::xmlContent(xmlNodePtr node) {
+  xmlChar* temp = xmlNodeGetContent(node);
+  if(temp == NULL) {
+    return "";
+  } else {
+    string content = (char*)temp;
+    xmlFree(temp);
+    return content;
+  }
+}
+
+string Xml2MetalinkProcessor::xpathContent(const string& xpath) {
+  xmlXPathObjectPtr result = xpathEvaluation(xpath);
+  if(result == NULL) {
+    return "";
+  }
+  xmlNodeSetPtr nodeSet = result->nodesetval;
+  xmlNodePtr node = nodeSet->nodeTab[0]->children;
+  string content = (char*)node->content;
+  xmlXPathFreeObject(result);
+  return content;
+}

+ 51 - 0
src/Xml2MetalinkProcessor.h

@@ -0,0 +1,51 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_XML2_METALINK_PROCESSOR_H_
+#define _D_XML2_METALINK_PROCESSOR_H_
+
+#include "MetalinkProcessor.h"
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+class Xml2MetalinkProcessor : public MetalinkProcessor {
+private:
+  xmlDocPtr doc;
+  xmlXPathContextPtr context;
+
+  MetalinkEntry* getEntry(const string& xpath);
+  MetalinkResource* getResource(const string& xpath);
+
+  xmlXPathObjectPtr xpathEvaluation(const string& xpath);
+  string xpathContent(const string& xpath);
+  string xmlAttribute(xmlNodePtr node, const string& attrName);
+  string xmlContent(xmlNodePtr node);
+
+  void release();
+public:
+  Xml2MetalinkProcessor();
+  virtual ~Xml2MetalinkProcessor();
+
+  virtual Metalinker* parseFile(const string& filename);
+  
+};
+
+#endif // _D_XML2_METALINK_PROCESSOR_H_

+ 144 - 36
src/main.cc

@@ -37,6 +37,7 @@
 #include "TrackerUpdateCommand.h"
 #include "ByteArrayDiskWriter.h"
 #include "PeerChokeCommand.h"
+#include "Xml2MetalinkProcessor.h"
 #include <deque>
 #include <algorithm>
 #include <time.h>
@@ -59,7 +60,10 @@ extern int optind, opterr, optopt;
 
 using namespace std;
 
-typedef deque<Request*> Requests;
+bool readyToTorrentMode = false;
+string downloadedTorrentFile;
+bool readyToMetalinkMode = false;
+string downloadedMetalinkFile;
 
 void printDownloadCompeleteMessage(string filename) {
   printf(_("\nThe download was complete. <%s>\n"), filename.c_str());
@@ -115,11 +119,10 @@ void torrentHandler(int signal) {
   te->torrentMan->setHalt(true);
 }
 
-void addCommand(int cuid, const string& url, string referer, Requests& requests) {
+void createRequest(int cuid, const string& url, string referer, Requests& requests) {
   Request* req = new Request();
   req->setReferer(referer);
   if(req->setUrl(url)) {
-    e->commands.push_back(InitiateConnectionCommandFactory::createInitiateConnectionCommand(cuid, req, e));
     requests.push_back(req);
   } else {
     fprintf(stderr, _("Unrecognized URL or unsupported protocol: %s\n"), req->getUrl().c_str());
@@ -258,6 +261,54 @@ void showUsage() {
   cout << endl;
 }
 
+bool normalDownload(const Requests& requests,
+		    const Requests& reserved,
+		    Option* op,
+		    const string& dir,
+		    const string& ufilename,
+		    string& downloadedFilename) {
+  setSignalHander(SIGINT, handler, 0);
+  setSignalHander(SIGTERM, handler, 0);
+  
+  e = new ConsoleDownloadEngine();
+  e->option = op;
+  e->segmentMan = new SegmentMan();
+  e->segmentMan->diskWriter = new DefaultDiskWriter();
+  e->segmentMan->dir = dir;
+  e->segmentMan->ufilename = ufilename;
+  e->segmentMan->option = op;
+  e->segmentMan->splitter = new SplitSlowestSegmentSplitter();
+  e->segmentMan->splitter->setMinSegmentSize(op->getAsLLInt(PREF_MIN_SEGMENT_SIZE));
+  e->segmentMan->reserved = reserved;
+  
+  int cuidCounter = 1;
+  for(Requests::const_iterator itr = requests.begin();
+      itr != requests.end();
+      itr++, cuidCounter++) {
+    e->commands.push_back(InitiateConnectionCommandFactory::createInitiateConnectionCommand(cuidCounter, *itr, e));
+  }
+  e->run();
+  bool success = false;
+  if(e->segmentMan->finished()) {
+    printDownloadCompeleteMessage(e->segmentMan->getFilePath());
+    if(Util::endsWith(e->segmentMan->getFilePath(), ".torrent")) {
+      downloadedTorrentFile = e->segmentMan->getFilePath();
+      readyToTorrentMode = true;
+    } else if(Util::endsWith(e->segmentMan->getFilePath(), ".metalink")) {
+      downloadedMetalinkFile = e->segmentMan->getFilePath();
+      readyToMetalinkMode = true;
+    }
+    downloadedFilename = e->segmentMan->getFilePath();
+    success = true;
+  } else {
+    printDownloadAbortMessage();
+  }
+  e->cleanQueue();
+  delete e;
+
+  return success;
+}
+
 int main(int argc, char* argv[]) {
 #ifdef ENABLE_NLS
   setlocale (LC_CTYPE, "");
@@ -273,13 +324,23 @@ int main(int argc, char* argv[]) {
   bool daemonMode = false;
   string referer;
   string torrentFile;
+  string metalinkFile;
   int listenPort = -1;
+  string metalinkVersion;
+  string metalinkLanguage;
+  string metalinkOs;
+  int metalinkConnection = 15;
   Integers selectFileIndexes;
 #ifdef ENABLE_BITTORRENT
   bool followTorrent = true;
 #else
   bool followTorrent = false;
 #endif // ENABLE_BITTORRENT
+#ifdef ENABLE_METALINK
+  bool followMetalink = true;
+#else
+  bool followMetalink = false;
+#endif // ENABLE_METALINK
 
   int c;
   Option* op = new Option();
@@ -333,11 +394,15 @@ int main(int argc, char* argv[]) {
       { "upload-limit", required_argument, &lopt, 20 },
       { "select-file", required_argument, &lopt, 21 },
 #endif // ENABLE_BITTORRENT
+#ifdef ENABLE_METALINK
+      { "metalink-file", required_argument, NULL, 'M' },
+      { "metalink-connection", required_argument, NULL, 'C' },
+#endif // ENABLE_METALINK
       { "version", no_argument, NULL, 'v' },
       { "help", no_argument, NULL, 'h' },
       { 0, 0, 0, 0 }
     };
-    c = getopt_long(argc, argv, "Dd:o:l:s:pt:m:vhST:", longOpts, &optIndex);
+    c = getopt_long(argc, argv, "Dd:o:l:s:pt:m:vhST:M:C:", longOpts, &optIndex);
     if(c == -1) {
       break;
     }
@@ -549,6 +614,17 @@ int main(int argc, char* argv[]) {
     case 'T':
       torrentFile = string(optarg);
       break;
+    case 'M':
+      metalinkFile = string(optarg);
+      break;
+    case 'C':
+      metalinkConnection = (int)strtol(optarg, NULL, 10);
+      if(metalinkConnection <= 0) {
+	cerr << _("metalink-connection must be greater than 0.") << endl;
+	showUsage();
+	exit(1);
+      }
+      break;      
     case 'v':
       showVersion();
       exit(0);
@@ -560,7 +636,7 @@ int main(int argc, char* argv[]) {
       exit(1);
     }
   }
-  if(torrentFile.empty()) {
+  if(torrentFile.empty() && metalinkFile.empty()) {
     if(optind == argc) {
       cerr << _("specify at least one URL") << endl;
       showUsage();
@@ -584,6 +660,9 @@ int main(int argc, char* argv[]) {
 #ifdef HAVE_LIBGNUTLS
   gnutls_global_init();
 #endif // HAVE_LIBGNUTLS
+#ifdef HAVE_LIBXML2
+  xmlInitParser();
+#endif // HAVE_LIBXML2
   srandom(time(NULL));
   if(stdoutLog) {
     LogFactory::setLogFile("/dev/stdout");
@@ -601,50 +680,76 @@ int main(int argc, char* argv[]) {
 
   setSignalHander(SIGPIPE, SIG_IGN, 0);
 
-  bool readyToTorrentMode = false;
-  string downloadedTorrentFile;
-  if(torrentFile.empty()) {
-    setSignalHander(SIGINT, handler, 0);
-    setSignalHander(SIGTERM, handler, 0);
-
-    e = new ConsoleDownloadEngine();
-    e->option = op;
-    e->segmentMan = new SegmentMan();
-    e->segmentMan->diskWriter = new DefaultDiskWriter();
-    e->segmentMan->dir = dir;
-    e->segmentMan->ufilename = ufilename;
-    e->segmentMan->option = op;
-    e->segmentMan->splitter = new SplitSlowestSegmentSplitter();
-    e->segmentMan->splitter->setMinSegmentSize(op->getAsLLInt(PREF_MIN_SEGMENT_SIZE));
-
-    
+  if(torrentFile.empty() && metalinkFile.empty()) {
     Requests requests;
     int cuidCounter = 1;
     for(Strings::const_iterator itr = args.begin(); itr != args.end(); itr++) {
       for(int s = 1; s <= split; s++) {
-	addCommand(cuidCounter, *itr, referer, requests); 
+	createRequest(cuidCounter, *itr, referer, requests); 
 	cuidCounter++;
       }
     }
+    setSignalHander(SIGINT, handler, 0);
+    setSignalHander(SIGTERM, handler, 0);
+
+    Requests reserved;
+    string downloadedFilename;
+    normalDownload(requests, reserved, op, dir, ufilename, downloadedFilename);
 
-    e->run();
+    for_each(requests.begin(), requests.end(), Deleter());
+    requests.clear();
+  }
+  if(!metalinkFile.empty() || followMetalink && readyToMetalinkMode) {
+    string targetMetalinkFile = metalinkFile.empty() ?
+      downloadedMetalinkFile : metalinkFile;
+    Xml2MetalinkProcessor proc;
+    Metalinker* metalinker = proc.parseFile(targetMetalinkFile);
     
-    if(e->segmentMan->finished()) {
-      printDownloadCompeleteMessage(e->segmentMan->getFilePath());
-      if(Util::endsWith(e->segmentMan->getFilePath(), ".torrent")) {
-	downloadedTorrentFile = e->segmentMan->getFilePath();
-	readyToTorrentMode = true;
-      }
-    } else {
-      printDownloadAbortMessage();
+    MetalinkEntry* entry = metalinker->queryEntry(metalinkVersion,
+						  metalinkLanguage,
+						  metalinkOs);
+    if(entry == NULL) {
+      printf("No file matched with your preference");
+      exit(1);
+    }
+    entry->dropUnsupportedResource();
+    entry->reorderResourcesByPreference();
+    Requests requests;
+    int cuidCounter = 1;
+    for(MetalinkResources::const_iterator itr = entry->resources.begin();
+	itr != entry->resources.end(); itr++) {
+      MetalinkResource* resource = *itr;
+      createRequest(cuidCounter, resource->url, referer, requests); 
+      cuidCounter++;
+    }
+    Requests reserved;
+    if((int)requests.size() > metalinkConnection) {
+      copy(requests.begin()+metalinkConnection, requests.end(),
+	   insert_iterator<Requests>(reserved, reserved.end()));
+      requests.erase(requests.begin()+metalinkConnection, requests.end());
     }
+
+    setSignalHander(SIGINT, handler, 0);
+    setSignalHander(SIGTERM, handler, 0);
+
+    string downloadedFilename;
+    bool success = normalDownload(requests, reserved, op, dir, ufilename,
+				  downloadedFilename);
+
     for_each(requests.begin(), requests.end(), Deleter());
     requests.clear();
 
-    e->cleanQueue();
-    delete e;
-  }
-  if(!torrentFile.empty() || followTorrent && readyToTorrentMode) {
+    if(success) {
+      if(entry->check(downloadedFilename)) {
+	printf("checksum OK.\n");
+      } else {
+	printf("checksum ERROR.\n");
+	exit(EXIT_FAILURE);
+      }
+    }
+
+    delete metalinker;
+  } else if(!torrentFile.empty() || followTorrent && readyToTorrentMode) {
     try {
       //op->put(PREF_MAX_TRIES, "0");
       setSignalHander(SIGINT, torrentHandler, SA_RESETHAND);
@@ -735,5 +840,8 @@ int main(int argc, char* argv[]) {
 #ifdef HAVE_LIBGNUTLS
   gnutls_global_deinit();
 #endif // HAVE_LIBGNUTLS
+#ifdef HAVE_LIBXML2
+  xmlCleanupParser();
+#endif // HAVE_LIBXML2
   return 0;
 }

+ 65 - 16
src/messageDigest.h

@@ -28,30 +28,79 @@
 
 #ifdef HAVE_LIBSSL
 #include <openssl/evp.h>
-#define MessageDigestContext EVP_MD_CTX
-#define sha1DigestInit(CTX) EVP_MD_CTX_init(&CTX)
-#define sha1DigestReset(CTX) EVP_DigestInit_ex(&CTX, EVP_sha1(), NULL)
-#define sha1DigestUpdate(CTX, DATA, LENGTH) EVP_DigestUpdate(&CTX, DATA, LENGTH)
-#define sha1DigestFinal(CTX, HASH) \
+#endif // HAVE_LIBSSL
+
+#ifdef HAVE_LIBGCRYPT
+#include <gcrypt.h>
+#endif // HAVE_LIBGCRYPT
+
+class MessageDigestContext {
+public:
+  enum HashAlgo {
+    ALGO_MD5,
+    ALGO_SHA1
+  };
+#ifdef HAVE_LIBSSL
+  EVP_MD_CTX ctx;
+  const EVP_MD* algo;
+#endif // HAVE_LIBSSL
+#ifdef HAVE_LIBGCRYPT
+  gcry_md_hd_t ctx;
+  int algo;
+#endif // HAVE_LIBGCRYPT
+
+  MessageDigestContext() {}
+  MessageDigestContext(HashAlgo algo) {
+    setAlgo(algo);
+  }
+
+  void setAlgo(HashAlgo algo) {
+    switch(algo) {
+    case ALGO_MD5:
+#ifdef HAVE_LIBSSL
+      this->algo = EVP_md5();
+#endif // HAVE_LIBSSL
+#ifdef HAVE_LIBGCRYPT
+      this->algo = GCRY_MD_MD5;
+#endif // HAVE_LIBGCRYPT
+      break;
+    case ALGO_SHA1:
+#ifdef HAVE_LIBSSL
+      this->algo = EVP_sha1();
+#endif // HAVE_LIBSSL
+#ifdef HAVE_LIBGCRYPT
+      this->algo = GCRY_MD_SHA1;
+#endif // HAVE_LIBGCRYPT
+      break;
+    default:
+      break;
+    }
+  }
+};
+
+#ifdef HAVE_LIBSSL
+#define digestInit(CTX) EVP_MD_CTX_init(&CTX.ctx)
+#define digestReset(CTX) EVP_DigestInit_ex(&CTX.ctx, CTX.algo, NULL)
+#define digestUpdate(CTX, DATA, LENGTH) EVP_DigestUpdate(&CTX.ctx, DATA, LENGTH)
+#define digestFinal(CTX, HASH) \
 {\
 int len;\
-EVP_DigestFinal_ex(&CTX, HASH, (unsigned int*)&len);\
+EVP_DigestFinal_ex(&CTX.ctx, HASH, (unsigned int*)&len);\
 }
-#define sha1DigestFree(CTX) EVP_MD_CTX_cleanup(&CTX)
+#define digestFree(CTX) EVP_MD_CTX_cleanup(&CTX.ctx)
+
 #endif // HAVE_LIBSSL
 
 #ifdef HAVE_LIBGCRYPT
-#include <gcrypt.h>
-#define MessageDigestContext gcry_md_hd_t
-#define sha1DigestInit(CTX) gcry_md_open(&CTX, GCRY_MD_SHA1, 0)
-#define sha1DigestReset(CTX) gcry_md_reset(CTX)
-#define sha1DigestUpdate(CTX, DATA, LENGTH) gcry_md_write(CTX, DATA, LENGTH)
-#define sha1DigestFinal(CTX, HASH) \
+#define digestInit(CTX) gcry_md_open(&CTX.ctx, CTX.algo, 0)
+#define digestReset(CTX) gcry_md_reset(CTX.ctx)
+#define digestUpdate(CTX, DATA, LENGTH) gcry_md_write(CTX.ctx, DATA, LENGTH)
+#define digestFinal(CTX, HASH) \
 {\
-gcry_md_final(CTX);\
-memcpy(HASH, gcry_md_read(CTX, 0), 20);\
+gcry_md_final(CTX.ctx);\
+memcpy(HASH, gcry_md_read(CTX.ctx, 0), gcry_md_get_algo_dlen(CTX.algo));\
 }
-#define sha1DigestFree(CTX) gcry_md_close(CTX)
+#define digestFree(CTX) gcry_md_close(CTX.ctx)
 #endif // HAVE_LIBGCRYPT
 
 #endif // ENABLE_BITTORRENT

+ 8 - 0
test/Base64Test.cc

@@ -26,6 +26,14 @@ CPPUNIT_TEST_SUITE_REGISTRATION( Base64Test );
 void Base64Test::testEncode() {
   CPPUNIT_ASSERT_EQUAL(string("SGVsbG8gV29ybGQh"),
 		       Base64::encode("Hello World!"));
+  CPPUNIT_ASSERT_EQUAL(string("SGVsbG8gV29ybGQ="),
+		       Base64::encode("Hello World"));
+  CPPUNIT_ASSERT_EQUAL(string("SGVsbG8gV29ybA=="),
+		       Base64::encode("Hello Worl"));
+  CPPUNIT_ASSERT_EQUAL(string("YQ=="),
+		       Base64::encode("a"));
+  CPPUNIT_ASSERT_EQUAL(string(""),
+		       Base64::encode(""));
 }
 
 void Base64Test::testDecode() {

+ 6 - 3
test/Makefile.am

@@ -31,16 +31,19 @@ aria2c_SOURCES = AllTest.cc\
 	PieceMessageTest.cc\
 	RejectMessageTest.cc\
 	AllowedFastMessageTest.cc\
-	SuggestPieceMessageTest.cc
+	SuggestPieceMessageTest.cc\
+	Xml2MetalinkProcessorTest.cc\
+	MetalinkerTest.cc\
+	MetalinkEntryTest.cc
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
 
 aria2c_LDADD = ../src/libaria2c.a\
 	 ${CPPUNIT_LIBS} @LIBGNUTLS_LIBS@\
-	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@
+	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@ @XML_LIBS@
 AM_CPPFLAGS =  -Wall\
 	${CPPUNIT_CFLAGS}\
 	-I ../src\
 	-I../lib -I../intl -I$(top_srcdir)/intl\
-	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@\
+	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@ @XML_CPPFLAGS@\
 	-D_FILE_OFFSET_BITS=64 -DLOCALEDIR=\"$(localedir)\" @DEFS@

+ 15 - 4
test/Makefile.in

@@ -71,7 +71,9 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) RequestTest.$(OBJEXT) \
 	BitfieldMessageTest.$(OBJEXT) RequestMessageTest.$(OBJEXT) \
 	CancelMessageTest.$(OBJEXT) PieceMessageTest.$(OBJEXT) \
 	RejectMessageTest.$(OBJEXT) AllowedFastMessageTest.$(OBJEXT) \
-	SuggestPieceMessageTest.$(OBJEXT)
+	SuggestPieceMessageTest.$(OBJEXT) \
+	Xml2MetalinkProcessorTest.$(OBJEXT) MetalinkerTest.$(OBJEXT) \
+	MetalinkEntryTest.$(OBJEXT)
 aria2c_OBJECTS = $(am_aria2c_OBJECTS)
 am__DEPENDENCIES_1 =
 aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@@ -168,6 +170,9 @@ USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
 USE_NLS = @USE_NLS@
 VERSION = @VERSION@
 XGETTEXT = @XGETTEXT@
+XML2_CONFIG = @XML2_CONFIG@
+XML_CPPFLAGS = @XML_CPPFLAGS@
+XML_LIBS = @XML_LIBS@
 YACC = @YACC@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
@@ -243,19 +248,22 @@ aria2c_SOURCES = AllTest.cc\
 	PieceMessageTest.cc\
 	RejectMessageTest.cc\
 	AllowedFastMessageTest.cc\
-	SuggestPieceMessageTest.cc
+	SuggestPieceMessageTest.cc\
+	Xml2MetalinkProcessorTest.cc\
+	MetalinkerTest.cc\
+	MetalinkEntryTest.cc
 
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
 aria2c_LDADD = ../src/libaria2c.a\
 	 ${CPPUNIT_LIBS} @LIBGNUTLS_LIBS@\
-	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@
+	@LIBGCRYPT_LIBS@ @OPENSSL_LIBS@ @XML_LIBS@
 
 AM_CPPFLAGS = -Wall\
 	${CPPUNIT_CFLAGS}\
 	-I ../src\
 	-I../lib -I../intl -I$(top_srcdir)/intl\
-	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@\
+	@LIBGNUTLS_CFLAGS@ @LIBGCRYPT_CFLAGS@ @OPENSSL_CFLAGS@ @XML_CPPFLAGS@\
 	-D_FILE_OFFSET_BITS=64 -DLOCALEDIR=\"$(localedir)\" @DEFS@
 
 all: all-am
@@ -323,6 +331,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InterestedMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ListTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtilTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkEntryTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetalinkerTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MultiDiskWriterTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NotInterestedMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptionTest.Po@am__quote@
@@ -336,6 +346,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TorrentManTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnchokeMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UtilTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Xml2MetalinkProcessorTest.Po@am__quote@
 
 .cc.o:
 @am__fastdepCXX_TRUE@	if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \

+ 34 - 0
test/UtilTest.cc

@@ -17,6 +17,9 @@ class UtilTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testGetContentDispositionFilename);
   CPPUNIT_TEST(testComputeFastSet);
   CPPUNIT_TEST(testRandomAlpha);
+  CPPUNIT_TEST(testFileChecksum);
+  CPPUNIT_TEST(testToUpper);
+  CPPUNIT_TEST(testToLower);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -34,6 +37,9 @@ public:
   // may be moved to other helper class in the future.
   void testGetContentDispositionFilename();
   void testRandomAlpha();
+  void testFileChecksum();
+  void testToUpper();
+  void testToLower();
 };
 
 
@@ -214,3 +220,31 @@ void UtilTest::testComputeFastSet() {
 void UtilTest::testRandomAlpha() {
   CPPUNIT_ASSERT_EQUAL(string("rUopvKRn"), Util::randomAlpha(8));
 }
+
+void UtilTest::testFileChecksum() {
+  unsigned char buf[20];
+  string filename = "4096chunk.txt";
+  Util::fileChecksum(filename, buf, MessageDigestContext::ALGO_SHA1);
+  string sha1 = Util::toHex(buf, 20);
+  CPPUNIT_ASSERT_EQUAL(string("608cabc0f2fa18c260cafd974516865c772363d5"),
+		       sha1);
+
+  Util::fileChecksum(filename, buf, MessageDigestContext::ALGO_MD5);
+  string md5 = Util::toHex(buf, 16);
+  CPPUNIT_ASSERT_EQUAL(string("82a7348c2e03731109d0cf45a7325b88"),
+		       md5);
+}
+
+void UtilTest::testToUpper() {
+  string src = "608cabc0f2fa18c260cafd974516865c772363d5";
+  string upp = "608CABC0F2FA18C260CAFD974516865C772363D5";
+
+  CPPUNIT_ASSERT_EQUAL(upp, Util::toUpper(src));
+}
+
+void UtilTest::testToLower() {
+  string src = "608CABC0F2FA18C260CAFD974516865C772363D5";
+  string upp = "608cabc0f2fa18c260cafd974516865c772363d5";
+
+  CPPUNIT_ASSERT_EQUAL(upp, Util::toLower(src));
+}