From 78eff232546a5fa6f48c151dc2343915ed94fa8c Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 3 Jul 2006 14:19:23 +0000 Subject: [PATCH] 2006-07-03 Tatsuhiro Tsujikawa 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(). --- ChangeLog | 72 ++++++++ Makefile.in | 3 + TODO | 3 + aclocal.m4 | 189 +++++++++++++++++++ config.h.in | 6 + configure | 340 ++++++++++++++++++++++++++++++++++- configure.ac | 12 ++ m4/Makefile.in | 3 + src/AbstractCommand.cc | 11 ++ src/AbstractCommand.h | 1 + src/AbstractDiskWriter.cc | 13 +- src/Base64.cc | 97 +++++----- src/Base64.h | 9 + src/Makefile.am | 11 +- src/Makefile.in | 22 ++- src/MetalinkEntry.cc | 81 +++++++++ src/MetalinkEntry.h | 55 ++++++ src/MetalinkProcessor.h | 35 ++++ src/MetalinkResource.cc | 26 +++ src/MetalinkResource.h | 47 +++++ src/Metalinker.cc | 74 ++++++++ src/Metalinker.h | 46 +++++ src/MultiDiskWriter.cc | 13 +- src/PeerConnection.cc | 4 +- src/PeerMessageUtil.cc | 3 + src/PieceMessage.cc | 1 + src/Request.h | 2 + src/SegmentMan.h | 2 + src/ShaVisitor.cc | 33 ++-- src/Util.cc | 80 +++++++-- src/Util.h | 12 ++ src/Xml2MetalinkProcessor.cc | 188 +++++++++++++++++++ src/Xml2MetalinkProcessor.h | 51 ++++++ src/main.cc | 180 +++++++++++++++---- src/messageDigest.h | 85 +++++++-- test/Base64Test.cc | 8 + test/Makefile.am | 9 +- test/Makefile.in | 19 +- test/UtilTest.cc | 34 ++++ 39 files changed, 1724 insertions(+), 156 deletions(-) create mode 100644 src/MetalinkEntry.cc create mode 100644 src/MetalinkEntry.h create mode 100644 src/MetalinkProcessor.h create mode 100644 src/MetalinkResource.cc create mode 100644 src/MetalinkResource.h create mode 100644 src/Metalinker.cc create mode 100644 src/Metalinker.h create mode 100644 src/Xml2MetalinkProcessor.cc create mode 100644 src/Xml2MetalinkProcessor.h diff --git a/ChangeLog b/ChangeLog index 8d6f6b68..d6b0ac1f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,75 @@ +2006-07-03 Tatsuhiro Tsujikawa + + 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 To fix the bug that causes same have message is sent many times to diff --git a/Makefile.in b/Makefile.in index 3c23e9dc..7375f735 100644 --- a/Makefile.in +++ b/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@ diff --git a/TODO b/TODO index 9d56f738..3a7fcb11 100644 --- a/TODO +++ b/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 \ No newline at end of file diff --git a/aclocal.m4 b/aclocal.m4 index c51bf07b..7c96ff90 100644 --- a/aclocal.m4 +++ b/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 +#include +#include +#include + +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, µ) != 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 +#include +], [ 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 diff --git a/config.h.in b/config.h.in index 54b7068b..a13a4822 100644 --- a/config.h.in +++ b/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 header file. */ #undef HAVE_LIMITS_H diff --git a/configure b/configure index 5972f242..cf884209 100755 --- a/configure +++ b/configure @@ -311,7 +311,7 @@ ac_includes_default="\ # include #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,9 +3708,322 @@ 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 +#include +#include +#include + +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, µ) != 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 +#include + +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 withval="$with_libgnutls_prefix" @@ -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 diff --git a/configure.ac b/configure.ac index 4e974144..840bef1b 100644 --- a/configure.ac +++ b/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 diff --git a/m4/Makefile.in b/m4/Makefile.in index e286bf15..fa7a7484 100644 --- a/m4/Makefile.in +++ b/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@ diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc index 0de96354..a622959a 100644 --- a/src/AbstractCommand.cc +++ b/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) { diff --git a/src/AbstractCommand.h b/src/AbstractCommand.h index c4b3d91f..6eb6e9f4 100644 --- a/src/AbstractCommand.h +++ b/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; diff --git a/src/AbstractDiskWriter.cc b/src/AbstractDiskWriter.cc index 42f23b03..f249b274 100644 --- a/src/AbstractDiskWriter.cc +++ b/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)); diff --git a/src/Base64.cc b/src/Base64.cc index bcfd7222..71227ac8 100644 --- a/src/Base64.cc +++ b/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', '+', '/', +}; + +void Base64::part_encode(const unsigned char* sub, int subLength, + unsigned char* buf) { - 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', '+', '/', - }; - 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; - } - return crypted; + 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); + } +} + + char Base64::getValue(char ch) { char retch; diff --git a/src/Base64.h b/src/Base64.h index e40e3b7e..f1ee8262 100644 --- a/src/Base64.h +++ b/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_ diff --git a/src/Makefile.am b/src/Makefile.am index 071d5c3e..cf8fd2b1 100644 --- a/src/Makefile.am +++ b/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@ \ No newline at end of file diff --git a/src/Makefile.in b/src/Makefile.in index 630fb59d..fd583552 100644 --- a/src/Makefile.in +++ b/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: diff --git a/src/MetalinkEntry.cc b/src/MetalinkEntry.cc new file mode 100644 index 00000000..506b5999 --- /dev/null +++ b/src/MetalinkEntry.cc @@ -0,0 +1,81 @@ +/* */ +#include "MetalinkEntry.h" +#include "Util.h" +#include + +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()); +} diff --git a/src/MetalinkEntry.h b/src/MetalinkEntry.h new file mode 100644 index 00000000..a72158a1 --- /dev/null +++ b/src/MetalinkEntry.h @@ -0,0 +1,55 @@ +/* */ +#ifndef _D_METALINK_ENTRY_H_ +#define _D_METALINK_ENTRY_H_ + +#include "common.h" +#include "MetalinkResource.h" +#include + +typedef deque 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_ diff --git a/src/MetalinkProcessor.h b/src/MetalinkProcessor.h new file mode 100644 index 00000000..15eace21 --- /dev/null +++ b/src/MetalinkProcessor.h @@ -0,0 +1,35 @@ +/* */ +#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_ diff --git a/src/MetalinkResource.cc b/src/MetalinkResource.cc new file mode 100644 index 00000000..8b9e7c64 --- /dev/null +++ b/src/MetalinkResource.cc @@ -0,0 +1,26 @@ +/* */ +#include "MetalinkResource.h" + +MetalinkResource::MetalinkResource() {} + +MetalinkResource::~MetalinkResource() {} diff --git a/src/MetalinkResource.h b/src/MetalinkResource.h new file mode 100644 index 00000000..c3414eb4 --- /dev/null +++ b/src/MetalinkResource.h @@ -0,0 +1,47 @@ +/* */ +#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_ diff --git a/src/Metalinker.cc b/src/Metalinker.cc new file mode 100644 index 00000000..303ce97b --- /dev/null +++ b/src/Metalinker.cc @@ -0,0 +1,74 @@ +/* */ +#include "Metalinker.h" +#include + +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; + } +} diff --git a/src/Metalinker.h b/src/Metalinker.h new file mode 100644 index 00000000..8eb539d5 --- /dev/null +++ b/src/Metalinker.h @@ -0,0 +1,46 @@ +/* */ +#ifndef _D_METALINKER_H_ +#define _D_METALINKER_H_ + +#include "common.h" +#include "MetalinkEntry.h" +#include +#include + +typedef deque 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_ diff --git a/src/MultiDiskWriter.cc b/src/MultiDiskWriter.cc index 044796ff..3a67a920 100644 --- a/src/MultiDiskWriter.cc +++ b/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)); diff --git a/src/PeerConnection.cc b/src/PeerConnection.cc index b8d3fee9..aa3149e9 100644 --- a/src/PeerConnection.cc +++ b/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; diff --git a/src/PeerMessageUtil.cc b/src/PeerMessageUtil.cc index 54f9dc98..b13cb401 100644 --- a/src/PeerMessageUtil.cc +++ b/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); diff --git a/src/PieceMessage.cc b/src/PieceMessage.cc index 26ae4e69..073dc364 100644 --- a/src/PieceMessage.cc +++ b/src/PieceMessage.cc @@ -81,6 +81,7 @@ void PieceMessage::receivedAction() { onGotNewPiece(piece); } else { onGotWrongPiece(piece); + peerInteraction->abortPiece(piece); } } } diff --git a/src/Request.h b/src/Request.h index 26ffff1e..dcba6911 100644 --- a/src/Request.h +++ b/src/Request.h @@ -103,4 +103,6 @@ public: }; +typedef deque Requests; + #endif // _D_REQUEST_H_ diff --git a/src/SegmentMan.h b/src/SegmentMan.h index aee97cde..53112d6d 100644 --- a/src/SegmentMan.h +++ b/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(); diff --git a/src/ShaVisitor.cc b/src/ShaVisitor.cc index b8f8f96f..0781e4dd 100644 --- a/src/ShaVisitor.cc +++ b/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 } diff --git a/src/Util.cc b/src/Util.cc index c48dfc3d..ffc1e7a9 100644 --- a/src/Util.cc +++ b/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 #include #include @@ -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; +} diff --git a/src/Util.h b/src/Util.h index 69ad6d1d..75afb8cc 100644 --- a/src/Util.h +++ b/src/Util.h @@ -23,6 +23,9 @@ #define _D_UTIL_H_ #include "common.h" +#ifdef ENABLE_SHA1DIGEST +#include "messageDigest.h" +#endif // ENABLE_SHA1DIGEST #include #include #include @@ -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_ diff --git a/src/Xml2MetalinkProcessor.cc b/src/Xml2MetalinkProcessor.cc new file mode 100644 index 00000000..81011803 --- /dev/null +++ b/src/Xml2MetalinkProcessor.cc @@ -0,0 +1,188 @@ +/* */ +#include "Xml2MetalinkProcessor.h" +#include "DlAbortEx.h" +#include "Util.h" +#include +#include +#include + +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; +} diff --git a/src/Xml2MetalinkProcessor.h b/src/Xml2MetalinkProcessor.h new file mode 100644 index 00000000..65358a6f --- /dev/null +++ b/src/Xml2MetalinkProcessor.h @@ -0,0 +1,51 @@ +/* */ +#ifndef _D_XML2_METALINK_PROCESSOR_H_ +#define _D_XML2_METALINK_PROCESSOR_H_ + +#include "MetalinkProcessor.h" +#include +#include + +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_ diff --git a/src/main.cc b/src/main.cc index 59f77978..51dfd800 100644 --- a/src/main.cc +++ b/src/main.cc @@ -37,6 +37,7 @@ #include "TrackerUpdateCommand.h" #include "ByteArrayDiskWriter.h" #include "PeerChokeCommand.h" +#include "Xml2MetalinkProcessor.h" #include #include #include @@ -59,7 +60,10 @@ extern int optind, opterr, optopt; using namespace std; -typedef deque 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); - e->run(); + Requests reserved; + string downloadedFilename; + normalDownload(requests, reserved, op, dir, ufilename, downloadedFilename); + + 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(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; } diff --git a/src/messageDigest.h b/src/messageDigest.h index a5f6c268..a514441c 100644 --- a/src/messageDigest.h +++ b/src/messageDigest.h @@ -28,30 +28,79 @@ #ifdef HAVE_LIBSSL #include -#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) \ -{\ -int len;\ -EVP_DigestFinal_ex(&CTX, HASH, (unsigned int*)&len);\ -} -#define sha1DigestFree(CTX) EVP_MD_CTX_cleanup(&CTX) #endif // HAVE_LIBSSL #ifdef HAVE_LIBGCRYPT #include -#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) \ +#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) \ {\ -gcry_md_final(CTX);\ -memcpy(HASH, gcry_md_read(CTX, 0), 20);\ +int len;\ +EVP_DigestFinal_ex(&CTX.ctx, HASH, (unsigned int*)&len);\ } -#define sha1DigestFree(CTX) gcry_md_close(CTX) +#define digestFree(CTX) EVP_MD_CTX_cleanup(&CTX.ctx) + +#endif // HAVE_LIBSSL + +#ifdef HAVE_LIBGCRYPT +#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.ctx);\ +memcpy(HASH, gcry_md_read(CTX.ctx, 0), gcry_md_get_algo_dlen(CTX.algo));\ +} +#define digestFree(CTX) gcry_md_close(CTX.ctx) #endif // HAVE_LIBGCRYPT #endif // ENABLE_BITTORRENT diff --git a/test/Base64Test.cc b/test/Base64Test.cc index 9ace6f5b..e7497312 100644 --- a/test/Base64Test.cc +++ b/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() { diff --git a/test/Makefile.am b/test/Makefile.am index 215c8903..e8fc2ca8 100644 --- a/test/Makefile.am +++ b/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@ diff --git a/test/Makefile.in b/test/Makefile.in index 2490ea5e..0c1606b2 100644 --- a/test/Makefile.in +++ b/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 $@ $<; \ diff --git a/test/UtilTest.cc b/test/UtilTest.cc index 2081d0a7..150c8f68 100644 --- a/test/UtilTest.cc +++ b/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)); +}