Explorar el Código

Merge remote-tracking branch 'donkult/debian/experimental' into debian/experimental

Conflicts:
	apt-pkg/acquire-item.cc
	apt-pkg/acquire-item.h
Michael Vogt hace 12 años
padre
commit
e110d7bf56
Se han modificado 52 ficheros con 2099 adiciones y 1163 borrados
  1. 102 114
      apt-pkg/acquire-item.cc
  2. 77 121
      apt-pkg/acquire-item.h
  3. 25 23
      apt-pkg/acquire-method.cc
  4. 4 6
      apt-pkg/acquire-method.h
  5. 24 16
      apt-pkg/acquire-worker.cc
  6. 1 1
      apt-pkg/aptconfiguration.cc
  7. 176 37
      apt-pkg/contrib/hashes.cc
  8. 145 20
      apt-pkg/contrib/hashes.h
  9. 18 22
      apt-pkg/deb/deblistparser.cc
  10. 2 2
      apt-pkg/deb/deblistparser.h
  11. 1 1
      apt-pkg/deb/debmetaindex.cc
  12. 51 50
      apt-pkg/deb/debrecords.cc
  13. 5 7
      apt-pkg/deb/debrecords.h
  14. 99 71
      apt-pkg/deb/debsrcrecords.cc
  15. 5 3
      apt-pkg/indexcopy.cc
  16. 1 1
      apt-pkg/indexrecords.cc
  17. 11 1
      apt-pkg/indexrecords.h
  18. 16 8
      apt-pkg/pkgcache.cc
  19. 3 1
      apt-pkg/pkgcache.h
  20. 58 41
      apt-pkg/pkgcachegen.cc
  21. 5 2
      apt-pkg/pkgcachegen.h
  22. 38 6
      apt-pkg/pkgrecords.h
  23. 13 4
      apt-pkg/srcrecords.h
  24. 131 78
      apt-pkg/tagfile.cc
  25. 42 21
      apt-pkg/tagfile.h
  26. 3 0
      apt-private/acqprogress.cc
  27. 8 5
      cmdline/apt-extracttemplates.cc
  28. 12 7
      cmdline/apt-get.cc
  29. 3 3
      doc/apt.conf.5.xml
  30. 61 133
      ftparchive/cachedb.cc
  31. 11 20
      ftparchive/cachedb.h
  32. 154 170
      ftparchive/writer.cc
  33. 5 9
      ftparchive/writer.h
  34. 42 4
      methods/server.cc
  35. 1 1
      methods/server.h
  36. 7 7
      test/integration/framework
  37. 2 2
      test/integration/test-apt-ftparchive-cachedb
  38. 10 5
      test/integration/test-apt-ftparchive-src-cachedb
  39. 24 24
      test/integration/test-apt-sources-deb822
  40. 1 1
      test/integration/test-bug-595691-empty-and-broken-archive-files
  41. 3 6
      test/integration/test-bug-712435-missing-descriptions
  42. 4 4
      test/integration/test-bug-722207-print-uris-even-if-very-quiet
  43. 29 0
      test/integration/test-bug-745036-new-foreign-invalidates-cache
  44. 51 0
      test/integration/test-bug-747261-arch-specific-conflicts
  45. 0 77
      test/integration/test-debsrc-hashes
  46. 43 0
      test/integration/test-http-pipeline-messup
  47. 1 1
      test/integration/test-kernel-helper-autoremove
  48. 0 6
      test/integration/test-pdiff-usage
  49. 262 0
      test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum
  50. 1 1
      test/integration/test-ubuntu-bug-346386-apt-get-update-paywall
  51. 129 20
      test/libapt/hashsums_test.cc
  52. 179 0
      test/libapt/tagfile_test.cc

+ 102 - 114
apt-pkg/acquire-item.cc

@@ -50,13 +50,24 @@
 
 
 using namespace std;
 using namespace std;
 
 
+static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
+{
+   if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
+      return;
+   std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
+   for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
+      std::cerr <<  "\t- " << hs->toStr() << std::endl;
+   std::cerr << " Actual Hash: " << std::endl;
+   for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
+      std::cerr <<  "\t- " << hs->toStr() << std::endl;
+}
+									/*}}}*/
+
 // Acquire::Item::Item - Constructor					/*{{{*/
 // Acquire::Item::Item - Constructor					/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
-                       PartialSize(0), Mode(0), ID(0), Complete(false), 
-                       Local(false), QueueCounter(0), 
-                       ExpectedAdditionalItems(0)
+pkgAcquire::Item::Item(pkgAcquire *Owner, HashStringList const &ExpectedHashes) :
+   Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
+   Local(false), QueueCounter(0), ExpectedAdditionalItems(0),
+   ExpectedHashes(ExpectedHashes)
 {
 {
    Owner->Add(this);
    Owner->Add(this);
    Status = StatIdle;
    Status = StatIdle;
@@ -118,7 +129,7 @@ void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
 // Acquire::Item::Done - Item downloaded OK				/*{{{*/
 // Acquire::Item::Done - Item downloaded OK				/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-void pkgAcquire::Item::Done(string Message,unsigned long long Size,string /*Hash*/,
+void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringList const &/*Hash*/,
 			    pkgAcquire::MethodConfig * /*Cnf*/)
 			    pkgAcquire::MethodConfig * /*Cnf*/)
 {
 {
    // We just downloaded something..
    // We just downloaded something..
@@ -229,8 +240,8 @@ void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
    possibly query additional files */
    possibly query additional files */
 pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
 pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
 				 string const &URIDesc, string const &ShortDesc,
 				 string const &URIDesc, string const &ShortDesc,
-				 HashString const &ExpectedHash)
-   : Item(Owner), ExpectedHash(ExpectedHash)
+				 HashStringList const &ExpectedHashes)
+   : Item(Owner, ExpectedHashes)
 {
 {
    /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
    /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
    Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
    Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
@@ -252,7 +263,7 @@ pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
 // AcqSubIndex::Custom600Headers - Insert custom request headers	/*{{{*/
 // AcqSubIndex::Custom600Headers - Insert custom request headers	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqSubIndex::Custom600Headers()
+string pkgAcqSubIndex::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(Desc.URI);
    Final += URItoFileName(Desc.URI);
@@ -275,7 +286,7 @@ void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*
    // No good Index is provided
    // No good Index is provided
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqSubIndex::Done(string Message,unsigned long long Size,string Md5Hash,	/*{{{*/
+void pkgAcqSubIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
 			   pkgAcquire::MethodConfig *Cnf)
 			   pkgAcquire::MethodConfig *Cnf)
 {
 {
    if(Debug)
    if(Debug)
@@ -297,7 +308,7 @@ void pkgAcqSubIndex::Done(string Message,unsigned long long Size,string Md5Hash,
       return;
       return;
    }
    }
 
 
-   Item::Done(Message,Size,Md5Hash,Cnf);
+   Item::Done(Message, Size, Hashes, Cnf);
 
 
    string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
    string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
 
 
@@ -343,10 +354,10 @@ bool pkgAcqSubIndex::ParseIndex(string const &IndexFile)		/*{{{*/
  * the original packages file
  * the original packages file
  */
  */
 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
-                                 IndexTarget const *Target,
-				 HashString ExpectedHash,
+                                 IndexTarget const * const Target,
+				 HashStringList const &ExpectedHashes,
                                  indexRecords *MetaIndexParser)
                                  indexRecords *MetaIndexParser)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser)
+   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser)
 {
 {
    
    
    Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
    Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
@@ -391,7 +402,7 @@ pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqDiffIndex::Custom600Headers()
+string pkgAcqDiffIndex::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(RealURI) + string(".IndexDiff");
    Final += URItoFileName(RealURI) + string(".IndexDiff");
@@ -444,7 +455,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)		/*{{{*/
 	    std::clog << "Package file is up-to-date" << std::endl;
 	    std::clog << "Package file is up-to-date" << std::endl;
 	 // list cleanup needs to know that this file as well as the already
 	 // list cleanup needs to know that this file as well as the already
 	 // present index is ours, so we create an empty diff to save it for us
 	 // present index is ours, so we create an empty diff to save it for us
-	 new pkgAcqIndexDiffs(Owner, Target, ExpectedHash, MetaIndexParser, 
+	 new pkgAcqIndexDiffs(Owner, Target, ExpectedHashes, MetaIndexParser, 
                               ServerSha1, available_patches);
                               ServerSha1, available_patches);
 	 return true;
 	 return true;
       }
       }
@@ -531,7 +542,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)		/*{{{*/
 
 
 	 if (pdiff_merge == false)
 	 if (pdiff_merge == false)
          {
          {
-	    new pkgAcqIndexDiffs(Owner, Target, ExpectedHash, MetaIndexParser,
+	    new pkgAcqIndexDiffs(Owner, Target, ExpectedHashes, MetaIndexParser,
                                  ServerSha1, available_patches);
                                  ServerSha1, available_patches);
          }
          }
          else
          else
@@ -539,7 +550,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)		/*{{{*/
 	    std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
 	    std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
 	    for(size_t i = 0; i < available_patches.size(); ++i)
 	    for(size_t i = 0; i < available_patches.size(); ++i)
 	       (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, Target,
 	       (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, Target,
-                                                       ExpectedHash,
+                                                       ExpectedHashes,
                                                        MetaIndexParser,
                                                        MetaIndexParser,
                                                        available_patches[i],
                                                        available_patches[i],
                                                        diffs);
                                                        diffs);
@@ -566,20 +577,20 @@ void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/
       std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
       std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
 		<< "Falling back to normal index file acquire" << std::endl;
 		<< "Falling back to normal index file acquire" << std::endl;
 
 
-   new pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser);
+   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
 
 
    Complete = false;
    Complete = false;
    Status = StatDone;
    Status = StatDone;
    Dequeue();
    Dequeue();
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,string Md5Hash,	/*{{{*/
+void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
 			   pkgAcquire::MethodConfig *Cnf)
 			   pkgAcquire::MethodConfig *Cnf)
 {
 {
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
       std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
 
 
-   Item::Done(Message,Size,Md5Hash,Cnf);
+   Item::Done(Message, Size, Hashes, Cnf);
 
 
    string FinalFile;
    string FinalFile;
    FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
    FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
@@ -610,11 +621,11 @@ void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,string Md5Hash
  */
  */
 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
                                    struct IndexTarget const * const Target,
                                    struct IndexTarget const * const Target,
-                                   HashString ExpectedHash,
+                                   HashStringList const &ExpectedHashes,
                                    indexRecords *MetaIndexParser,
                                    indexRecords *MetaIndexParser,
 				   string ServerSha1,
 				   string ServerSha1,
 				   vector<DiffInfo> diffs)
 				   vector<DiffInfo> diffs)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser),
+   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser),
      available_patches(diffs), ServerSha1(ServerSha1)
      available_patches(diffs), ServerSha1(ServerSha1)
 {
 {
    
    
@@ -646,7 +657,7 @@ void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
       std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
 		<< "Falling back to normal index file acquire" << std::endl;
 		<< "Falling back to normal index file acquire" << std::endl;
-   new pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser);
+   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
    Finish();
    Finish();
 }
 }
 									/*}}}*/
 									/*}}}*/
@@ -660,7 +671,7 @@ void pkgAcqIndexDiffs::Finish(bool allDone)
       DestFile = _config->FindDir("Dir::State::lists");
       DestFile = _config->FindDir("Dir::State::lists");
       DestFile += URItoFileName(RealURI);
       DestFile += URItoFileName(RealURI);
 
 
-      if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
+      if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
       {
       {
 	 RenameOnError(HashSumMismatch);
 	 RenameOnError(HashSumMismatch);
 	 Dequeue();
 	 Dequeue();
@@ -738,13 +749,13 @@ bool pkgAcqIndexDiffs::QueueNextDiff()					/*{{{*/
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Hash,	/*{{{*/
+void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes,	/*{{{*/
 			    pkgAcquire::MethodConfig *Cnf)
 			    pkgAcquire::MethodConfig *Cnf)
 {
 {
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
       std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
 
 
-   Item::Done(Message,Size,Md5Hash,Cnf);
+   Item::Done(Message, Size, Hashes, Cnf);
 
 
    string FinalFile;
    string FinalFile;
    FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
    FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
@@ -787,7 +798,7 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Has
       // see if there is more to download
       // see if there is more to download
       if(available_patches.empty() == false) {
       if(available_patches.empty() == false) {
 	 new pkgAcqIndexDiffs(Owner, Target,
 	 new pkgAcqIndexDiffs(Owner, Target,
-			      ExpectedHash, MetaIndexParser,
+			      ExpectedHashes, MetaIndexParser,
                               ServerSha1, available_patches);
                               ServerSha1, available_patches);
 	 return Finish();
 	 return Finish();
       } else 
       } else 
@@ -798,11 +809,11 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Has
 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor			/*{{{*/
 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor			/*{{{*/
 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
                                              struct IndexTarget const * const Target,
                                              struct IndexTarget const * const Target,
-                                             HashString ExpectedHash,
+                                             HashStringList const &ExpectedHashes,
                                              indexRecords *MetaIndexParser,
                                              indexRecords *MetaIndexParser,
                                              DiffInfo const &patch,
                                              DiffInfo const &patch,
                                              std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
                                              std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser),
+   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser),
      patch(patch), allPatches(allPatches), State(StateFetchDiff)
      patch(patch), allPatches(allPatches), State(StateFetchDiff)
 {
 {
 
 
@@ -845,16 +856,16 @@ void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*C
    // first failure means we should fallback
    // first failure means we should fallback
    State = StateErrorDiff;
    State = StateErrorDiff;
    std::clog << "Falling back to normal index file acquire" << std::endl;
    std::clog << "Falling back to normal index file acquire" << std::endl;
-   new pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser);
+   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,string Md5Hash,	/*{{{*/
+void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
 			    pkgAcquire::MethodConfig *Cnf)
 			    pkgAcquire::MethodConfig *Cnf)
 {
 {
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
       std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
 
 
-   Item::Done(Message,Size,Md5Hash,Cnf);
+   Item::Done(Message,Size,Hashes,Cnf);
 
 
    string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
    string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
 
 
@@ -890,7 +901,7 @@ void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,string M
    else if (State == StateApplyDiff)
    else if (State == StateApplyDiff)
    {
    {
       // see if we really got the expected file
       // see if we really got the expected file
-      if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
+      if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
       {
       {
 	 RenameOnError(HashSumMismatch);
 	 RenameOnError(HashSumMismatch);
 	 return;
 	 return;
@@ -927,7 +938,7 @@ void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,string M
    instantiated to fetch the revision file */   
    instantiated to fetch the revision file */   
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
 			 string URI,string URIDesc,string ShortDesc,
 			 string URI,string URIDesc,string ShortDesc,
-			 HashString ExpectedHash, string comprExt)
+			 HashStringList const  &ExpectedHash, string comprExt)
    : pkgAcqBaseIndex(Owner, NULL, ExpectedHash, NULL), RealURI(URI)
    : pkgAcqBaseIndex(Owner, NULL, ExpectedHash, NULL), RealURI(URI)
 {
 {
    if(comprExt.empty() == true)
    if(comprExt.empty() == true)
@@ -946,14 +957,15 @@ pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
    Init(URI, URIDesc, ShortDesc);
    Init(URI, URIDesc, ShortDesc);
 }
 }
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
-			 HashString const &ExpectedHash, indexRecords *MetaIndexParser)
+			 HashStringList const &ExpectedHash, 
+                         indexRecords *MetaIndexParser)
    : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser), 
    : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser), 
      RealURI(Target->URI)
      RealURI(Target->URI)
 {
 {
    // autoselect the compression method
    // autoselect the compression method
    std::vector<std::string> types = APT::Configuration::getCompressionTypes();
    std::vector<std::string> types = APT::Configuration::getCompressionTypes();
    CompressionExtension = "";
    CompressionExtension = "";
-   if (ExpectedHash.empty() == false)
+   if (ExpectedHashes.usable())
    {
    {
       for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
       for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
 	 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
 	 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
@@ -1018,7 +1030,7 @@ void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &S
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqIndex::Custom600Headers()
+string pkgAcqIndex::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(RealURI);
    Final += URItoFileName(RealURI);
@@ -1064,22 +1076,17 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)	/*{{{*/
    to the uncompressed version of the file. If this is so the file
    to the uncompressed version of the file. If this is so the file
    is copied into the partial directory. In all other cases the file
    is copied into the partial directory. In all other cases the file
    is decompressed with a gzip uri. */
    is decompressed with a gzip uri. */
-void pkgAcqIndex::Done(string Message,unsigned long long Size,string Hash,
+void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,
 		       pkgAcquire::MethodConfig *Cfg)
 		       pkgAcquire::MethodConfig *Cfg)
 {
 {
-   Item::Done(Message,Size,Hash,Cfg);
+   Item::Done(Message,Size,Hashes,Cfg);
 
 
    if (Decompression == true)
    if (Decompression == true)
    {
    {
-      if (_config->FindB("Debug::pkgAcquire::Auth", false))
-      {
-         std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
-         std::cerr << "  Expected Hash: " << ExpectedHash.toStr() << std::endl;
-      }
-
-      if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
+      if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
       {
       {
 	 RenameOnError(HashSumMismatch);
 	 RenameOnError(HashSumMismatch);
+	 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
          return;
          return;
       }
       }
 
 
@@ -1201,12 +1208,12 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,string Hash,
 /* The Translation file is added to the queue */
 /* The Translation file is added to the queue */
 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
 			    string URI,string URIDesc,string ShortDesc) 
 			    string URI,string URIDesc,string ShortDesc) 
-  : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
+  : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList(), "")
 {
 {
 }
 }
-pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target,
-			 HashString const &ExpectedHash, indexRecords *MetaIndexParser)
-  : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser)
+pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const * const Target,
+			 HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser)
+  : pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser)
 {
 {
    // load the filesize
    // load the filesize
    indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
    indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
@@ -1216,7 +1223,7 @@ pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target,
 									/*}}}*/
 									/*}}}*/
 // AcqIndexTrans::Custom600Headers - Insert custom request headers	/*{{{*/
 // AcqIndexTrans::Custom600Headers - Insert custom request headers	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
-string pkgAcqIndexTrans::Custom600Headers()
+string pkgAcqIndexTrans::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(RealURI);
    Final += URItoFileName(RealURI);
@@ -1260,7 +1267,7 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,				/*{{{*/
 			     string MetaIndexShortDesc,
 			     string MetaIndexShortDesc,
 			     const vector<IndexTarget*>* IndexTargets,
 			     const vector<IndexTarget*>* IndexTargets,
 			     indexRecords* MetaIndexParser) :
 			     indexRecords* MetaIndexParser) :
-   Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
+   Item(Owner, HashStringList()), RealURI(URI), MetaIndexURI(MetaIndexURI),
    MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
    MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
    MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
    MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
 {
 {
@@ -1313,7 +1320,7 @@ pkgAcqMetaSig::~pkgAcqMetaSig()						/*{{{*/
 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers	/*{{{*/
 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqMetaSig::Custom600Headers()
+string pkgAcqMetaSig::Custom600Headers() const
 {
 {
    struct stat Buf;
    struct stat Buf;
    if (stat(LastGoodSig.c_str(),&Buf) != 0)
    if (stat(LastGoodSig.c_str(),&Buf) != 0)
@@ -1322,10 +1329,10 @@ string pkgAcqMetaSig::Custom600Headers()
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
 }
 
 
-void pkgAcqMetaSig::Done(string Message,unsigned long long Size,string MD5,
+void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
 			 pkgAcquire::MethodConfig *Cfg)
 			 pkgAcquire::MethodConfig *Cfg)
 {
 {
-   Item::Done(Message,Size,MD5,Cfg);
+   Item::Done(Message, Size, Hashes, Cfg);
 
 
    string FileName = LookupTag(Message,"Filename");
    string FileName = LookupTag(Message,"Filename");
    if (FileName.empty() == true)
    if (FileName.empty() == true)
@@ -1405,9 +1412,9 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,			/*{{{*/
 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,			/*{{{*/
 				 string URI,string URIDesc,string ShortDesc,
 				 string URI,string URIDesc,string ShortDesc,
 				 string SigFile,
 				 string SigFile,
-				 const vector<struct IndexTarget*>* IndexTargets,
+				 const vector<IndexTarget*>* IndexTargets,
 				 indexRecords* MetaIndexParser) :
 				 indexRecords* MetaIndexParser) :
-   Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
+   Item(Owner, HashStringList()), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
    MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
    MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
 {
 {
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
@@ -1428,7 +1435,7 @@ pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,			/*{{{*/
 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers	/*{{{*/
 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqMetaIndex::Custom600Headers()
+string pkgAcqMetaIndex::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(RealURI);
    Final += URItoFileName(RealURI);
@@ -1440,10 +1447,10 @@ string pkgAcqMetaIndex::Custom600Headers()
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,string Hash,	/*{{{*/
+void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
 			   pkgAcquire::MethodConfig *Cfg)
 			   pkgAcquire::MethodConfig *Cfg)
 {
 {
-   Item::Done(Message,Size,Hash,Cfg);
+   Item::Done(Message,Size,Hashes,Cfg);
 
 
    // MetaIndexes are done in two passes: one to download the
    // MetaIndexes are done in two passes: one to download the
    // metaindex with an appropriate method, and a second to verify it
    // metaindex with an appropriate method, and a second to verify it
@@ -1614,12 +1621,12 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)				/*{{{*/
    // at this point the real Items are loaded in the fetcher
    // at this point the real Items are loaded in the fetcher
    ExpectedAdditionalItems = 0;
    ExpectedAdditionalItems = 0;
 
 
-   for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
+   for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
         Target != IndexTargets->end();
         Target != IndexTargets->end();
         ++Target)
         ++Target)
    {
    {
-      HashString ExpectedIndexHash;
-      indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
+      HashStringList ExpectedIndexHashes;
+      const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
       bool compressedAvailable = false;
       bool compressedAvailable = false;
       if (Record == NULL)
       if (Record == NULL)
       {
       {
@@ -1642,14 +1649,16 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)				/*{{{*/
       }
       }
       else
       else
       {
       {
-	 ExpectedIndexHash = Record->Hash;
+	 ExpectedIndexHashes = Record->Hashes;
 	 if (_config->FindB("Debug::pkgAcquire::Auth", false))
 	 if (_config->FindB("Debug::pkgAcquire::Auth", false))
 	 {
 	 {
-	    std::cerr << "Queueing: " << (*Target)->URI << std::endl;
-	    std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
+	    std::cerr << "Queueing: " << (*Target)->URI << std::endl
+	       << "Expected Hash:" << std::endl;
+	    for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
+	       std::cerr <<  "\t- " << hs->toStr() << std::endl;
 	    std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
 	    std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
 	 }
 	 }
-	 if (verify == true && ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false)
+	 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
 	 {
 	 {
 	    Status = StatAuthError;
 	    Status = StatAuthError;
 	    strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
 	    strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
@@ -1661,14 +1670,14 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)				/*{{{*/
       {
       {
 	 if ((*Target)->IsSubIndex() == true)
 	 if ((*Target)->IsSubIndex() == true)
 	    new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
 	    new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
-				(*Target)->ShortDesc, ExpectedIndexHash);
+				(*Target)->ShortDesc, ExpectedIndexHashes);
 	 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
 	 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
 	 {
 	 {
 	    if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
 	    if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
 		MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
 		MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
-	       new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
+	       new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
 	    else
 	    else
-	       new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
+	       new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
 	 }
 	 }
 	 continue;
 	 continue;
       }
       }
@@ -1679,9 +1688,9 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)				/*{{{*/
          instead, but passing the required info to it is to much hassle */
          instead, but passing the required info to it is to much hassle */
       if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
       if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
 	  MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
 	  MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
-	 new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
+	 new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
       else
       else
-	 new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
+	 new pkgAcqIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
    }
    }
 }
 }
 									/*}}}*/
 									/*}}}*/
@@ -1834,7 +1843,7 @@ pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner,		/*{{{*/
 		string const &URI, string const &URIDesc, string const &ShortDesc,
 		string const &URI, string const &URIDesc, string const &ShortDesc,
 		string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
 		string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
 		string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
 		string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
-		const vector<struct IndexTarget*>* IndexTargets,
+		const vector<IndexTarget*>* IndexTargets,
 		indexRecords* MetaIndexParser) :
 		indexRecords* MetaIndexParser) :
 	pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
 	pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
 	MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
 	MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
@@ -1870,7 +1879,7 @@ pkgAcqMetaClearSig::~pkgAcqMetaClearSig()				/*{{{*/
 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers	/*{{{*/
 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 // FIXME: this can go away once the InRelease file is used widely
 // FIXME: this can go away once the InRelease file is used widely
-string pkgAcqMetaClearSig::Custom600Headers()
+string pkgAcqMetaClearSig::Custom600Headers() const
 {
 {
    string Final = _config->FindDir("Dir::State::lists");
    string Final = _config->FindDir("Dir::State::lists");
    Final += URItoFileName(RealURI);
    Final += URItoFileName(RealURI);
@@ -1919,7 +1928,7 @@ void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*
 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
 			     pkgRecords *Recs,pkgCache::VerIterator const &Version,
 			     pkgRecords *Recs,pkgCache::VerIterator const &Version,
 			     string &StoreFilename) :
 			     string &StoreFilename) :
-               Item(Owner), Version(Version), Sources(Sources), Recs(Recs), 
+               Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs), 
                StoreFilename(StoreFilename), Vf(Version.FileList()), 
                StoreFilename(StoreFilename), Vf(Version.FileList()), 
 	       Trusted(false)
 	       Trusted(false)
 {
 {
@@ -2004,7 +2013,6 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
    checking later. */
    checking later. */
 bool pkgAcqArchive::QueueNext()
 bool pkgAcqArchive::QueueNext()
 {
 {
-   string const ForceHash = _config->Find("Acquire::ForceHash");
    for (; Vf.end() == false; ++Vf)
    for (; Vf.end() == false; ++Vf)
    {
    {
       // Ignore not source sources
       // Ignore not source sources
@@ -2025,31 +2033,10 @@ bool pkgAcqArchive::QueueNext()
       pkgRecords::Parser &Parse = Recs->Lookup(Vf);
       pkgRecords::Parser &Parse = Recs->Lookup(Vf);
       if (_error->PendingError() == true)
       if (_error->PendingError() == true)
 	 return false;
 	 return false;
-      
+
       string PkgFile = Parse.FileName();
       string PkgFile = Parse.FileName();
-      if (ForceHash.empty() == false)
-      {
-	 if(stringcasecmp(ForceHash, "sha512") == 0)
-	    ExpectedHash = HashString("SHA512", Parse.SHA512Hash());
-	 else if(stringcasecmp(ForceHash, "sha256") == 0)
-	    ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
-	 else if (stringcasecmp(ForceHash, "sha1") == 0)
-	    ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
-	 else
-	    ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
-      }
-      else
-      {
-	 string Hash;
-	 if ((Hash = Parse.SHA512Hash()).empty() == false)
-	    ExpectedHash = HashString("SHA512", Hash);
-	 else if ((Hash = Parse.SHA256Hash()).empty() == false)
-	    ExpectedHash = HashString("SHA256", Hash);
-	 else if ((Hash = Parse.SHA1Hash()).empty() == false)
-	    ExpectedHash = HashString("SHA1", Hash);
-	 else
-	    ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
-      }
+      ExpectedHashes = Parse.Hashes();
+
       if (PkgFile.empty() == true)
       if (PkgFile.empty() == true)
 	 return _error->Error(_("The package index files are corrupted. No Filename: "
 	 return _error->Error(_("The package index files are corrupted. No Filename: "
 			      "field for package %s."),
 			      "field for package %s."),
@@ -2136,10 +2123,10 @@ bool pkgAcqArchive::QueueNext()
 // AcqArchive::Done - Finished fetching					/*{{{*/
 // AcqArchive::Done - Finished fetching					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
+void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
 			 pkgAcquire::MethodConfig *Cfg)
 			 pkgAcquire::MethodConfig *Cfg)
 {
 {
-   Item::Done(Message,Size,CalcHash,Cfg);
+   Item::Done(Message, Size, CalcHashes, Cfg);
    
    
    // Check the size
    // Check the size
    if (Size != Version->Size)
    if (Size != Version->Size)
@@ -2147,12 +2134,12 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
       RenameOnError(SizeMismatch);
       RenameOnError(SizeMismatch);
       return;
       return;
    }
    }
-   
-   // Check the hash
+
    // FIXME: could this empty() check impose *any* sort of security issue?
    // FIXME: could this empty() check impose *any* sort of security issue?
-   if(ExpectedHash.empty() == false && ExpectedHash.toStr() != CalcHash)
+   if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
    {
    {
       RenameOnError(HashSumMismatch);
       RenameOnError(HashSumMismatch);
+      printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
       return;
       return;
    }
    }
 
 
@@ -2224,7 +2211,7 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 									/*}}}*/
 									/*}}}*/
 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
-APT_PURE bool pkgAcqArchive::IsTrusted()
+APT_PURE bool pkgAcqArchive::IsTrusted() const
 {
 {
    return Trusted;
    return Trusted;
 }
 }
@@ -2243,11 +2230,11 @@ void pkgAcqArchive::Finished()
 // AcqFile::pkgAcqFile - Constructor					/*{{{*/
 // AcqFile::pkgAcqFile - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The file is added to the queue */
 /* The file is added to the queue */
-pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
+pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
 		       unsigned long long Size,string Dsc,string ShortDesc,
 		       unsigned long long Size,string Dsc,string ShortDesc,
 		       const string &DestDir, const string &DestFilename,
 		       const string &DestDir, const string &DestFilename,
                        bool IsIndexFile) :
                        bool IsIndexFile) :
-                       Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile)
+                       Item(Owner, Hashes), IsIndexFile(IsIndexFile)
 {
 {
    Retries = _config->FindI("Acquire::Retries",0);
    Retries = _config->FindI("Acquire::Retries",0);
    
    
@@ -2284,15 +2271,16 @@ pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
 // AcqFile::Done - Item downloaded OK					/*{{{*/
 // AcqFile::Done - Item downloaded OK					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-void pkgAcqFile::Done(string Message,unsigned long long Size,string CalcHash,
+void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
 		      pkgAcquire::MethodConfig *Cnf)
 		      pkgAcquire::MethodConfig *Cnf)
 {
 {
-   Item::Done(Message,Size,CalcHash,Cnf);
+   Item::Done(Message,Size,CalcHashes,Cnf);
 
 
    // Check the hash
    // Check the hash
-   if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
+   if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
    {
    {
       RenameOnError(HashSumMismatch);
       RenameOnError(HashSumMismatch);
+      printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
       return;
       return;
    }
    }
    
    
@@ -2363,7 +2351,7 @@ void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
 /* The only header we use is the last-modified header. */
-string pkgAcqFile::Custom600Headers()
+string pkgAcqFile::Custom600Headers() const
 {
 {
    if (IsIndexFile)
    if (IsIndexFile)
       return "\nIndex-File: true";
       return "\nIndex-File: true";

+ 77 - 121
apt-pkg/acquire-item.h

@@ -46,6 +46,7 @@
 class indexRecords;
 class indexRecords;
 class pkgRecords;
 class pkgRecords;
 class pkgSourceList;
 class pkgSourceList;
+class IndexTarget;
 
 
 /** \brief Represents the process by which a pkgAcquire object should	{{{
 /** \brief Represents the process by which a pkgAcquire object should	{{{
  *  retrieve a file or a collection of files.
  *  retrieve a file or a collection of files.
@@ -211,12 +212,12 @@ class pkgAcquire::Item : public WeakPointable
     *  \param Message Data from the acquire method.  Use LookupTag()
     *  \param Message Data from the acquire method.  Use LookupTag()
     *  to parse it.
     *  to parse it.
     *  \param Size The size of the object that was fetched.
     *  \param Size The size of the object that was fetched.
-    *  \param Hash The HashSum of the object that was fetched.
+    *  \param Hashes The HashSums of the object that was fetched.
     *  \param Cnf The method via which the object was fetched.
     *  \param Cnf The method via which the object was fetched.
     *
     *
     *  \sa pkgAcqMethod
     *  \sa pkgAcqMethod
     */
     */
-   virtual void Done(std::string Message,unsigned long long Size,std::string Hash,
+   virtual void Done(std::string Message, unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
 
 
    /** \brief Invoked when the worker starts to fetch this object.
    /** \brief Invoked when the worker starts to fetch this object.
@@ -238,34 +239,35 @@ class pkgAcquire::Item : public WeakPointable
     *  line, so they should (if nonempty) have a leading newline and
     *  line, so they should (if nonempty) have a leading newline and
     *  no trailing newline.
     *  no trailing newline.
     */
     */
-   virtual std::string Custom600Headers() {return std::string();};
+   virtual std::string Custom600Headers() const {return std::string();};
 
 
    /** \brief A "descriptive" URI-like string.
    /** \brief A "descriptive" URI-like string.
     *
     *
     *  \return a URI that should be used to describe what is being fetched.
     *  \return a URI that should be used to describe what is being fetched.
     */
     */
-   virtual std::string DescURI() = 0;
+   virtual std::string DescURI() const = 0;
    /** \brief Short item description.
    /** \brief Short item description.
     *
     *
     *  \return a brief description of the object being fetched.
     *  \return a brief description of the object being fetched.
     */
     */
-   virtual std::string ShortDesc() {return DescURI();}
+   virtual std::string ShortDesc() const {return DescURI();}
 
 
    /** \brief Invoked by the worker when the download is completely done. */
    /** \brief Invoked by the worker when the download is completely done. */
    virtual void Finished() {};
    virtual void Finished() {};
    
    
-   /** \brief HashSum 
+   /** \brief HashSums
     *
     *
-    *  \return the HashSum of this object, if applicable; otherwise, an
-    *  empty string.
+    *  \return the HashSums of this object, if applicable; otherwise, an
+    *  empty list.
     */
     */
-   virtual std::string HashSum() {return std::string();};
+   HashStringList HashSums() const {return ExpectedHashes;};
+   std::string HashSum() const {HashStringList const hashes = HashSums(); HashString const * const hs = hashes.find(NULL); return hs != NULL ? hs->toStr() : ""; };
 
 
    /** \return the acquire process with which this item is associated. */
    /** \return the acquire process with which this item is associated. */
-   pkgAcquire *GetOwner() {return Owner;};
+   pkgAcquire *GetOwner() const {return Owner;};
 
 
    /** \return \b true if this object is being fetched from a trusted source. */
    /** \return \b true if this object is being fetched from a trusted source. */
-   virtual bool IsTrusted() {return false;};
+   virtual bool IsTrusted() const {return false;};
    
    
    // report mirror problems
    // report mirror problems
    /** \brief Report mirror problem
    /** \brief Report mirror problem
@@ -284,12 +286,10 @@ class pkgAcquire::Item : public WeakPointable
     *  process, but does not place it into any fetch queues (you must
     *  process, but does not place it into any fetch queues (you must
     *  manually invoke QueueURI() to do so).
     *  manually invoke QueueURI() to do so).
     *
     *
-    *  Initializes all fields of the item other than Owner to 0,
-    *  false, or the empty string.
-    *
     *  \param Owner The new owner of this item.
     *  \param Owner The new owner of this item.
+    *  \param ExpectedHashes of the file represented by this item
     */
     */
-   Item(pkgAcquire *Owner);
+   Item(pkgAcquire *Owner, HashStringList const &ExpectedHashes);
 
 
    /** \brief Remove this item from its owner's queue by invoking
    /** \brief Remove this item from its owner's queue by invoking
     *  pkgAcquire::Remove.
     *  pkgAcquire::Remove.
@@ -309,6 +309,12 @@ class pkgAcquire::Item : public WeakPointable
     * \param state respresenting the error we encountered
     * \param state respresenting the error we encountered
     */
     */
    bool RenameOnError(RenameOnErrorState const state);
    bool RenameOnError(RenameOnErrorState const state);
+
+   /** \brief The HashSums of the item is supposed to have than done */
+   HashStringList ExpectedHashes;
+
+   /** \brief The item that is currently being downloaded. */
+   pkgAcquire::ItemDesc Desc;
 };
 };
 									/*}}}*/
 									/*}}}*/
 /** \brief Information about an index patch (aka diff). */		/*{{{*/
 /** \brief Information about an index patch (aka diff). */		/*{{{*/
@@ -335,20 +341,13 @@ class pkgAcqSubIndex : public pkgAcquire::Item
    /** \brief If \b true, debugging information will be written to std::clog. */
    /** \brief If \b true, debugging information will be written to std::clog. */
    bool Debug;
    bool Debug;
 
 
-   /** \brief The item that is currently being downloaded. */
-   pkgAcquire::ItemDesc Desc;
-
-   /** \brief The Hash that this file should have after download
-    */
-   HashString ExpectedHash;
-
  public:
  public:
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return Desc.URI;};
-   virtual std::string Custom600Headers();
+   virtual std::string DescURI() const {return Desc.URI;};
+   virtual std::string Custom600Headers() const;
    virtual bool ParseIndex(std::string const &IndexFile);
    virtual bool ParseIndex(std::string const &IndexFile);
 
 
    /** \brief Create a new pkgAcqSubIndex.
    /** \brief Create a new pkgAcqSubIndex.
@@ -361,10 +360,10 @@ class pkgAcqSubIndex : public pkgAcquire::Item
     *
     *
     *  \param ShortDesc A short description of the list file to download.
     *  \param ShortDesc A short description of the list file to download.
     *
     *
-    *  \param ExpectedHash The list file's MD5 signature.
+    *  \param ExpectedHashes The list file's hashsums which are expected.
     */
     */
    pkgAcqSubIndex(pkgAcquire *Owner, std::string const &URI,std::string const &URIDesc,
    pkgAcqSubIndex(pkgAcquire *Owner, std::string const &URI,std::string const &URIDesc,
-		   std::string const &ShortDesc, HashString const &ExpectedHash);
+		   std::string const &ShortDesc, HashStringList const &ExpectedHashes);
 };
 };
 									/*}}}*/
 									/*}}}*/
 
 
@@ -379,16 +378,13 @@ class pkgAcqBaseIndex : public pkgAcquire::Item
    const struct IndexTarget * Target;
    const struct IndexTarget * Target;
    indexRecords *MetaIndexParser;
    indexRecords *MetaIndexParser;
 
 
-   /** \brief The Hash that this file should have after download
-    */
-   HashString ExpectedHash;
-
    pkgAcqBaseIndex(pkgAcquire *Owner,
    pkgAcqBaseIndex(pkgAcquire *Owner,
                    struct IndexTarget const * const Target,
                    struct IndexTarget const * const Target,
-                   HashString ExpectedHash,
+                   HashStringList const &ExpectedHashes,
                    indexRecords *MetaIndexParser)
                    indexRecords *MetaIndexParser)
-      : Item(Owner), Target(Target), MetaIndexParser(MetaIndexParser),
-        ExpectedHash(ExpectedHash) {};
+      : Item(Owner, ExpectedHashes), Target(Target), 
+        MetaIndexParser(MetaIndexParser) {};
+
 };
 };
 									/*}}}*/
 									/*}}}*/
 /** \brief An item that is responsible for fetching an index file of	{{{
 /** \brief An item that is responsible for fetching an index file of	{{{
@@ -406,9 +402,6 @@ class pkgAcqDiffIndex : public pkgAcqBaseIndex
    /** \brief If \b true, debugging information will be written to std::clog. */
    /** \brief If \b true, debugging information will be written to std::clog. */
    bool Debug;
    bool Debug;
 
 
-   /** \brief The item that is currently being downloaded. */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The URI of the index file to recreate at our end (either
    /** \brief The URI of the index file to recreate at our end (either
     *  by downloading it or by applying partial patches).
     *  by downloading it or by applying partial patches).
     */
     */
@@ -427,10 +420,10 @@ class pkgAcqDiffIndex : public pkgAcqBaseIndex
  public:
  public:
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return RealURI + "Index";};
-   virtual std::string Custom600Headers();
+   virtual std::string DescURI() const {return RealURI + "Index";};
+   virtual std::string Custom600Headers() const;
 
 
    /** \brief Parse the Index file for a set of Packages diffs.
    /** \brief Parse the Index file for a set of Packages diffs.
     *
     *
@@ -455,11 +448,11 @@ class pkgAcqDiffIndex : public pkgAcqBaseIndex
     *
     *
     *  \param ShortDesc A short description of the list file to download.
     *  \param ShortDesc A short description of the list file to download.
     *
     *
-    *  \param ExpectedHash The list file's MD5 signature.
+    *  \param ExpectedHashes The list file's hashsums which are expected.
     */
     */
    pkgAcqDiffIndex(pkgAcquire *Owner,
    pkgAcqDiffIndex(pkgAcquire *Owner,
                    struct IndexTarget const * const Target,
                    struct IndexTarget const * const Target,
-                   HashString ExpectedHash,
+                   HashStringList const &ExpectedHashes,
                    indexRecords *MetaIndexParser);
                    indexRecords *MetaIndexParser);
 };
 };
 									/*}}}*/
 									/*}}}*/
@@ -483,11 +476,6 @@ class pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
     */
     */
    bool Debug;
    bool Debug;
 
 
-   /** \brief description of the item that is currently being
-    *  downloaded.
-    */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief URI of the package index file that is being
    /** \brief URI of the package index file that is being
     *  reconstructed.
     *  reconstructed.
     */
     */
@@ -525,10 +513,9 @@ class pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
     *  outright; its arguments are ignored.
     *  outright; its arguments are ignored.
     */
     */
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
-		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return RealURI + "Index";};
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
+	 pkgAcquire::MethodConfig *Cnf);
+   virtual std::string DescURI() const {return RealURI + "Index";};
 
 
    /** \brief Create an index merge-diff item.
    /** \brief Create an index merge-diff item.
     *
     *
@@ -541,7 +528,7 @@ class pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
     *
     *
     *  \param ShortDesc A brief description of this item.
     *  \param ShortDesc A brief description of this item.
     *
     *
-    *  \param ExpectedHash The expected md5sum of the completely
+    *  \param ExpectedHashes The expected md5sum of the completely
     *  reconstructed package index file; the index file will be tested
     *  reconstructed package index file; the index file will be tested
     *  against this value when it is entirely reconstructed.
     *  against this value when it is entirely reconstructed.
     *
     *
@@ -553,7 +540,7 @@ class pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
     */
     */
    pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
    pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
                          struct IndexTarget const * const Target,
                          struct IndexTarget const * const Target,
-                         HashString ExpectedHash,
+                         HashStringList const &ExpectedHash,
                          indexRecords *MetaIndexParser,
                          indexRecords *MetaIndexParser,
                          DiffInfo const &patch, 
                          DiffInfo const &patch, 
                          std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches);
                          std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches);
@@ -588,8 +575,8 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
    /** \brief Handle tasks that must be performed after the item
    /** \brief Handle tasks that must be performed after the item
     *  finishes downloading.
     *  finishes downloading.
     *
     *
-    *  Dequeues the item and checks the resulting file's md5sum
-    *  against ExpectedHash after the last patch was applied.
+    *  Dequeues the item and checks the resulting file's hashsums
+    *  against ExpectedHashes after the last patch was applied.
     *  There is no need to check the md5/sha1 after a "normal" 
     *  There is no need to check the md5/sha1 after a "normal" 
     *  patch because QueueNextDiff() will check the sha1 later.
     *  patch because QueueNextDiff() will check the sha1 later.
     *
     *
@@ -605,11 +592,6 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
     */
     */
    bool Debug;
    bool Debug;
 
 
-   /** \brief A description of the item that is currently being
-    *  downloaded.
-    */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The URI of the package index file that is being
    /** \brief The URI of the package index file that is being
     *  reconstructed.
     *  reconstructed.
     */
     */
@@ -656,9 +638,9 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
     */
     */
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
 
 
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return RealURI + "Index";};
+   virtual std::string DescURI() const {return RealURI + "Index";};
 
 
    /** \brief Create an index diff item.
    /** \brief Create an index diff item.
     *
     *
@@ -674,7 +656,7 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
     *
     *
     *  \param ShortDesc A brief description of this item.
     *  \param ShortDesc A brief description of this item.
     *
     *
-    *  \param ExpectedHash The expected md5sum of the completely
+    *  \param ExpectedHashes The expected md5sum of the completely
     *  reconstructed package index file; the index file will be tested
     *  reconstructed package index file; the index file will be tested
     *  against this value when it is entirely reconstructed.
     *  against this value when it is entirely reconstructed.
     *
     *
@@ -686,7 +668,7 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
     */
     */
    pkgAcqIndexDiffs(pkgAcquire *Owner,
    pkgAcqIndexDiffs(pkgAcquire *Owner,
                     struct IndexTarget const * const Target,
                     struct IndexTarget const * const Target,
-                    HashString ExpectedHash,
+                    HashStringList const &ExpectedHash,
                     indexRecords *MetaIndexParser,
                     indexRecords *MetaIndexParser,
 		    std::string ServerSha1,
 		    std::string ServerSha1,
 		    std::vector<DiffInfo> diffs=std::vector<DiffInfo>());
 		    std::vector<DiffInfo> diffs=std::vector<DiffInfo>());
@@ -721,11 +703,6 @@ class pkgAcqIndex : public pkgAcqBaseIndex
    //        the downloaded file contains the expected tag
    //        the downloaded file contains the expected tag
    bool Verify;
    bool Verify;
 
 
-   /** \brief The download request that is currently being
-    *   processed.
-    */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The object that is actually being fetched (minus any
    /** \brief The object that is actually being fetched (minus any
     *  compression-related extensions).
     *  compression-related extensions).
     */
     */
@@ -740,11 +717,10 @@ class pkgAcqIndex : public pkgAcqBaseIndex
    
    
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers();
-   virtual std::string DescURI() {return Desc.URI;};
-   virtual std::string HashSum() {return ExpectedHash.toStr(); };
+   virtual std::string Custom600Headers() const;
+   virtual std::string DescURI() const {return Desc.URI;};
 
 
    /** \brief Create a pkgAcqIndex.
    /** \brief Create a pkgAcqIndex.
     *
     *
@@ -757,7 +733,7 @@ class pkgAcqIndex : public pkgAcqBaseIndex
     *
     *
     *  \param ShortDesc A brief description of this index file.
     *  \param ShortDesc A brief description of this index file.
     *
     *
-    *  \param ExpectedHash The expected hashsum of this index file.
+    *  \param ExpectedHashes The expected hashsum of this index file.
     *
     *
     *  \param compressExt The compression-related extension with which
     *  \param compressExt The compression-related extension with which
     *  this index file should be downloaded, or "" to autodetect
     *  this index file should be downloaded, or "" to autodetect
@@ -766,11 +742,11 @@ class pkgAcqIndex : public pkgAcqBaseIndex
     *  fallback is ".gz" or none.
     *  fallback is ".gz" or none.
     */
     */
    pkgAcqIndex(pkgAcquire *Owner,std::string URI,std::string URIDesc,
    pkgAcqIndex(pkgAcquire *Owner,std::string URI,std::string URIDesc,
-	       std::string ShortDesc, HashString ExpectedHash, 
+	       std::string ShortDesc, HashStringList const &ExpectedHashes,
 	       std::string compressExt="");
 	       std::string compressExt="");
    pkgAcqIndex(pkgAcquire *Owner,
    pkgAcqIndex(pkgAcquire *Owner,
                struct IndexTarget const * const Target,
                struct IndexTarget const * const Target,
-               HashString const &ExpectedHash,
+               HashStringList const &ExpectedHash,
                indexRecords *MetaIndexParser);
                indexRecords *MetaIndexParser);
    void Init(std::string const &URI, std::string const &URIDesc,
    void Init(std::string const &URI, std::string const &URIDesc,
              std::string const &ShortDesc);
              std::string const &ShortDesc);
@@ -788,7 +764,7 @@ class pkgAcqIndexTrans : public pkgAcqIndex
    public:
    public:
   
   
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers();
+   virtual std::string Custom600Headers() const;
 
 
    /** \brief Create a pkgAcqIndexTrans.
    /** \brief Create a pkgAcqIndexTrans.
     *
     *
@@ -803,8 +779,8 @@ class pkgAcqIndexTrans : public pkgAcqIndex
     */
     */
    pkgAcqIndexTrans(pkgAcquire *Owner,std::string URI,std::string URIDesc,
    pkgAcqIndexTrans(pkgAcquire *Owner,std::string URI,std::string URIDesc,
 		    std::string ShortDesc);
 		    std::string ShortDesc);
-   pkgAcqIndexTrans(pkgAcquire *Owner, struct IndexTarget const * const Target,
-		    HashString const &ExpectedHash, indexRecords *MetaIndexParser);
+   pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const * const Target,
+		    HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser);
 };
 };
 									/*}}}*/
 									/*}}}*/
 /** \brief Information about an index file. */				/*{{{*/
 /** \brief Information about an index file. */				/*{{{*/
@@ -872,9 +848,6 @@ class pkgAcqMetaSig : public pkgAcquire::Item
    /** \brief The last good signature file */
    /** \brief The last good signature file */
    std::string LastGoodSig;
    std::string LastGoodSig;
 
 
-   /** \brief The fetch request that is currently being processed. */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The URI of the signature file.  Unlike Desc.URI, this is
    /** \brief The URI of the signature file.  Unlike Desc.URI, this is
     *  never modified; it is used to determine the file that is being
     *  never modified; it is used to determine the file that is being
     *  downloaded.
     *  downloaded.
@@ -902,21 +875,21 @@ class pkgAcqMetaSig : public pkgAcquire::Item
     *
     *
     *  \todo Why a list of pointers instead of a list of structs?
     *  \todo Why a list of pointers instead of a list of structs?
     */
     */
-   const std::vector<struct IndexTarget*>* IndexTargets;
+   const std::vector<IndexTarget*>* IndexTargets;
 
 
    public:
    public:
    
    
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string Md5Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers();
-   virtual std::string DescURI() {return RealURI; };
+   virtual std::string Custom600Headers() const;
+   virtual std::string DescURI() const {return RealURI; };
 
 
    /** \brief Create a new pkgAcqMetaSig. */
    /** \brief Create a new pkgAcqMetaSig. */
    pkgAcqMetaSig(pkgAcquire *Owner,std::string URI,std::string URIDesc, std::string ShortDesc,
    pkgAcqMetaSig(pkgAcquire *Owner,std::string URI,std::string URIDesc, std::string ShortDesc,
 		 std::string MetaIndexURI, std::string MetaIndexURIDesc, std::string MetaIndexShortDesc,
 		 std::string MetaIndexURI, std::string MetaIndexURIDesc, std::string MetaIndexShortDesc,
-		 const std::vector<struct IndexTarget*>* IndexTargets,
+		 const std::vector<IndexTarget*>* IndexTargets,
 		 indexRecords* MetaIndexParser);
 		 indexRecords* MetaIndexParser);
    virtual ~pkgAcqMetaSig();
    virtual ~pkgAcqMetaSig();
 };
 };
@@ -934,9 +907,6 @@ class pkgAcqMetaSig : public pkgAcquire::Item
 class pkgAcqMetaIndex : public pkgAcquire::Item
 class pkgAcqMetaIndex : public pkgAcquire::Item
 {
 {
    protected:
    protected:
-   /** \brief The fetch command that is currently being processed. */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The URI that is actually being downloaded; never
    /** \brief The URI that is actually being downloaded; never
     *  modified by pkgAcqMetaIndex.
     *  modified by pkgAcqMetaIndex.
     */
     */
@@ -950,7 +920,7 @@ class pkgAcqMetaIndex : public pkgAcquire::Item
    std::string SigFile;
    std::string SigFile;
 
 
    /** \brief The index files to download. */
    /** \brief The index files to download. */
-   const std::vector<struct IndexTarget*>* IndexTargets;
+   const std::vector<IndexTarget*>* IndexTargets;
 
 
    /** \brief The parser for the meta-index file. */
    /** \brief The parser for the meta-index file. */
    indexRecords* MetaIndexParser;
    indexRecords* MetaIndexParser;
@@ -1004,16 +974,16 @@ class pkgAcqMetaIndex : public pkgAcquire::Item
    
    
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size, std::string Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers();
-   virtual std::string DescURI() {return RealURI; };
+   virtual std::string Custom600Headers() const;
+   virtual std::string DescURI() const {return RealURI; };
 
 
    /** \brief Create a new pkgAcqMetaIndex. */
    /** \brief Create a new pkgAcqMetaIndex. */
    pkgAcqMetaIndex(pkgAcquire *Owner,
    pkgAcqMetaIndex(pkgAcquire *Owner,
 		   std::string URI,std::string URIDesc, std::string ShortDesc,
 		   std::string URI,std::string URIDesc, std::string ShortDesc,
 		   std::string SigFile,
 		   std::string SigFile,
-		   const std::vector<struct IndexTarget*>* IndexTargets,
+		   const std::vector<IndexTarget*>* IndexTargets,
 		   indexRecords* MetaIndexParser);
 		   indexRecords* MetaIndexParser);
 };
 };
 									/*}}}*/
 									/*}}}*/
@@ -1040,14 +1010,14 @@ class pkgAcqMetaClearSig : public pkgAcqMetaIndex
 
 
 public:
 public:
    void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers();
+   virtual std::string Custom600Headers() const;
 
 
    /** \brief Create a new pkgAcqMetaClearSig. */
    /** \brief Create a new pkgAcqMetaClearSig. */
    pkgAcqMetaClearSig(pkgAcquire *Owner,
    pkgAcqMetaClearSig(pkgAcquire *Owner,
 		std::string const &URI, std::string const &URIDesc, std::string const &ShortDesc,
 		std::string const &URI, std::string const &URIDesc, std::string const &ShortDesc,
 		std::string const &MetaIndexURI, std::string const &MetaIndexURIDesc, std::string const &MetaIndexShortDesc,
 		std::string const &MetaIndexURI, std::string const &MetaIndexURIDesc, std::string const &MetaIndexShortDesc,
 		std::string const &MetaSigURI, std::string const &MetaSigURIDesc, std::string const &MetaSigShortDesc,
 		std::string const &MetaSigURI, std::string const &MetaSigURIDesc, std::string const &MetaSigShortDesc,
-		const std::vector<struct IndexTarget*>* IndexTargets,
+		const std::vector<IndexTarget*>* IndexTargets,
 		indexRecords* MetaIndexParser);
 		indexRecords* MetaIndexParser);
    virtual ~pkgAcqMetaClearSig();
    virtual ~pkgAcqMetaClearSig();
 };
 };
@@ -1063,9 +1033,6 @@ class pkgAcqArchive : public pkgAcquire::Item
    /** \brief The package version being fetched. */
    /** \brief The package version being fetched. */
    pkgCache::VerIterator Version;
    pkgCache::VerIterator Version;
 
 
-   /** \brief The fetch command that is currently being processed. */
-   pkgAcquire::ItemDesc Desc;
-
    /** \brief The list of sources from which to pick archives to
    /** \brief The list of sources from which to pick archives to
     *  download this package from.
     *  download this package from.
     */
     */
@@ -1076,9 +1043,6 @@ class pkgAcqArchive : public pkgAcquire::Item
     */
     */
    pkgRecords *Recs;
    pkgRecords *Recs;
 
 
-   /** \brief The hashsum of this package. */
-   HashString ExpectedHash;
-
    /** \brief A location in which the actual filename of the package
    /** \brief A location in which the actual filename of the package
     *  should be stored.
     *  should be stored.
     */
     */
@@ -1105,13 +1069,12 @@ class pkgAcqArchive : public pkgAcquire::Item
    public:
    public:
    
    
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string Hash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return Desc.URI;};
-   virtual std::string ShortDesc() {return Desc.ShortDesc;};
+   virtual std::string DescURI() const {return Desc.URI;};
+   virtual std::string ShortDesc() const {return Desc.ShortDesc;};
    virtual void Finished();
    virtual void Finished();
-   virtual std::string HashSum() {return ExpectedHash.toStr(); };
-   virtual bool IsTrusted();
+   virtual bool IsTrusted() const;
    
    
    /** \brief Create a new pkgAcqArchive.
    /** \brief Create a new pkgAcqArchive.
     *
     *
@@ -1144,12 +1107,6 @@ class pkgAcqArchive : public pkgAcquire::Item
  */
  */
 class pkgAcqFile : public pkgAcquire::Item
 class pkgAcqFile : public pkgAcquire::Item
 {
 {
-   /** \brief The currently active download process. */
-   pkgAcquire::ItemDesc Desc;
-
-   /** \brief The hashsum of the file to download, if it is known. */
-   HashString ExpectedHash;
-
    /** \brief How many times to retry the download, set from
    /** \brief How many times to retry the download, set from
     *  Acquire::Retries.
     *  Acquire::Retries.
     */
     */
@@ -1162,11 +1119,10 @@ class pkgAcqFile : public pkgAcquire::Item
    
    
    // Specialized action members
    // Specialized action members
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
    virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size,std::string CalcHash,
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &CalcHashes,
 		     pkgAcquire::MethodConfig *Cnf);
 		     pkgAcquire::MethodConfig *Cnf);
-   virtual std::string DescURI() {return Desc.URI;};
-   virtual std::string HashSum() {return ExpectedHash.toStr(); };
-   virtual std::string Custom600Headers();
+   virtual std::string DescURI() const {return Desc.URI;};
+   virtual std::string Custom600Headers() const;
 
 
    /** \brief Create a new pkgAcqFile object.
    /** \brief Create a new pkgAcqFile object.
     *
     *
@@ -1175,8 +1131,8 @@ class pkgAcqFile : public pkgAcquire::Item
     *
     *
     *  \param URI The URI to download.
     *  \param URI The URI to download.
     *
     *
-    *  \param Hash The hashsum of the file to download, if it is known;
-    *  otherwise "".
+    *  \param Hashes The hashsums of the file to download, if they are known;
+    *  otherwise empty list.
     *
     *
     *  \param Size The size of the file to download, if it is known;
     *  \param Size The size of the file to download, if it is known;
     *  otherwise 0.
     *  otherwise 0.
@@ -1199,7 +1155,7 @@ class pkgAcqFile : public pkgAcquire::Item
     * is the absolute name to which the file should be downloaded.
     * is the absolute name to which the file should be downloaded.
     */
     */
 
 
-   pkgAcqFile(pkgAcquire *Owner, std::string URI, std::string Hash, unsigned long long Size,
+   pkgAcqFile(pkgAcquire *Owner, std::string URI, HashStringList const &Hashes, unsigned long long Size,
 	      std::string Desc, std::string ShortDesc,
 	      std::string Desc, std::string ShortDesc,
 	      const std::string &DestDir="", const std::string &DestFilename="",
 	      const std::string &DestDir="", const std::string &DestFilename="",
 	      bool IsIndexFile=false);
 	      bool IsIndexFile=false);

+ 25 - 23
apt-pkg/acquire-method.cc

@@ -147,6 +147,16 @@ void pkgAcqMethod::URIStart(FetchResult &Res)
 // AcqMethod::URIDone - A URI is finished				/*{{{*/
 // AcqMethod::URIDone - A URI is finished				/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
+static void printHashStringList(HashStringList const * const list)
+{
+      for (HashStringList::const_iterator hash = list->begin(); hash != list->end(); ++hash)
+      {
+	 // very old compatibility name for MD5Sum
+	 if (hash->HashType() == "MD5Sum")
+	    std::cout << "MD5-Hash: " << hash->HashValue() << "\n";
+	 std::cout << hash->HashType() << "-Hash: " << hash->HashValue() << "\n";
+      }
+}
 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
 {
 {
    if (Queue == 0)
    if (Queue == 0)
@@ -164,15 +174,8 @@ void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
    if (Res.LastModified != 0)
    if (Res.LastModified != 0)
       std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
       std::cout << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
 
 
-   if (Res.MD5Sum.empty() == false)
-      std::cout << "MD5-Hash: " << Res.MD5Sum << "\n"
-		<< "MD5Sum-Hash: " << Res.MD5Sum << "\n";
-   if (Res.SHA1Sum.empty() == false)
-      std::cout << "SHA1-Hash: " << Res.SHA1Sum << "\n";
-   if (Res.SHA256Sum.empty() == false)
-      std::cout << "SHA256-Hash: " << Res.SHA256Sum << "\n";
-   if (Res.SHA512Sum.empty() == false)
-      std::cout << "SHA512-Hash: " << Res.SHA512Sum << "\n";
+   printHashStringList(&Res.Hashes);
+
    if (UsedMirror.empty() == false)
    if (UsedMirror.empty() == false)
       std::cout << "UsedMirror: " << UsedMirror << "\n";
       std::cout << "UsedMirror: " << UsedMirror << "\n";
    if (Res.GPGVOutput.empty() == false)
    if (Res.GPGVOutput.empty() == false)
@@ -200,15 +203,8 @@ void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
       if (Alt->LastModified != 0)
       if (Alt->LastModified != 0)
 	 std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
 	 std::cout << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
 
 
-      if (Alt->MD5Sum.empty() == false)
-	 std::cout << "Alt-MD5-Hash: " << Alt->MD5Sum << "\n";
-      if (Alt->SHA1Sum.empty() == false)
-	 std::cout << "Alt-SHA1-Hash: " << Alt->SHA1Sum << "\n";
-      if (Alt->SHA256Sum.empty() == false)
-	 std::cout << "Alt-SHA256-Hash: " << Alt->SHA256Sum << "\n";
-      if (Alt->SHA512Sum.empty() == false)
-         std::cout << "Alt-SHA512-Hash: " << Alt->SHA512Sum << "\n";
-     
+      printHashStringList(&Alt->Hashes);
+
       if (Alt->IMSHit == true)
       if (Alt->IMSHit == true)
 	 std::cout << "Alt-IMS-Hit: true\n";
 	 std::cout << "Alt-IMS-Hit: true\n";
    }
    }
@@ -355,6 +351,15 @@ int pkgAcqMethod::Run(bool Single)
 	       Tmp->LastModified = 0;
 	       Tmp->LastModified = 0;
 	    Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
 	    Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
 	    Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
 	    Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
+	    Tmp->ExpectedHashes = HashStringList();
+	    for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+	    {
+	       std::string tag = "Expected-";
+	       tag.append(*t);
+	       std::string const hash = LookupTag(Message, tag.c_str());
+	       if (hash.empty() == false)
+		  Tmp->ExpectedHashes.push_back(HashString(*t, hash));
+	    }
 	    Tmp->Next = 0;
 	    Tmp->Next = 0;
 	    
 	    
 	    // Append it to the list
 	    // Append it to the list
@@ -442,12 +447,9 @@ pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* This hides the number of hashes we are supporting from the caller. 
 /* This hides the number of hashes we are supporting from the caller. 
    It just deals with the hash class. */
    It just deals with the hash class. */
-void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
+void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
 {
 {
-   MD5Sum = Hash.MD5.Result();
-   SHA1Sum = Hash.SHA1.Result();
-   SHA256Sum = Hash.SHA256.Result();
-   SHA512Sum = Hash.SHA512.Result();
+   Hashes = Hash.GetHashStringList();
 }
 }
 									/*}}}*/
 									/*}}}*/
 void pkgAcqMethod::Dequeue() {						/*{{{*/
 void pkgAcqMethod::Dequeue() {						/*{{{*/

+ 4 - 6
apt-pkg/acquire-method.h

@@ -20,6 +20,7 @@
 #ifndef PKGLIB_ACQUIRE_METHOD_H
 #ifndef PKGLIB_ACQUIRE_METHOD_H
 #define PKGLIB_ACQUIRE_METHOD_H
 #define PKGLIB_ACQUIRE_METHOD_H
 
 
+#include <apt-pkg/hashes.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/macros.h>
 
 
 #include <stdarg.h>
 #include <stdarg.h>
@@ -33,7 +34,6 @@
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/strutl.h>
 #endif
 #endif
 
 
-class Hashes;
 class pkgAcqMethod
 class pkgAcqMethod
 {
 {
    protected:
    protected:
@@ -47,14 +47,12 @@ class pkgAcqMethod
       time_t LastModified;
       time_t LastModified;
       bool IndexFile;
       bool IndexFile;
       bool FailIgnore;
       bool FailIgnore;
+      HashStringList ExpectedHashes;
    };
    };
    
    
    struct FetchResult
    struct FetchResult
    {
    {
-      std::string MD5Sum;
-      std::string SHA1Sum;
-      std::string SHA256Sum;
-      std::string SHA512Sum;
+      HashStringList Hashes;
       std::vector<std::string> GPGVOutput;
       std::vector<std::string> GPGVOutput;
       time_t LastModified;
       time_t LastModified;
       bool IMSHit;
       bool IMSHit;
@@ -62,7 +60,7 @@ class pkgAcqMethod
       unsigned long long Size;
       unsigned long long Size;
       unsigned long long ResumePoint;
       unsigned long long ResumePoint;
       
       
-      void TakeHashes(Hashes &Hash);
+      void TakeHashes(class Hashes &Hash);
       FetchResult();
       FetchResult();
    };
    };
 
 

+ 24 - 16
apt-pkg/acquire-worker.cc

@@ -326,25 +326,30 @@ bool pkgAcquire::Worker::RunMessages()
 			       Owner->DestFile.c_str(), LookupTag(Message,"Size","0").c_str(),TotalSize);
 			       Owner->DestFile.c_str(), LookupTag(Message,"Size","0").c_str(),TotalSize);
 
 
 	    // see if there is a hash to verify
 	    // see if there is a hash to verify
-	    string RecivedHash;
-	    HashString expectedHash(Owner->HashSum());
-	    if(!expectedHash.empty()) 
+	    HashStringList RecivedHashes;
+	    HashStringList expectedHashes = Owner->HashSums();
+	    for (HashStringList::const_iterator hs = expectedHashes.begin(); hs != expectedHashes.end(); ++hs)
 	    {
 	    {
-	       string hashTag = expectedHash.HashType()+"-Hash";
-	       string hashSum = LookupTag(Message, hashTag.c_str());
-	       if(!hashSum.empty())
-		  RecivedHash = expectedHash.HashType() + ":" + hashSum;
-	       if(_config->FindB("Debug::pkgAcquire::Auth", false) == true)
-	       {
-		  clog << "201 URI Done: " << Owner->DescURI() << endl
-		       << "RecivedHash: " << RecivedHash << endl
-		       << "ExpectedHash: " << expectedHash.toStr() 
-		       << endl << endl;
-	       }
+	       std::string const tagname = hs->HashType() + "-Hash";
+	       std::string const hashsum = LookupTag(Message, tagname.c_str());
+	       if (hashsum.empty() == false)
+		  RecivedHashes.push_back(HashString(hs->HashType(), hashsum));
+	    }
+
+	    if(_config->FindB("Debug::pkgAcquire::Auth", false) == true)
+	    {
+	       std::clog << "201 URI Done: " << Owner->DescURI() << endl
+		  << "RecivedHash:" << endl;
+	       for (HashStringList::const_iterator hs = RecivedHashes.begin(); hs != RecivedHashes.end(); ++hs)
+		  std::clog <<  "\t- " << hs->toStr() << std::endl;
+	       std::clog << "ExpectedHash:" << endl;
+	       for (HashStringList::const_iterator hs = expectedHashes.begin(); hs != expectedHashes.end(); ++hs)
+		  std::clog <<  "\t- " << hs->toStr() << std::endl;
+	       std::clog << endl;
 	    }
 	    }
-	    Owner->Done(Message, ServerSize, RecivedHash.c_str(), Config);
+	    Owner->Done(Message, ServerSize, RecivedHashes, Config);
 	    ItemDone();
 	    ItemDone();
-	    
+
 	    // Log that we are done
 	    // Log that we are done
 	    if (Log != 0)
 	    if (Log != 0)
 	    {
 	    {
@@ -525,6 +530,9 @@ bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item)
    Message.reserve(300);
    Message.reserve(300);
    Message += "URI: " + Item->URI;
    Message += "URI: " + Item->URI;
    Message += "\nFilename: " + Item->Owner->DestFile;
    Message += "\nFilename: " + Item->Owner->DestFile;
+   HashStringList const hsl = Item->Owner->HashSums();
+   for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs)
+      Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue();
    Message += Item->Owner->Custom600Headers();
    Message += Item->Owner->Custom600Headers();
    Message += "\n\n";
    Message += "\n\n";
    
    

+ 1 - 1
apt-pkg/aptconfiguration.cc

@@ -540,7 +540,7 @@ std::string const Configuration::getBuildProfilesString() {
 		return "";
 		return "";
 	std::vector<std::string>::const_iterator p = profiles.begin();
 	std::vector<std::string>::const_iterator p = profiles.begin();
 	std::string list = *p;
 	std::string list = *p;
-	for (; p != profiles.end(); ++p)
+	for (++p; p != profiles.end(); ++p)
 	   list.append(",").append(*p);
 	   list.append(",").append(*p);
 	return list;
 	return list;
 }
 }

+ 176 - 37
apt-pkg/contrib/hashes.cc

@@ -27,7 +27,7 @@
 #include <iostream>
 #include <iostream>
 									/*}}}*/
 									/*}}}*/
 
 
-const char* HashString::_SupportedHashes[] = 
+const char * HashString::_SupportedHashes[] =
 {
 {
    "SHA512", "SHA256", "SHA1", "MD5Sum", NULL
    "SHA512", "SHA256", "SHA1", "MD5Sum", NULL
 };
 };
@@ -42,11 +42,16 @@ HashString::HashString(std::string Type, std::string Hash) : Type(Type), Hash(Ha
 
 
 HashString::HashString(std::string StringedHash)			/*{{{*/
 HashString::HashString(std::string StringedHash)			/*{{{*/
 {
 {
-   // legacy: md5sum without "MD5Sum:" prefix
-   if (StringedHash.find(":") == std::string::npos && StringedHash.size() == 32)
+   if (StringedHash.find(":") == std::string::npos)
    {
    {
-      Type = "MD5Sum";
-      Hash = StringedHash;
+      // legacy: md5sum without "MD5Sum:" prefix
+      if (StringedHash.size() == 32)
+      {
+	 Type = "MD5Sum";
+	 Hash = StringedHash;
+      }
+      if(_config->FindB("Debug::Hashes",false) == true)
+	 std::clog << "HashString(string): invalid StringedHash " << StringedHash << std::endl;
       return;
       return;
    }
    }
    std::string::size_type pos = StringedHash.find(":");
    std::string::size_type pos = StringedHash.find(":");
@@ -82,25 +87,25 @@ std::string HashString::GetHashForFile(std::string filename) const      /*{{{*/
    std::string fileHash;
    std::string fileHash;
 
 
    FileFd Fd(filename, FileFd::ReadOnly);
    FileFd Fd(filename, FileFd::ReadOnly);
-   if(Type == "MD5Sum")
+   if(strcasecmp(Type.c_str(), "MD5Sum") == 0)
    {
    {
       MD5Summation MD5;
       MD5Summation MD5;
       MD5.AddFD(Fd);
       MD5.AddFD(Fd);
       fileHash = (std::string)MD5.Result();
       fileHash = (std::string)MD5.Result();
    }
    }
-   else if (Type == "SHA1")
+   else if (strcasecmp(Type.c_str(), "SHA1") == 0)
    {
    {
       SHA1Summation SHA1;
       SHA1Summation SHA1;
       SHA1.AddFD(Fd);
       SHA1.AddFD(Fd);
       fileHash = (std::string)SHA1.Result();
       fileHash = (std::string)SHA1.Result();
    }
    }
-   else if (Type == "SHA256")
+   else if (strcasecmp(Type.c_str(), "SHA256") == 0)
    {
    {
       SHA256Summation SHA256;
       SHA256Summation SHA256;
       SHA256.AddFD(Fd);
       SHA256.AddFD(Fd);
       fileHash = (std::string)SHA256.Result();
       fileHash = (std::string)SHA256.Result();
    }
    }
-   else if (Type == "SHA512")
+   else if (strcasecmp(Type.c_str(), "SHA512") == 0)
    {
    {
       SHA512Summation SHA512;
       SHA512Summation SHA512;
       SHA512.AddFD(Fd);
       SHA512.AddFD(Fd);
@@ -111,26 +116,147 @@ std::string HashString::GetHashForFile(std::string filename) const      /*{{{*/
    return fileHash;
    return fileHash;
 }
 }
 									/*}}}*/
 									/*}}}*/
-const char** HashString::SupportedHashes()
+const char** HashString::SupportedHashes()				/*{{{*/
 {
 {
    return _SupportedHashes;
    return _SupportedHashes;
 }
 }
-
-APT_PURE bool HashString::empty() const
+									/*}}}*/
+APT_PURE bool HashString::empty() const					/*{{{*/
 {
 {
    return (Type.empty() || Hash.empty());
    return (Type.empty() || Hash.empty());
 }
 }
+									/*}}}*/
+std::string HashString::toStr() const					/*{{{*/
+{
+   return Type + ":" + Hash;
+}
+									/*}}}*/
+APT_PURE bool HashString::operator==(HashString const &other) const	/*{{{*/
+{
+   return (strcasecmp(Type.c_str(), other.Type.c_str()) == 0 && Hash == other.Hash);
+}
+APT_PURE bool HashString::operator!=(HashString const &other) const
+{
+   return !(*this == other);
+}
+									/*}}}*/
+
+bool HashStringList::usable() const					/*{{{*/
+{
+   if (empty() == true)
+      return false;
+   std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+   if (forcedType.empty() == true)
+      return true;
+   return find(forcedType) != NULL;
+}
+									/*}}}*/
+HashString const * HashStringList::find(char const * const type) const /*{{{*/
+{
+   if (type == NULL || type[0] == '\0')
+   {
+      std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+      if (forcedType.empty() == false)
+	 return find(forcedType.c_str());
+      for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+	 for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
+	    if (strcasecmp(hs->HashType().c_str(), *t) == 0)
+	       return &*hs;
+      return NULL;
+   }
+   for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
+      if (strcasecmp(hs->HashType().c_str(), type) == 0)
+	 return &*hs;
+   return NULL;
+}
+									/*}}}*/
+bool HashStringList::supported(char const * const type)			/*{{{*/
+{
+   for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+      if (strcasecmp(*t, type) == 0)
+	 return true;
+   return false;
+}
+									/*}}}*/
+bool HashStringList::push_back(const HashString &hashString)		/*{{{*/
+{
+   if (hashString.HashType().empty() == true ||
+	 hashString.HashValue().empty() == true ||
+	 supported(hashString.HashType().c_str()) == false)
+      return false;
+
+   // ensure that each type is added only once
+   HashString const * const hs = find(hashString.HashType().c_str());
+   if (hs != NULL)
+      return *hs == hashString;
 
 
-std::string HashString::toStr() const
+   list.push_back(hashString);
+   return true;
+}
+									/*}}}*/
+bool HashStringList::VerifyFile(std::string filename) const		/*{{{*/
 {
 {
-   return Type + std::string(":") + Hash;
+   if (list.empty() == true)
+      return false;
+   HashString const * const hs = find(NULL);
+   if (hs == NULL || hs->VerifyFile(filename) == false)
+      return false;
+   return true;
 }
 }
+									/*}}}*/
+bool HashStringList::operator==(HashStringList const &other) const	/*{{{*/
+{
+   std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+   if (forcedType.empty() == false)
+   {
+      HashString const * const hs = other.find(forcedType);
+      HashString const * const ohs = other.find(forcedType);
+      if (hs == NULL || ohs == NULL)
+	 return false;
+      return hs == ohs;
+   }
+   short matches = 0;
+   for (const_iterator hs = begin(); hs != end(); ++hs)
+   {
+      HashString const * const ohs = other.find(hs->HashType());
+      if (ohs == NULL)
+	 continue;
+      if (*hs != *ohs)
+	 return false;
+      ++matches;
+   }
+   if (matches == 0)
+      return false;
+   return true;
+}
+bool HashStringList::operator!=(HashStringList const &other) const
+{
+   return !(*this == other);
+}
+									/*}}}*/
 
 
-// Hashes::AddFD - Add the contents of the FD				/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool Hashes::AddFD(int const Fd,unsigned long long Size, bool const addMD5,
-		   bool const addSHA1, bool const addSHA256, bool const addSHA512)
+// Hashes::Add* - Add the contents of data or FD			/*{{{*/
+bool Hashes::Add(const unsigned char * const Data,unsigned long long const Size, unsigned int const Hashes)
+{
+   bool Res = true;
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+   if ((Hashes & MD5SUM) == MD5SUM)
+      Res &= MD5.Add(Data, Size);
+   if ((Hashes & SHA1SUM) == SHA1SUM)
+      Res &= SHA1.Add(Data, Size);
+   if ((Hashes & SHA256SUM) == SHA256SUM)
+      Res &= SHA256.Add(Data, Size);
+   if ((Hashes & SHA512SUM) == SHA512SUM)
+      Res &= SHA512.Add(Data, Size);
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+   return Res;
+}
+bool Hashes::AddFD(int const Fd,unsigned long long Size, unsigned int const Hashes)
 {
 {
    unsigned char Buf[64*64];
    unsigned char Buf[64*64];
    bool const ToEOF = (Size == UntilEOF);
    bool const ToEOF = (Size == UntilEOF);
@@ -144,19 +270,12 @@ bool Hashes::AddFD(int const Fd,unsigned long long Size, bool const addMD5,
       if (ToEOF && Res == 0) // EOF
       if (ToEOF && Res == 0) // EOF
 	 break;
 	 break;
       Size -= Res;
       Size -= Res;
-      if (addMD5 == true)
-	 MD5.Add(Buf,Res);
-      if (addSHA1 == true)
-	 SHA1.Add(Buf,Res);
-      if (addSHA256 == true)
-	 SHA256.Add(Buf,Res);
-      if (addSHA512 == true)
-	 SHA512.Add(Buf,Res);
+      if (Add(Buf, Res, Hashes) == false)
+	 return false;
    }
    }
    return true;
    return true;
 }
 }
-bool Hashes::AddFD(FileFd &Fd,unsigned long long Size, bool const addMD5,
-		   bool const addSHA1, bool const addSHA256, bool const addSHA512)
+bool Hashes::AddFD(FileFd &Fd,unsigned long long Size, unsigned int const Hashes)
 {
 {
    unsigned char Buf[64*64];
    unsigned char Buf[64*64];
    bool const ToEOF = (Size == 0);
    bool const ToEOF = (Size == 0);
@@ -175,15 +294,35 @@ bool Hashes::AddFD(FileFd &Fd,unsigned long long Size, bool const addMD5,
       else if (a == 0) // EOF
       else if (a == 0) // EOF
 	 break;
 	 break;
       Size -= a;
       Size -= a;
-      if (addMD5 == true)
-	 MD5.Add(Buf, a);
-      if (addSHA1 == true)
-	 SHA1.Add(Buf, a);
-      if (addSHA256 == true)
-	 SHA256.Add(Buf, a);
-      if (addSHA512 == true)
-	 SHA512.Add(Buf, a);
+      if (Add(Buf, a, Hashes) == false)
+	 return false;
    }
    }
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
+HashStringList Hashes::GetHashStringList()
+{
+   HashStringList hashes;
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+   hashes.push_back(HashString("MD5Sum", MD5.Result().Value()));
+   hashes.push_back(HashString("SHA1", SHA1.Result().Value()));
+   hashes.push_back(HashString("SHA256", SHA256.Result().Value()));
+   hashes.push_back(HashString("SHA512", SHA512.Result().Value()));
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+   return hashes;
+}
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+	#pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
+#endif
+Hashes::Hashes() {}
+Hashes::~Hashes() {}
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif

+ 145 - 20
apt-pkg/contrib/hashes.h

@@ -17,6 +17,7 @@
 #include <apt-pkg/md5.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/sha1.h>
 #include <apt-pkg/sha1.h>
 #include <apt-pkg/sha2.h>
 #include <apt-pkg/sha2.h>
+#include <apt-pkg/macros.h>
 
 
 #include <cstring>
 #include <cstring>
 #include <string>
 #include <string>
@@ -41,7 +42,7 @@ class HashString
  protected:
  protected:
    std::string Type;
    std::string Type;
    std::string Hash;
    std::string Hash;
-   static const char* _SupportedHashes[10];
+   static const char * _SupportedHashes[10];
 
 
    // internal helper
    // internal helper
    std::string GetHashForFile(std::string filename) const;
    std::string GetHashForFile(std::string filename) const;
@@ -52,7 +53,8 @@ class HashString
    HashString();
    HashString();
 
 
    // get hash type used
    // get hash type used
-   std::string HashType() { return Type; };
+   std::string HashType() const { return Type; };
+   std::string HashValue() const { return Hash; };
 
 
    // verify the given filename against the currently loaded hash
    // verify the given filename against the currently loaded hash
    bool VerifyFile(std::string filename) const;
    bool VerifyFile(std::string filename) const;
@@ -64,37 +66,160 @@ class HashString
    // helper
    // helper
    std::string toStr() const;                    // convert to str as "type:hash"
    std::string toStr() const;                    // convert to str as "type:hash"
    bool empty() const;
    bool empty() const;
+   bool operator==(HashString const &other) const;
+   bool operator!=(HashString const &other) const;
 
 
    // return the list of hashes we support
    // return the list of hashes we support
    static APT_CONST const char** SupportedHashes();
    static APT_CONST const char** SupportedHashes();
 };
 };
 
 
+class HashStringList
+{
+   public:
+   /** find best hash if no specific one is requested
+    *
+    * @param type of the checksum to return, can be \b NULL
+    * @return If type is \b NULL (or the empty string) it will
+    *  return the 'best' hash; otherwise the hash which was
+    *  specifically requested. If no hash is found \b NULL will be returned.
+    */
+   HashString const * find(char const * const type) const;
+   HashString const * find(std::string const &type) const { return find(type.c_str()); }
+   /** check if the given hash type is supported
+    *
+    * @param type to check
+    * @return true if supported, otherwise false
+    */
+   static APT_PURE bool supported(char const * const type);
+   /** add the given #HashString to the list
+    *
+    * @param hashString to add
+    * @return true if the hash is added because it is supported and
+    *  not already a different hash of the same type included, otherwise false
+    */
+   bool push_back(const HashString &hashString);
+   /** @return size of the list of HashStrings */
+   size_t size() const { return list.size(); }
+
+   /** take the 'best' hash and verify file with it
+    *
+    * @param filename to verify
+    * @return true if the file matches the hashsum, otherwise false
+    */
+   bool VerifyFile(std::string filename) const;
+
+   /** is the list empty ?
+    *
+    * @return \b true if the list is empty, otherwise \b false
+    */
+   bool empty() const { return list.empty(); }
+
+   /** has the list at least one good entry
+    *
+    * similar to #empty, but handles forced hashes.
+    *
+    * @return if no hash is forced, same result as #empty,
+    * if one is forced \b true if this has is available, \b false otherwise
+    */
+   bool usable() const;
+
+   typedef std::vector<HashString>::const_iterator const_iterator;
+
+   /** iterator to the first element */
+   const_iterator begin() const { return list.begin(); }
+
+   /** iterator to the end element */
+   const_iterator end() const { return list.end(); }
+
+   /** start fresh with a clear list */
+   void clear() { list.clear(); }
+
+   /** compare two HashStringList for similarity.
+    *
+    * Two lists are similar if at least one hashtype is in both lists
+    * and the hashsum matches. All hashes are checked by default,
+    * if one doesn't match false is returned regardless of how many
+    * matched before. If a hash is forced, only this hash is compared,
+    * all others are ignored.
+    */
+   bool operator==(HashStringList const &other) const;
+   bool operator!=(HashStringList const &other) const;
+
+   HashStringList() {}
+
+   // simplifying API-compatibility constructors
+   HashStringList(std::string const &hash) {
+      if (hash.empty() == false)
+	 list.push_back(HashString(hash));
+   }
+   HashStringList(char const * const hash) {
+      if (hash != NULL && hash[0] != '\0')
+	 list.push_back(HashString(hash));
+   }
+
+   private:
+   std::vector<HashString> list;
+};
+
 class Hashes
 class Hashes
 {
 {
+   /** \brief dpointer placeholder */
+   void *d;
+
    public:
    public:
+   /* those will disappear in the future as it is hard to add new ones this way.
+    * Use Add* to build the results and get them via GetHashStringList() instead */
+   APT_DEPRECATED MD5Summation MD5;
+   APT_DEPRECATED SHA1Summation SHA1;
+   APT_DEPRECATED SHA256Summation SHA256;
+   APT_DEPRECATED SHA512Summation SHA512;
 
 
-   MD5Summation MD5;
-   SHA1Summation SHA1;
-   SHA256Summation SHA256;
-   SHA512Summation SHA512;
-   
    static const int UntilEOF = 0;
    static const int UntilEOF = 0;
 
 
-   inline bool Add(const unsigned char *Data,unsigned long long Size)
+   bool Add(const unsigned char * const Data, unsigned long long const Size, unsigned int const Hashes = ~0);
+   inline bool Add(const char * const Data)
+   {return Add((unsigned char const * const)Data,strlen(Data));};
+   inline bool Add(const unsigned char * const Beg,const unsigned char * const End)
+   {return Add(Beg,End-Beg);};
+
+   enum SupportedHashes { MD5SUM = (1 << 0), SHA1SUM = (1 << 1), SHA256SUM = (1 << 2),
+      SHA512SUM = (1 << 3) };
+   bool AddFD(int const Fd,unsigned long long Size = 0, unsigned int const Hashes = ~0);
+   bool AddFD(FileFd &Fd,unsigned long long Size = 0, unsigned int const Hashes = ~0);
+
+   HashStringList GetHashStringList();
+
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+   Hashes();
+   virtual ~Hashes();
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+
+   private:
+   APT_HIDDEN APT_CONST inline unsigned int boolsToFlag(bool const addMD5, bool const addSHA1, bool const addSHA256, bool const addSHA512)
    {
    {
-      return MD5.Add(Data,Size) && SHA1.Add(Data,Size) && SHA256.Add(Data,Size) && SHA512.Add(Data,Size);
+      unsigned int Hashes = ~0;
+      if (addMD5 == false) Hashes &= ~MD5SUM;
+      if (addSHA1 == false) Hashes &= ~SHA1SUM;
+      if (addSHA256 == false) Hashes &= ~SHA256SUM;
+      if (addSHA512 == false) Hashes &= ~SHA512SUM;
+      return Hashes;
+   }
+
+   public:
+   APT_DEPRECATED bool AddFD(int const Fd, unsigned long long Size, bool const addMD5,
+	 bool const addSHA1, bool const addSHA256, bool const addSHA512) {
+      return AddFD(Fd, Size, boolsToFlag(addMD5, addSHA1, addSHA256, addSHA512));
+   };
+
+   APT_DEPRECATED bool AddFD(FileFd &Fd, unsigned long long Size, bool const addMD5,
+	 bool const addSHA1, bool const addSHA256, bool const addSHA512) {
+      return AddFD(Fd, Size, boolsToFlag(addMD5, addSHA1, addSHA256, addSHA512));
    };
    };
-   inline bool Add(const char *Data) {return Add((unsigned char const *)Data,strlen(Data));};
-   inline bool AddFD(int const Fd,unsigned long long Size = 0)
-   { return AddFD(Fd, Size, true, true, true, true); };
-   bool AddFD(int const Fd, unsigned long long Size, bool const addMD5,
-	      bool const addSHA1, bool const addSHA256, bool const addSHA512);
-   inline bool AddFD(FileFd &Fd,unsigned long long Size = 0)
-   { return AddFD(Fd, Size, true, true, true, true); };
-   bool AddFD(FileFd &Fd, unsigned long long Size, bool const addMD5,
-	      bool const addSHA1, bool const addSHA256, bool const addSHA512);
-   inline bool Add(const unsigned char *Beg,const unsigned char *End) 
-                  {return Add(Beg,End-Beg);};
 };
 };
 
 
 #endif
 #endif

+ 18 - 22
apt-pkg/deb/deblistparser.cc

@@ -194,35 +194,31 @@ bool debListParser::NewVersion(pkgCache::VerIterator &Ver)
 /* This is to return the string describing the package in debian
 /* This is to return the string describing the package in debian
    form. If this returns the blank string then the entry is assumed to
    form. If this returns the blank string then the entry is assumed to
    only describe package properties */
    only describe package properties */
-string debListParser::Description()
+string debListParser::Description(std::string const &lang)
 {
 {
-   string const lang = DescriptionLanguage();
    if (lang.empty())
    if (lang.empty())
       return Section.FindS("Description");
       return Section.FindS("Description");
    else
    else
       return Section.FindS(string("Description-").append(lang).c_str());
       return Section.FindS(string("Description-").append(lang).c_str());
 }
 }
-                                                                        /*}}}*/
-// ListParser::DescriptionLanguage - Return the description lang string	/*{{{*/
-// ---------------------------------------------------------------------
-/* This is to return the string describing the language of
-   description. If this returns the blank string then the entry is
-   assumed to describe original description. */
-string debListParser::DescriptionLanguage()
+									/*}}}*/
+// ListParser::AvailableDescriptionLanguages				/*{{{*/
+std::vector<std::string> debListParser::AvailableDescriptionLanguages()
 {
 {
-   if (Section.FindS("Description").empty() == false)
-      return "";
-
-   std::vector<string> const lang = APT::Configuration::getLanguages(true);
-   for (std::vector<string>::const_iterator l = lang.begin();
-	l != lang.end(); ++l)
-      if (Section.FindS(string("Description-").append(*l).c_str()).empty() == false)
-	 return *l;
-
-   return "";
+   std::vector<std::string> const understood = APT::Configuration::getLanguages();
+   std::vector<std::string> avail;
+   if (Section.Exists("Description") == true)
+      avail.push_back("");
+   for (std::vector<std::string>::const_iterator lang = understood.begin(); lang != understood.end(); ++lang)
+   {
+      std::string const tagname = "Description-" + *lang;
+      if (Section.Exists(tagname.c_str()) == true)
+	 avail.push_back(*lang);
+   }
+   return avail;
 }
 }
-                                                                        /*}}}*/
-// ListParser::Description - Return the description_md5 MD5SumValue	/*{{{*/
+									/*}}}*/
+// ListParser::Description_md5 - Return the description_md5 MD5SumValue	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* This is to return the md5 string to allow the check if it is the right
 /* This is to return the md5 string to allow the check if it is the right
    description. If no Description-md5 is found in the section it will be
    description. If no Description-md5 is found in the section it will be
@@ -233,7 +229,7 @@ MD5SumValue debListParser::Description_md5()
    string const value = Section.FindS("Description-md5");
    string const value = Section.FindS("Description-md5");
    if (value.empty() == true)
    if (value.empty() == true)
    {
    {
-      std::string const desc = Description() + "\n";
+      std::string const desc = Description("") + "\n";
       if (desc == "\n")
       if (desc == "\n")
 	 return MD5SumValue();
 	 return MD5SumValue();
 
 

+ 2 - 2
apt-pkg/deb/deblistparser.h

@@ -68,8 +68,8 @@ class debListParser : public pkgCacheGenerator::ListParser
    virtual bool ArchitectureAll();
    virtual bool ArchitectureAll();
    virtual std::string Version();
    virtual std::string Version();
    virtual bool NewVersion(pkgCache::VerIterator &Ver);
    virtual bool NewVersion(pkgCache::VerIterator &Ver);
-   virtual std::string Description();
-   virtual std::string DescriptionLanguage();
+   virtual std::string Description(std::string const &lang);
+   virtual std::vector<std::string> AvailableDescriptionLanguages();
    virtual MD5SumValue Description_md5();
    virtual MD5SumValue Description_md5();
    virtual unsigned short VersionHash();
    virtual unsigned short VersionHash();
 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
 #if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)

+ 1 - 1
apt-pkg/deb/debmetaindex.cc

@@ -258,7 +258,7 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool const &GetAll) const
       vector <struct IndexTarget *> *targets = ComputeIndexTargets();
       vector <struct IndexTarget *> *targets = ComputeIndexTargets();
       for (vector <struct IndexTarget*>::const_iterator Target = targets->begin(); Target != targets->end(); ++Target) {
       for (vector <struct IndexTarget*>::const_iterator Target = targets->begin(); Target != targets->end(); ++Target) {
 	 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
 	 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
-			 (*Target)->ShortDesc, HashString());
+			 (*Target)->ShortDesc, HashStringList());
       }
       }
       delete targets;
       delete targets;
 
 

+ 51 - 50
apt-pkg/deb/debrecords.cc

@@ -73,36 +73,17 @@ string debRecordParser::Homepage()
    return Section.FindS("Homepage");
    return Section.FindS("Homepage");
 }
 }
 									/*}}}*/
 									/*}}}*/
-// RecordParser::MD5Hash - Return the archive hash			/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-string debRecordParser::MD5Hash()
-{
-   return Section.FindS("MD5Sum");
-}
-									/*}}}*/
-// RecordParser::SHA1Hash - Return the archive hash			/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-string debRecordParser::SHA1Hash()
-{
-   return Section.FindS("SHA1");
-}
-									/*}}}*/
-// RecordParser::SHA256Hash - Return the archive hash			/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-string debRecordParser::SHA256Hash()
-{
-   return Section.FindS("SHA256");
-}
-									/*}}}*/
-// RecordParser::SHA512Hash - Return the archive hash			/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-string debRecordParser::SHA512Hash()
+// RecordParser::Hashes - return the available archive hashes		/*{{{*/
+HashStringList debRecordParser::Hashes() const
 {
 {
-   return Section.FindS("SHA512");
+   HashStringList hashes;
+   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+   {
+      std::string const hash = Section.FindS(*type);
+      if (hash.empty() == false)
+	 hashes.push_back(HashString(*type, hash));
+   }
+   return hashes;
 }
 }
 									/*}}}*/
 									/*}}}*/
 // RecordParser::Maintainer - Return the maintainer email		/*{{{*/
 // RecordParser::Maintainer - Return the maintainer email		/*{{{*/
@@ -125,10 +106,12 @@ string debRecordParser::RecordField(const char *fieldName)
 // RecordParser::ShortDesc - Return a 1 line description		/*{{{*/
 // RecordParser::ShortDesc - Return a 1 line description		/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-string debRecordParser::ShortDesc()
+string debRecordParser::ShortDesc(std::string const &lang)
 {
 {
-   string Res = LongDesc();
-   string::size_type Pos = Res.find('\n');
+   string const Res = LongDesc(lang);
+   if (Res.empty() == true)
+      return "";
+   string::size_type const Pos = Res.find('\n');
    if (Pos == string::npos)
    if (Pos == string::npos)
       return Res;
       return Res;
    return string(Res,0,Pos);
    return string(Res,0,Pos);
@@ -137,26 +120,44 @@ string debRecordParser::ShortDesc()
 // RecordParser::LongDesc - Return a longer description			/*{{{*/
 // RecordParser::LongDesc - Return a longer description			/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-string debRecordParser::LongDesc()
-{
-  string orig, dest;
+string debRecordParser::LongDesc(std::string const &lang)
+{
+   string orig;
+   if (lang.empty() == true)
+   {
+      std::vector<string> const lang = APT::Configuration::getLanguages();
+      for (std::vector<string>::const_iterator l = lang.begin();
+	    l != lang.end(); ++l)
+      {
+	 std::string const tagname = "Description-" + *l;
+	 orig = Section.FindS(tagname.c_str());
+	 if (orig.empty() == false)
+	    break;
+	 else if (*l == "en")
+	 {
+	    orig = Section.FindS("Description");
+	    if (orig.empty() == false)
+	       break;
+	 }
+      }
+      if (orig.empty() == true)
+	 orig = Section.FindS("Description");
+   }
+   else
+   {
+      std::string const tagname = "Description-" + lang;
+      orig = Section.FindS(tagname.c_str());
+      if (orig.empty() == true && lang == "en")
+	 orig = Section.FindS("Description");
+   }
 
 
-  if (!Section.FindS("Description").empty())
-     orig = Section.FindS("Description").c_str();
-  else
-  {
-     std::vector<string> const lang = APT::Configuration::getLanguages();
-     for (std::vector<string>::const_iterator l = lang.begin();
-	  orig.empty() && l != lang.end(); ++l)
-	orig = Section.FindS(string("Description-").append(*l).c_str());
-  }
+   char const * const codeset = nl_langinfo(CODESET);
+   if (strcmp(codeset,"UTF-8") != 0) {
+      string dest;
+      UTF8ToCodeset(codeset, orig, &dest);
+      return dest;
+   }
 
 
-  char const * const codeset = nl_langinfo(CODESET);
-  if (strcmp(codeset,"UTF-8") != 0) {
-     UTF8ToCodeset(codeset, orig, &dest);
-     orig = dest;
-   }    
-  
    return orig;
    return orig;
 }
 }
 									/*}}}*/
 									/*}}}*/

+ 5 - 7
apt-pkg/deb/debrecords.h

@@ -42,17 +42,15 @@ class debRecordParser : public pkgRecords::Parser
 
 
    // These refer to the archive file for the Version
    // These refer to the archive file for the Version
    virtual std::string FileName();
    virtual std::string FileName();
-   virtual std::string MD5Hash();
-   virtual std::string SHA1Hash();
-   virtual std::string SHA256Hash();
-   virtual std::string SHA512Hash();
    virtual std::string SourcePkg();
    virtual std::string SourcePkg();
    virtual std::string SourceVer();
    virtual std::string SourceVer();
-   
+
+   virtual HashStringList Hashes() const;
+
    // These are some general stats about the package
    // These are some general stats about the package
    virtual std::string Maintainer();
    virtual std::string Maintainer();
-   virtual std::string ShortDesc();
-   virtual std::string LongDesc();
+   virtual std::string ShortDesc(std::string const &lang);
+   virtual std::string LongDesc(std::string const &lang);
    virtual std::string Name();
    virtual std::string Name();
    virtual std::string Homepage();
    virtual std::string Homepage();
 
 

+ 99 - 71
apt-pkg/deb/debsrcrecords.cc

@@ -124,85 +124,113 @@ bool debSrcRecordParser::Files(std::vector<pkgSrcRecords::File> &List)
 {
 {
    List.erase(List.begin(),List.end());
    List.erase(List.begin(),List.end());
 
 
-   // map from the Hashsum field to the hashsum function,
-   // unfortunately this is not a 1:1 mapping from
-   // Hashes::SupporedHashes as e.g. Files is a historic name for the md5
-   const std::pair<const char*, const char*> SourceHashFields[] = {
-      std::make_pair( "Checksums-Sha512",  "SHA512"),
-      std::make_pair( "Checksums-Sha256",  "SHA256"),
-      std::make_pair( "Checksums-Sha1",  "SHA1"),
-      std::make_pair( "Files",  "MD5Sum"),      // historic Name
-   };
-   
-   for (unsigned int i=0;
-        i < sizeof(SourceHashFields)/sizeof(SourceHashFields[0]);
-        i++)
-   {
-      string Files = Sect.FindS(SourceHashFields[i].first);
-      if (Files.empty() == true)
-         continue;
+   // Stash the / terminated directory prefix
+   string Base = Sect.FindS("Directory");
+   if (Base.empty() == false && Base[Base.length()-1] != '/')
+      Base += '/';
 
 
-      // Stash the / terminated directory prefix
-      string Base = Sect.FindS("Directory");
-      if (Base.empty() == false && Base[Base.length()-1] != '/')
-         Base += '/';
+   std::vector<std::string> const compExts = APT::Configuration::getCompressorExtensions();
 
 
-      std::vector<std::string> const compExts = APT::Configuration::getCompressorExtensions();
+   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+   {
+      // derive field from checksum type
+      std::string checksumField("Checksums-");
+      if (strcmp(*type, "MD5Sum") == 0)
+	 checksumField = "Files"; // historic name for MD5 checksums
+      else
+	 checksumField.append(*type);
+
+      string const Files = Sect.FindS(checksumField.c_str());
+      if (Files.empty() == true)
+	 continue;
 
 
       // Iterate over the entire list grabbing each triplet
       // Iterate over the entire list grabbing each triplet
       const char *C = Files.c_str();
       const char *C = Files.c_str();
       while (*C != 0)
       while (*C != 0)
-      {   
-         pkgSrcRecords::File F;
-         string Size;
-         
-         // Parse each of the elements
-         std::string RawHash;
-         if (ParseQuoteWord(C, RawHash) == false ||
-             ParseQuoteWord(C, Size) == false ||
-             ParseQuoteWord(C, F.Path) == false)
-            return _error->Error("Error parsing '%s' record", 
-                                 SourceHashFields[i].first);
-         // assign full hash string
-         F.Hash = HashString(SourceHashFields[i].second, RawHash).toStr();
-         // API compat hack 
-         if(strcmp(SourceHashFields[i].second, "MD5Sum") == 0)
-            F.MD5Hash = RawHash;
-         
-         // Parse the size and append the directory
-         F.Size = atoi(Size.c_str());
-         F.Path = Base + F.Path;
-         
-         // Try to guess what sort of file it is we are getting.
-         string::size_type Pos = F.Path.length()-1;
-         while (1)
-         {
-            string::size_type Tmp = F.Path.rfind('.',Pos);
-            if (Tmp == string::npos)
-               break;
-            if (F.Type == "tar") {
-               // source v3 has extension 'debian.tar.*' instead of 'diff.*'
-               if (string(F.Path, Tmp+1, Pos-Tmp) == "debian")
-                  F.Type = "diff";
-               break;
-            }
-            F.Type = string(F.Path,Tmp+1,Pos-Tmp);
-            
-            if (std::find(compExts.begin(), compExts.end(), std::string(".").append(F.Type)) != compExts.end() ||
-                F.Type == "tar")
-            {
-               Pos = Tmp-1;
-               continue;
-            }
-	 
-            break;
-         }
-      
-         List.push_back(F);
+      {
+	 string hash, size, path;
+
+	 // Parse each of the elements
+	 if (ParseQuoteWord(C, hash) == false ||
+	       ParseQuoteWord(C, size) == false ||
+	       ParseQuoteWord(C, path) == false)
+	    return _error->Error("Error parsing file record in %s of source package %s", checksumField.c_str(), Package().c_str());
+
+	 HashString const hashString(*type, hash);
+	 if (Base.empty() == false)
+	    path = Base + path;
+
+	 // look if we have a record for this file already
+	 std::vector<pkgSrcRecords::File>::iterator file = List.begin();
+	 for (; file != List.end(); ++file)
+	    if (file->Path == path)
+	       break;
+
+	 // we have it already, store the new hash and be done
+	 if (file != List.end())
+	 {
+#if __GNUC__ >= 4
+	// set for compatibility only, so warn users not us
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+	    if (checksumField == "Files")
+	       file->MD5Hash = hash;
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+	    // an error here indicates that we have two different hashes for the same file
+	    if (file->Hashes.push_back(hashString) == false)
+	       return _error->Error("Error parsing checksum in %s of source package %s", checksumField.c_str(), Package().c_str());
+	    continue;
+	 }
+
+	 // we haven't seen this file yet
+	 pkgSrcRecords::File F;
+	 F.Path = path;
+	 F.Size = strtoull(size.c_str(), NULL, 10);
+	 F.Hashes.push_back(hashString);
+
+#if __GNUC__ >= 4
+	// set for compatibility only, so warn users not us
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+	 if (checksumField == "Files")
+	    F.MD5Hash = hash;
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+
+	 // Try to guess what sort of file it is we are getting.
+	 string::size_type Pos = F.Path.length()-1;
+	 while (1)
+	 {
+	    string::size_type Tmp = F.Path.rfind('.',Pos);
+	    if (Tmp == string::npos)
+	       break;
+	    if (F.Type == "tar") {
+	       // source v3 has extension 'debian.tar.*' instead of 'diff.*'
+	       if (string(F.Path, Tmp+1, Pos-Tmp) == "debian")
+		  F.Type = "diff";
+	       break;
+	    }
+	    F.Type = string(F.Path,Tmp+1,Pos-Tmp);
+
+	    if (std::find(compExts.begin(), compExts.end(), std::string(".").append(F.Type)) != compExts.end() ||
+		  F.Type == "tar")
+	    {
+	       Pos = Tmp-1;
+	       continue;
+	    }
+
+	    break;
+	 }
+	 List.push_back(F);
       }
       }
-      break;
    }
    }
-   return (List.size() > 0);
+
+   return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
 // SrcRecordParser::~SrcRecordParser - Destructor			/*{{{*/
 // SrcRecordParser::~SrcRecordParser - Destructor			/*{{{*/

+ 5 - 3
apt-pkg/indexcopy.cc

@@ -516,7 +516,7 @@ bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
       return false;
       return false;
    }
    }
 
 
-   if (!Record->Hash.VerifyFile(prefix+file))
+   if (!Record->Hashes.VerifyFile(prefix+file))
    {
    {
       _error->Warning(_("Hash mismatch for: %s"),file.c_str());
       _error->Warning(_("Hash mismatch for: %s"),file.c_str());
       return false;
       return false;
@@ -524,8 +524,10 @@ bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
 
 
    if(Debug == true)
    if(Debug == true)
    {
    {
-      cout << "File: " << prefix+file << endl;
-      cout << "Expected Hash " << Record->Hash.toStr() << endl;
+      cout << "File: " << prefix+file << endl
+	 << "Expected Hash " << endl;
+      for (HashStringList::const_iterator hs = Record->Hashes.begin(); hs != Record->Hashes.end(); ++hs)
+	 std::cout <<  "\t- " << hs->toStr() << std::endl;
    }
    }
 
 
    return true;
    return true;

+ 1 - 1
apt-pkg/indexrecords.cc

@@ -105,7 +105,7 @@ bool indexRecords::Load(const string Filename)				/*{{{*/
 	    return false;
 	    return false;
 	 indexRecords::checkSum *Sum = new indexRecords::checkSum;
 	 indexRecords::checkSum *Sum = new indexRecords::checkSum;
 	 Sum->MetaKeyFilename = Name;
 	 Sum->MetaKeyFilename = Name;
-	 Sum->Hash = HashString(HashString::SupportedHashes()[i],Hash);
+	 Sum->Hashes.push_back(HashString(HashString::SupportedHashes()[i],Hash));
 	 Sum->Size = Size;
 	 Sum->Size = Size;
 	 Entries[Name] = Sum;
 	 Entries[Name] = Sum;
       }
       }

+ 11 - 1
apt-pkg/indexrecords.h

@@ -55,11 +55,21 @@ class indexRecords
    virtual ~indexRecords(){};
    virtual ~indexRecords(){};
 };
 };
 
 
+#if __GNUC__ >= 4
+	// ensure that con- & de-structor don't trigger this warning
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
 struct indexRecords::checkSum
 struct indexRecords::checkSum
 {
 {
    std::string MetaKeyFilename;
    std::string MetaKeyFilename;
-   HashString Hash;
+   HashStringList Hashes;
    unsigned long long Size;
    unsigned long long Size;
+
+   APT_DEPRECATED HashString Hash;
 };
 };
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
 
 
 #endif
 #endif

+ 16 - 8
apt-pkg/pkgcache.cc

@@ -168,15 +168,23 @@ bool pkgCache::ReMap(bool const &Errorchecks)
    if (Map.Size() < HeaderP->CacheFileSize)
    if (Map.Size() < HeaderP->CacheFileSize)
       return _error->Error(_("The package cache file is corrupted, it is too small"));
       return _error->Error(_("The package cache file is corrupted, it is too small"));
 
 
+   if (HeaderP->VerSysName == 0 || HeaderP->Architecture == 0 || HeaderP->Architectures == 0)
+      return _error->Error(_("The package cache file is corrupted"));
+
    // Locate our VS..
    // Locate our VS..
-   if (HeaderP->VerSysName == 0 ||
-       (VS = pkgVersioningSystem::GetVS(StrP + HeaderP->VerSysName)) == 0)
+   if ((VS = pkgVersioningSystem::GetVS(StrP + HeaderP->VerSysName)) == 0)
       return _error->Error(_("This APT does not support the versioning system '%s'"),StrP + HeaderP->VerSysName);
       return _error->Error(_("This APT does not support the versioning system '%s'"),StrP + HeaderP->VerSysName);
 
 
-   // Chcek the arhcitecture
-   if (HeaderP->Architecture == 0 ||
-       _config->Find("APT::Architecture") != StrP + HeaderP->Architecture)
-      return _error->Error(_("The package cache was built for a different architecture"));
+   // Check the architecture
+   std::vector<std::string> archs = APT::Configuration::getArchitectures();
+   std::vector<std::string>::const_iterator a = archs.begin();
+   std::string list = *a;
+   for (++a; a != archs.end(); ++a)
+      list.append(",").append(*a);
+   if (_config->Find("APT::Architecture") != StrP + HeaderP->Architecture ||
+	 list != StrP + HeaderP->Architectures)
+      return _error->Error(_("The package cache was built for different architectures: %s vs %s"), StrP + HeaderP->Architectures, list.c_str());
+
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
@@ -822,7 +830,7 @@ int pkgCache::VerIterator::CompareVer(const VerIterator &B) const
 // VerIterator::Downloadable - Checks if the version is downloadable	/*{{{*/
 // VerIterator::Downloadable - Checks if the version is downloadable	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-bool pkgCache::VerIterator::Downloadable() const
+APT_PURE bool pkgCache::VerIterator::Downloadable() const
 {
 {
    VerFileIterator Files = FileList();
    VerFileIterator Files = FileList();
    for (; Files.end() == false; ++Files)
    for (; Files.end() == false; ++Files)
@@ -835,7 +843,7 @@ bool pkgCache::VerIterator::Downloadable() const
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* This checks to see if any of the versions files are not NotAutomatic. 
 /* This checks to see if any of the versions files are not NotAutomatic. 
    True if this version is selectable for automatic installation. */
    True if this version is selectable for automatic installation. */
-bool pkgCache::VerIterator::Automatic() const
+APT_PURE bool pkgCache::VerIterator::Automatic() const
 {
 {
    VerFileIterator Files = FileList();
    VerFileIterator Files = FileList();
    for (; Files.end() == false; ++Files)
    for (; Files.end() == false; ++Files)

+ 3 - 1
apt-pkg/pkgcache.h

@@ -286,8 +286,10 @@ struct pkgCache::Header
    map_ptrloc StringList;
    map_ptrloc StringList;
    /** \brief String representing the version system used */
    /** \brief String representing the version system used */
    map_ptrloc VerSysName;
    map_ptrloc VerSysName;
-   /** \brief Architecture(s) the cache was built against */
+   /** \brief native architecture the cache was built against */
    map_ptrloc Architecture;
    map_ptrloc Architecture;
+   /** \brief all architectures the cache was built against */
+   map_ptrloc Architectures;
    /** \brief The maximum size of a raw entry from the original Package file */
    /** \brief The maximum size of a raw entry from the original Package file */
    unsigned long MaxVerFileSize;
    unsigned long MaxVerFileSize;
    /** \brief The maximum size of a raw entry from the original Translation file */
    /** \brief The maximum size of a raw entry from the original Translation file */

+ 58 - 41
apt-pkg/pkgcachegen.cc

@@ -74,13 +74,31 @@ pkgCacheGenerator::pkgCacheGenerator(DynamicMMap *pMap,OpProgress *Prog) :
       // Starting header
       // Starting header
       *Cache.HeaderP = pkgCache::Header();
       *Cache.HeaderP = pkgCache::Header();
       map_ptrloc const idxVerSysName = WriteStringInMap(_system->VS->Label);
       map_ptrloc const idxVerSysName = WriteStringInMap(_system->VS->Label);
+      if (unlikely(idxVerSysName == 0))
+	 return;
       Cache.HeaderP->VerSysName = idxVerSysName;
       Cache.HeaderP->VerSysName = idxVerSysName;
       // this pointer is set in ReMap, but we need it now for WriteUniqString
       // this pointer is set in ReMap, but we need it now for WriteUniqString
       Cache.StringItemP = (pkgCache::StringItem *)Map.Data();
       Cache.StringItemP = (pkgCache::StringItem *)Map.Data();
       map_ptrloc const idxArchitecture = WriteUniqString(_config->Find("APT::Architecture"));
       map_ptrloc const idxArchitecture = WriteUniqString(_config->Find("APT::Architecture"));
-      Cache.HeaderP->Architecture = idxArchitecture;
-      if (unlikely(idxVerSysName == 0 || idxArchitecture == 0))
+      if (unlikely(idxArchitecture == 0))
 	 return;
 	 return;
+      Cache.HeaderP->Architecture = idxArchitecture;
+
+      std::vector<std::string> archs = APT::Configuration::getArchitectures();
+      if (archs.size() > 1)
+      {
+	 std::vector<std::string>::const_iterator a = archs.begin();
+	 std::string list = *a;
+	 for (++a; a != archs.end(); ++a)
+	    list.append(",").append(*a);
+	 map_ptrloc const idxArchitectures = WriteStringInMap(list);
+	 if (unlikely(idxArchitectures == 0))
+	    return;
+	 Cache.HeaderP->Architectures = idxArchitectures;
+      }
+      else
+	 Cache.HeaderP->Architectures = idxArchitecture;
+
       Cache.ReMap();
       Cache.ReMap();
    }
    }
    else
    else
@@ -302,10 +320,9 @@ bool pkgCacheGenerator::MergeListPackage(ListParser &List, pkgCache::PkgIterator
 
 
    // Find the right version to write the description
    // Find the right version to write the description
    MD5SumValue CurMd5 = List.Description_md5();
    MD5SumValue CurMd5 = List.Description_md5();
-   if (CurMd5.Value().empty() == true || List.Description().empty() == true)
+   if (CurMd5.Value().empty() == true && List.Description("").empty() == true)
       return true;
       return true;
-   std::string CurLang = List.DescriptionLanguage();
-
+   std::vector<std::string> availDesc = List.AvailableDescriptionLanguages();
    for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
    for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
    {
    {
       pkgCache::DescIterator VerDesc = Ver.DescriptionList();
       pkgCache::DescIterator VerDesc = Ver.DescriptionList();
@@ -314,31 +331,16 @@ bool pkgCacheGenerator::MergeListPackage(ListParser &List, pkgCache::PkgIterator
       if (VerDesc.end() == true || MD5SumValue(VerDesc.md5()) != CurMd5)
       if (VerDesc.end() == true || MD5SumValue(VerDesc.md5()) != CurMd5)
 	 continue;
 	 continue;
 
 
-      // don't add a new description if we have one for the given
-      // md5 && language
-      if (IsDuplicateDescription(VerDesc, CurMd5, CurLang) == true)
-	 continue;
-
-      pkgCache::DescIterator Desc;
-      Dynamic<pkgCache::DescIterator> DynDesc(Desc);
-
-      map_ptrloc const descindex = NewDescription(Desc, CurLang, CurMd5, VerDesc->md5sum);
-      if (unlikely(descindex == 0 && _error->PendingError()))
-	 return _error->Error(_("Error occurred while processing %s (%s%d)"),
-			      Pkg.Name(), "NewDescription", 1);
-
-      Desc->ParentPkg = Pkg.Index();
-
-      // we add at the end, so that the start is constant as we need
-      // that to be able to efficiently share these lists
-      VerDesc = Ver.DescriptionList(); // old value might be invalid after ReMap
-      for (;VerDesc.end() == false && VerDesc->NextDesc != 0; ++VerDesc);
-      map_ptrloc * const LastNextDesc = (VerDesc.end() == true) ? &Ver->DescriptionList : &VerDesc->NextDesc;
-      *LastNextDesc = descindex;
+      map_ptrloc md5idx = VerDesc->md5sum;
+      for (std::vector<std::string>::const_iterator CurLang = availDesc.begin(); CurLang != availDesc.end(); ++CurLang)
+      {
+	 // don't add a new description if we have one for the given
+	 // md5 && language
+	 if (IsDuplicateDescription(VerDesc, CurMd5, *CurLang) == true)
+	    continue;
 
 
-      if (NewFileDesc(Desc,List) == false)
-	 return _error->Error(_("Error occurred while processing %s (%s%d)"),
-			      Pkg.Name(), "NewFileDesc", 1);
+	 AddNewDescription(List, Ver, *CurLang, CurMd5, md5idx);
+      }
 
 
       // we can stop here as all "same" versions will share the description
       // we can stop here as all "same" versions will share the description
       break;
       break;
@@ -486,11 +488,10 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
       return true;
       return true;
    }
    }
 
 
-   /* Record the Description (it is not translated) */
+   /* Record the Description(s) based on their master md5sum */
    MD5SumValue CurMd5 = List.Description_md5();
    MD5SumValue CurMd5 = List.Description_md5();
-   if (CurMd5.Value().empty() == true || List.Description().empty() == true)
+   if (CurMd5.Value().empty() == true && List.Description("").empty() == true)
       return true;
       return true;
-   std::string CurLang = List.DescriptionLanguage();
 
 
    /* Before we add a new description we first search in the group for
    /* Before we add a new description we first search in the group for
       a version with a description of the same MD5 - if so we reuse this
       a version with a description of the same MD5 - if so we reuse this
@@ -501,28 +502,44 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
       for (pkgCache::VerIterator V = P.VersionList();
       for (pkgCache::VerIterator V = P.VersionList();
 	   V.end() == false; ++V)
 	   V.end() == false; ++V)
       {
       {
-	 if (IsDuplicateDescription(V.DescriptionList(), CurMd5, "") == false)
+	 if (V->DescriptionList == 0 || MD5SumValue(V.DescriptionList().md5()) != CurMd5)
 	    continue;
 	    continue;
 	 Ver->DescriptionList = V->DescriptionList;
 	 Ver->DescriptionList = V->DescriptionList;
-	 return true;
       }
       }
    }
    }
 
 
-   // We haven't found reusable descriptions, so add the first description
-   pkgCache::DescIterator Desc = Ver.DescriptionList();
+   // We haven't found reusable descriptions, so add the first description(s)
+   map_ptrloc md5idx = Ver->DescriptionList == 0 ? 0 : Ver.DescriptionList()->md5sum;
+   std::vector<std::string> availDesc = List.AvailableDescriptionLanguages();
+   for (std::vector<std::string>::const_iterator CurLang = availDesc.begin(); CurLang != availDesc.end(); ++CurLang)
+      if (AddNewDescription(List, Ver, *CurLang, CurMd5, md5idx) == false)
+	 return false;
+   return true;
+}
+									/*}}}*/
+bool pkgCacheGenerator::AddNewDescription(ListParser &List, pkgCache::VerIterator &Ver, std::string const &lang, MD5SumValue const &CurMd5, map_ptrloc &md5idx) /*{{{*/
+{
+   pkgCache::DescIterator Desc;
    Dynamic<pkgCache::DescIterator> DynDesc(Desc);
    Dynamic<pkgCache::DescIterator> DynDesc(Desc);
 
 
-   map_ptrloc const descindex = NewDescription(Desc, CurLang, CurMd5, 0);
+   map_ptrloc const descindex = NewDescription(Desc, lang, CurMd5, md5idx);
    if (unlikely(descindex == 0 && _error->PendingError()))
    if (unlikely(descindex == 0 && _error->PendingError()))
       return _error->Error(_("Error occurred while processing %s (%s%d)"),
       return _error->Error(_("Error occurred while processing %s (%s%d)"),
-			   Pkg.Name(), "NewDescription", 2);
+	    Ver.ParentPkg().Name(), "NewDescription", 1);
+
+   md5idx = Desc->md5sum;
+   Desc->ParentPkg = Ver.ParentPkg().Index();
 
 
-   Desc->ParentPkg = Pkg.Index();
-   Ver->DescriptionList = descindex;
+   // we add at the end, so that the start is constant as we need
+   // that to be able to efficiently share these lists
+   pkgCache::DescIterator VerDesc = Ver.DescriptionList(); // old value might be invalid after ReMap
+   for (;VerDesc.end() == false && VerDesc->NextDesc != 0; ++VerDesc);
+   map_ptrloc * const LastNextDesc = (VerDesc.end() == true) ? &Ver->DescriptionList : &VerDesc->NextDesc;
+   *LastNextDesc = descindex;
 
 
    if (NewFileDesc(Desc,List) == false)
    if (NewFileDesc(Desc,List) == false)
       return _error->Error(_("Error occurred while processing %s (%s%d)"),
       return _error->Error(_("Error occurred while processing %s (%s%d)"),
-			   Pkg.Name(), "NewFileDesc", 2);
+	    Ver.ParentPkg().Name(), "NewFileDesc", 1);
 
 
    return true;
    return true;
 }
 }

+ 5 - 2
apt-pkg/pkgcachegen.h

@@ -125,6 +125,9 @@ class pkgCacheGenerator							/*{{{*/
    APT_HIDDEN bool AddImplicitDepends(pkgCache::GrpIterator &G, pkgCache::PkgIterator &P,
    APT_HIDDEN bool AddImplicitDepends(pkgCache::GrpIterator &G, pkgCache::PkgIterator &P,
 			   pkgCache::VerIterator &V);
 			   pkgCache::VerIterator &V);
    APT_HIDDEN bool AddImplicitDepends(pkgCache::VerIterator &V, pkgCache::PkgIterator &D);
    APT_HIDDEN bool AddImplicitDepends(pkgCache::VerIterator &V, pkgCache::PkgIterator &D);
+
+   APT_HIDDEN bool AddNewDescription(ListParser &List, pkgCache::VerIterator &Ver,
+	 std::string const &lang, MD5SumValue const &CurMd5, map_ptrloc &md5idx);
 };
 };
 									/*}}}*/
 									/*}}}*/
 // This is the abstract package list parser class.			/*{{{*/
 // This is the abstract package list parser class.			/*{{{*/
@@ -160,8 +163,8 @@ class pkgCacheGenerator::ListParser
    virtual bool ArchitectureAll() = 0;
    virtual bool ArchitectureAll() = 0;
    virtual std::string Version() = 0;
    virtual std::string Version() = 0;
    virtual bool NewVersion(pkgCache::VerIterator &Ver) = 0;
    virtual bool NewVersion(pkgCache::VerIterator &Ver) = 0;
-   virtual std::string Description() = 0;
-   virtual std::string DescriptionLanguage() = 0;
+   virtual std::string Description(std::string const &lang) = 0;
+   virtual std::vector<std::string> AvailableDescriptionLanguages() = 0;
    virtual MD5SumValue Description_md5() = 0;
    virtual MD5SumValue Description_md5() = 0;
    virtual unsigned short VersionHash() = 0;
    virtual unsigned short VersionHash() = 0;
    /** compare currently parsed version with given version
    /** compare currently parsed version with given version

+ 38 - 6
apt-pkg/pkgrecords.h

@@ -18,6 +18,8 @@
 #define PKGLIB_PKGRECORDS_H
 #define PKGLIB_PKGRECORDS_H
 
 
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgcache.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/macros.h>
 
 
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -56,17 +58,39 @@ class pkgRecords::Parser						/*{{{*/
    
    
    // These refer to the archive file for the Version
    // These refer to the archive file for the Version
    virtual std::string FileName() {return std::string();};
    virtual std::string FileName() {return std::string();};
-   virtual std::string MD5Hash() {return std::string();};
-   virtual std::string SHA1Hash() {return std::string();};
-   virtual std::string SHA256Hash() {return std::string();};
-   virtual std::string SHA512Hash() {return std::string();};
    virtual std::string SourcePkg() {return std::string();};
    virtual std::string SourcePkg() {return std::string();};
    virtual std::string SourceVer() {return std::string();};
    virtual std::string SourceVer() {return std::string();};
 
 
+   /** return all known hashes in this record.
+    *
+    * For authentication proposes packages come with hashsums which
+    * this method is supposed to parse and return so that clients can
+    * choose the hash to be used.
+    */
+   virtual HashStringList Hashes() const { return HashStringList(); };
+   APT_DEPRECATED std::string MD5Hash() const { return GetHashFromHashes("MD5Sum"); };
+   APT_DEPRECATED std::string SHA1Hash() const { return GetHashFromHashes("SHA1"); };
+   APT_DEPRECATED std::string SHA256Hash() const { return GetHashFromHashes("SHA256"); };
+   APT_DEPRECATED std::string SHA512Hash() const { return GetHashFromHashes("SHA512"); };
+
    // These are some general stats about the package
    // These are some general stats about the package
    virtual std::string Maintainer() {return std::string();};
    virtual std::string Maintainer() {return std::string();};
-   virtual std::string ShortDesc() {return std::string();};
-   virtual std::string LongDesc() {return std::string();};
+   /** return short description in language from record.
+    *
+    * @see #LongDesc
+    */
+   virtual std::string ShortDesc(std::string const &/*lang*/) {return std::string();};
+   /** return long description in language from record.
+    *
+    * If \b lang is empty the "best" available language will be
+    * returned as determined by the APT::Languages configuration.
+    * If a (requested) language can't be found in this record an empty
+    * string will be returned.
+    */
+   virtual std::string LongDesc(std::string const &/*lang*/) {return std::string();};
+   std::string ShortDesc() {return ShortDesc("");};
+   std::string LongDesc() {return LongDesc("");};
+
    virtual std::string Name() {return std::string();};
    virtual std::string Name() {return std::string();};
    virtual std::string Homepage() {return std::string();}
    virtual std::string Homepage() {return std::string();}
 
 
@@ -77,6 +101,14 @@ class pkgRecords::Parser						/*{{{*/
    virtual void GetRec(const char *&Start,const char *&Stop) {Start = Stop = 0;};
    virtual void GetRec(const char *&Start,const char *&Stop) {Start = Stop = 0;};
    
    
    virtual ~Parser() {};
    virtual ~Parser() {};
+
+   private:
+   APT_HIDDEN std::string GetHashFromHashes(char const * const type) const
+   {
+      HashStringList const hashes = Hashes();
+      HashString const * const hs = hashes.find(type);
+      return hs != NULL ? hs->HashValue() : "";
+   };
 };
 };
 									/*}}}*/
 									/*}}}*/
 #endif
 #endif

+ 13 - 4
apt-pkg/srcrecords.h

@@ -14,6 +14,7 @@
 #define PKGLIB_SRCRECORDS_H
 #define PKGLIB_SRCRECORDS_H
 
 
 #include <apt-pkg/macros.h>
 #include <apt-pkg/macros.h>
+#include <apt-pkg/hashes.h>
 
 
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -29,16 +30,24 @@ class pkgSrcRecords
 {
 {
    public:
    public:
 
 
+#if __GNUC__ >= 4
+	// ensure that con- & de-structor don't trigger this warning
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
    // Describes a single file
    // Describes a single file
    struct File
    struct File
    {
    {
-      std::string MD5Hash;
-      std::string Hash;
-      unsigned long Size;
       std::string Path;
       std::string Path;
       std::string Type;
       std::string Type;
+      unsigned long long Size;
+      HashStringList Hashes;
+      APT_DEPRECATED std::string MD5Hash;
    };
    };
-   
+#if __GNUC__ >= 4
+	#pragma GCC diagnostic pop
+#endif
+
    // Abstract parser for each source record
    // Abstract parser for each source record
    class Parser
    class Parser
    {
    {

+ 131 - 78
apt-pkg/tagfile.cc

@@ -47,6 +47,22 @@ public:
    unsigned long long Size;
    unsigned long long Size;
 };
 };
 
 
+static unsigned long AlphaHash(const char *Text, size_t Length)		/*{{{*/
+{
+   /* This very simple hash function for the last 8 letters gives
+      very good performance on the debian package files */
+   if (Length > 8)
+   {
+    Text += (Length - 8);
+    Length = 8;
+   }
+   unsigned long Res = 0;
+   for (size_t i = 0; i < Length; ++i)
+      Res = ((unsigned long)(Text[i]) & 0xDF) ^ (Res << 1);
+   return Res & 0xFF;
+}
+									/*}}}*/
+
 // TagFile::pkgTagFile - Constructor					/*{{{*/
 // TagFile::pkgTagFile - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
@@ -139,18 +155,23 @@ bool pkgTagFile::Resize(unsigned long long const newSize)
  */
  */
 bool pkgTagFile::Step(pkgTagSection &Tag)
 bool pkgTagFile::Step(pkgTagSection &Tag)
 {
 {
-   while (Tag.Scan(d->Start,d->End - d->Start) == false)
+   if(Tag.Scan(d->Start,d->End - d->Start) == false)
    {
    {
-      if (Fill() == false)
-	 return false;
-      
-      if(Tag.Scan(d->Start,d->End - d->Start))
-	 break;
+      do
+      {
+	 if (Fill() == false)
+	    return false;
+
+	 if(Tag.Scan(d->Start,d->End - d->Start, false))
+	    break;
+
+	 if (Resize() == false)
+	    return _error->Error(_("Unable to parse package file %s (1)"),
+		  d->Fd.Name().c_str());
 
 
-      if (Resize() == false)
-	 return _error->Error(_("Unable to parse package file %s (1)"),
-                              d->Fd.Name().c_str());
+      } while (Tag.Scan(d->Start,d->End - d->Start, false) == false);
    }
    }
+
    d->Start += Tag.size();
    d->Start += Tag.size();
    d->iOffset += Tag.size();
    d->iOffset += Tag.size();
 
 
@@ -244,7 +265,7 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
    if (Fill() == false)
    if (Fill() == false)
       return false;
       return false;
    
    
-   if (Tag.Scan(d->Start, d->End - d->Start) == false)
+   if (Tag.Scan(d->Start, d->End - d->Start, false) == false)
       return _error->Error(_("Unable to parse package file %s (2)"),d->Fd.Name().c_str());
       return _error->Error(_("Unable to parse package file %s (2)"),d->Fd.Name().c_str());
    
    
    return true;
    return true;
@@ -254,27 +275,46 @@ bool pkgTagFile::Jump(pkgTagSection &Tag,unsigned long long Offset)
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
 pkgTagSection::pkgTagSection()
 pkgTagSection::pkgTagSection()
-   : Section(0), TagCount(0), d(NULL), Stop(0)
+   : Section(0), d(NULL), Stop(0)
 {
 {
-   memset(&Indexes, 0, sizeof(Indexes));
-   memset(&AlphaIndexes, 0, sizeof(AlphaIndexes));
+   memset(&LookupTable, 0, sizeof(LookupTable));
 }
 }
 									/*}}}*/
 									/*}}}*/
 // TagSection::Scan - Scan for the end of the header information	/*{{{*/
 // TagSection::Scan - Scan for the end of the header information	/*{{{*/
-// ---------------------------------------------------------------------
-/* This looks for the first double new line in the data stream.
-   It also indexes the tags in the section. */
-bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
+bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength, bool const Restart)
 {
 {
+   Section = Start;
    const char *End = Start + MaxLength;
    const char *End = Start + MaxLength;
-   Stop = Section = Start;
-   memset(AlphaIndexes,0,sizeof(AlphaIndexes));
+
+   if (Restart == false && Tags.empty() == false)
+   {
+      Stop = Section + Tags.back().StartTag;
+      if (End <= Stop)
+	 return false;
+      Stop = (const char *)memchr(Stop,'\n',End - Stop);
+      if (Stop == NULL)
+	 return false;
+      ++Stop;
+   }
+   else
+   {
+      Stop = Section;
+      if (Tags.empty() == false)
+      {
+	 memset(&LookupTable, 0, sizeof(LookupTable));
+	 Tags.clear();
+      }
+      Tags.reserve(0x100);
+   }
+   size_t TagCount = Tags.size();
 
 
    if (Stop == 0)
    if (Stop == 0)
       return false;
       return false;
 
 
-   TagCount = 0;
-   while (TagCount+1 < sizeof(Indexes)/sizeof(Indexes[0]) && Stop < End)
+   TagData lastTagData(0);
+   lastTagData.EndTag = 0;
+   unsigned long lastTagHash = 0;
+   while (Stop < End)
    {
    {
       TrimRecord(true,End);
       TrimRecord(true,End);
 
 
@@ -286,12 +326,39 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
       // Start a new index and add it to the hash
       // Start a new index and add it to the hash
       if (isspace(Stop[0]) == 0)
       if (isspace(Stop[0]) == 0)
       {
       {
-	 Indexes[TagCount++] = Stop - Section;
-	 AlphaIndexes[AlphaHash(Stop,End)] = TagCount;
+	 // store the last found tag
+	 if (lastTagData.EndTag != 0)
+	 {
+	    if (LookupTable[lastTagHash] != 0)
+	       lastTagData.NextInBucket = LookupTable[lastTagHash];
+	    LookupTable[lastTagHash] = TagCount;
+	    Tags.push_back(lastTagData);
+	 }
+
+	 ++TagCount;
+	 lastTagData = TagData(Stop - Section);
+	 // find the colon separating tag and value
+	 char const * Colon = (char const *) memchr(Stop, ':', End - Stop);
+	 if (Colon == NULL)
+	    return false;
+	 // find the end of the tag (which might or might not be the colon)
+	 char const * EndTag = Colon;
+	 --EndTag;
+	 for (; EndTag > Stop && isspace(*EndTag) != 0; --EndTag)
+	    ;
+	 ++EndTag;
+	 lastTagData.EndTag = EndTag - Section;
+	 lastTagHash = AlphaHash(Stop, EndTag - Stop);
+	 // find the beginning of the value
+	 Stop = Colon + 1;
+	 for (; isspace(*Stop) != 0; ++Stop);
+	 if (Stop >= End)
+	    return false;
+	 lastTagData.StartValue = Stop - Section;
       }
       }
 
 
       Stop = (const char *)memchr(Stop,'\n',End - Stop);
       Stop = (const char *)memchr(Stop,'\n',End - Stop);
-      
+
       if (Stop == 0)
       if (Stop == 0)
 	 return false;
 	 return false;
 
 
@@ -302,7 +369,16 @@ bool pkgTagSection::Scan(const char *Start,unsigned long MaxLength)
       // Double newline marks the end of the record
       // Double newline marks the end of the record
       if (Stop+1 < End && Stop[1] == '\n')
       if (Stop+1 < End && Stop[1] == '\n')
       {
       {
-	 Indexes[TagCount] = Stop - Section;
+	 if (lastTagData.EndTag != 0)
+	 {
+	    if (LookupTable[lastTagHash] != 0)
+	       lastTagData.NextInBucket = LookupTable[lastTagHash];
+	    LookupTable[lastTagHash] = TagCount;
+	    Tags.push_back(lastTagData);
+	 }
+
+	 TagData const td(Stop - Section);
+	 Tags.push_back(td);
 	 TrimRecord(false,End);
 	 TrimRecord(false,End);
 	 return true;
 	 return true;
       }
       }
@@ -331,8 +407,8 @@ void pkgTagSection::Trim()
    for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
    for (; Stop > Section + 2 && (Stop[-2] == '\n' || Stop[-2] == '\r'); Stop--);
 }
 }
 									/*}}}*/
 									/*}}}*/
-// TagSection::Exists - return True if a tag exists                	/*{{{*/
-bool pkgTagSection::Exists(const char* const Tag)
+// TagSection::Exists - return True if a tag exists			/*{{{*/
+bool pkgTagSection::Exists(const char* const Tag) const
 {
 {
    unsigned int tmp;
    unsigned int tmp;
    return Find(Tag, tmp);
    return Find(Tag, tmp);
@@ -343,73 +419,43 @@ bool pkgTagSection::Exists(const char* const Tag)
 /* This searches the section for a tag that matches the given string. */
 /* This searches the section for a tag that matches the given string. */
 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
 bool pkgTagSection::Find(const char *Tag,unsigned int &Pos) const
 {
 {
-   unsigned int Length = strlen(Tag);
-   unsigned int I = AlphaIndexes[AlphaHash(Tag)];
-   if (I == 0)
+   size_t const Length = strlen(Tag);
+   unsigned int Bucket = LookupTable[AlphaHash(Tag, Length)];
+   if (Bucket == 0)
       return false;
       return false;
-   I--;
-   
-   for (unsigned int Counter = 0; Counter != TagCount; Counter++, 
-	I = (I+1)%TagCount)
+
+   for (; Bucket != 0; Bucket = Tags[Bucket - 1].NextInBucket)
    {
    {
-      const char *St;
-      St = Section + Indexes[I];
-      if (strncasecmp(Tag,St,Length) != 0)
+      if ((Tags[Bucket - 1].EndTag - Tags[Bucket - 1].StartTag) != Length)
 	 continue;
 	 continue;
 
 
-      // Make sure the colon is in the right place
-      const char *C = St + Length;
-      for (; isspace(*C) != 0; C++);
-      if (*C != ':')
+      char const * const St = Section + Tags[Bucket - 1].StartTag;
+      if (strncasecmp(Tag,St,Length) != 0)
 	 continue;
 	 continue;
-      Pos = I;
+
+      Pos = Bucket - 1;
       return true;
       return true;
    }
    }
 
 
    Pos = 0;
    Pos = 0;
    return false;
    return false;
 }
 }
-									/*}}}*/
-// TagSection::Find - Locate a tag					/*{{{*/
-// ---------------------------------------------------------------------
-/* This searches the section for a tag that matches the given string. */
 bool pkgTagSection::Find(const char *Tag,const char *&Start,
 bool pkgTagSection::Find(const char *Tag,const char *&Start,
 		         const char *&End) const
 		         const char *&End) const
 {
 {
-   unsigned int Length = strlen(Tag);
-   unsigned int I = AlphaIndexes[AlphaHash(Tag)];
-   if (I == 0)
+   unsigned int Pos;
+   if (Find(Tag, Pos) == false)
       return false;
       return false;
-   I--;
-   
-   for (unsigned int Counter = 0; Counter != TagCount; Counter++, 
-	I = (I+1)%TagCount)
-   {
-      const char *St;
-      St = Section + Indexes[I];
-      if (strncasecmp(Tag,St,Length) != 0)
-	 continue;
-      
-      // Make sure the colon is in the right place
-      const char *C = St + Length;
-      for (; isspace(*C) != 0; C++);
-      if (*C != ':')
-	 continue;
 
 
-      // Strip off the gunk from the start end
-      Start = C;
-      End = Section + Indexes[I+1];
-      if (Start >= End)
-	 return _error->Error("Internal parsing error");
-      
-      for (; (isspace(*Start) != 0 || *Start == ':') && Start < End; Start++);
-      for (; isspace(End[-1]) != 0 && End > Start; End--);
-      
-      return true;
-   }
-   
-   Start = End = 0;
-   return false;
+   Start = Section + Tags[Pos].StartValue;
+   // Strip off the gunk from the end
+   End = Section + Tags[Pos + 1].StartTag;
+   if (unlikely(Start > End))
+      return _error->Error("Internal parsing error");
+
+   for (; isspace(End[-1]) != 0 && End > Start; --End);
+
+   return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
 // TagSection::FindS - Find a string					/*{{{*/
 // TagSection::FindS - Find a string					/*{{{*/
@@ -504,6 +550,13 @@ bool pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag,
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
+APT_PURE unsigned int pkgTagSection::Count() const {			/*{{{*/
+   if (Tags.empty() == true)
+      return 0;
+   // the last element is just marking the end and isn't a real one
+   return Tags.size() - 1;
+}
+									/*}}}*/
 // TFRewrite - Rewrite a control record					/*{{{*/
 // TFRewrite - Rewrite a control record					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* This writes the control record to stdout rewriting it as necessary. The
 /* This writes the control record to stdout rewriting it as necessary. The

+ 42 - 21
apt-pkg/tagfile.h

@@ -25,6 +25,8 @@
 #include <stdio.h>
 #include <stdio.h>
 
 
 #include <string>
 #include <string>
+#include <vector>
+#include <list>
 
 
 #ifndef APT_8_CLEANER_HEADERS
 #ifndef APT_8_CLEANER_HEADERS
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/fileutl.h>
@@ -35,23 +37,20 @@ class FileFd;
 class pkgTagSection
 class pkgTagSection
 {
 {
    const char *Section;
    const char *Section;
-   // We have a limit of 256 tags per section.
-   unsigned int Indexes[256];
-   unsigned int AlphaIndexes[0x100];
-   unsigned int TagCount;
+   struct TagData {
+      unsigned int StartTag;
+      unsigned int EndTag;
+      unsigned int StartValue;
+      unsigned int NextInBucket;
+
+      TagData(unsigned int const StartTag) : StartTag(StartTag), NextInBucket(0) {}
+   };
+   std::vector<TagData> Tags;
+   unsigned int LookupTable[0x100];
+
    // dpointer placeholder (for later in case we need it)
    // dpointer placeholder (for later in case we need it)
    void *d;
    void *d;
 
 
-   /* This very simple hash function for the last 8 letters gives
-      very good performance on the debian package files */
-   inline static unsigned long AlphaHash(const char *Text, const char *End = 0)
-   {
-      unsigned long Res = 0;
-      for (; Text != End && *Text != ':' && *Text != 0; Text++)
-	 Res = ((unsigned long)(*Text) & 0xDF) ^ (Res << 1);
-      return Res & 0xFF;
-   }
-
    protected:
    protected:
    const char *Stop;
    const char *Stop;
 
 
@@ -69,17 +68,39 @@ class pkgTagSection
 		 unsigned long Flag) const;
 		 unsigned long Flag) const;
    bool static FindFlag(unsigned long &Flags, unsigned long Flag,
    bool static FindFlag(unsigned long &Flags, unsigned long Flag,
 				const char* Start, const char* Stop);
 				const char* Start, const char* Stop);
-   bool Scan(const char *Start,unsigned long MaxLength);
+
+   /** \brief searches the boundaries of the current section
+    *
+    * While parameter Start marks the beginning of the section, this method
+    * will search for the first double newline in the data stream which marks
+    * the end of the section. It also does a first pass over the content of
+    * the section parsing it as encountered for processing later on by Find
+    *
+    * @param Start is the beginning of the section
+    * @param MaxLength is the size of valid data in the stream pointed to by Start
+    * @param Restart if enabled internal state will be cleared, otherwise it is
+    *  assumed that now more data is available in the stream and the parsing will
+    *  start were it encountered insufficent data the last time.
+    *
+    * @return \b true if section end was found, \b false otherwise.
+    *  Beware that internal state will be inconsistent if \b false is returned!
+    */
+   APT_MUSTCHECK bool Scan(const char *Start, unsigned long MaxLength, bool const Restart = true);
    inline unsigned long size() const {return Stop - Section;};
    inline unsigned long size() const {return Stop - Section;};
    void Trim();
    void Trim();
    virtual void TrimRecord(bool BeforeRecord, const char* &End);
    virtual void TrimRecord(bool BeforeRecord, const char* &End);
-   
-   inline unsigned int Count() const {return TagCount;};
-   bool Exists(const char* const Tag);
- 
+
+   /** \brief amount of Tags in the current section
+    *
+    * Note: if a Tag is mentioned repeatly it will be counted multiple
+    * times, but only the last occurance is available via Find methods.
+    */
+   unsigned int Count() const;
+   bool Exists(const char* const Tag) const;
+
    inline void Get(const char *&Start,const char *&Stop,unsigned int I) const
    inline void Get(const char *&Start,const char *&Stop,unsigned int I) const
-                   {Start = Section + Indexes[I]; Stop = Section + Indexes[I+1];}
-	    
+                   {Start = Section + Tags[I].StartTag; Stop = Section + Tags[I+1].StartTag;}
+
    inline void GetSection(const char *&Start,const char *&Stop) const
    inline void GetSection(const char *&Start,const char *&Stop) const
    {
    {
       Start = Section;
       Start = Section;

+ 3 - 0
apt-private/acqprogress.cc

@@ -140,6 +140,9 @@ void AcqTextStatus::Stop()
    if (Quiet <= 0)
    if (Quiet <= 0)
       cout << '\r' << BlankLine << '\r' << flush;
       cout << '\r' << BlankLine << '\r' << flush;
 
 
+   if (_config->FindB("quiet::NoStatistic", false) == true)
+      return;
+
    if (FetchedBytes != 0 && _error->PendingError() == false)
    if (FetchedBytes != 0 && _error->PendingError() == false)
       ioprintf(cout,_("Fetched %sB in %s (%sB/s)\n"),
       ioprintf(cout,_("Fetched %sB in %s (%sB/s)\n"),
 	       SizeToStr(FetchedBytes).c_str(),
 	       SizeToStr(FetchedBytes).c_str(),

+ 8 - 5
cmdline/apt-extracttemplates.cc

@@ -103,10 +103,12 @@ bool DebFile::DoItem(Item &I, int &Fd)
 	if (strcmp(I.Name, "control") == 0)
 	if (strcmp(I.Name, "control") == 0)
 	{
 	{
 		delete [] Control;
 		delete [] Control;
-		Control = new char[I.Size+1];
-		Control[I.Size] = 0;
+		Control = new char[I.Size+3];
+		Control[I.Size] = '\n';
+		Control[I.Size + 1] = '\n';
+		Control[I.Size + 2] = '\0';
 		Which = IsControl;
 		Which = IsControl;
-		ControlLen = I.Size;
+		ControlLen = I.Size + 3;
 		// make it call the Process method below. this is so evil
 		// make it call the Process method below. this is so evil
 		Fd = -2;
 		Fd = -2;
 	}
 	}
@@ -162,9 +164,10 @@ bool DebFile::Process(Item &/*I*/, const unsigned char *data,
 bool DebFile::ParseInfo()
 bool DebFile::ParseInfo()
 {
 {
 	if (Control == NULL) return false;
 	if (Control == NULL) return false;
-	
+
 	pkgTagSection Section;
 	pkgTagSection Section;
-	Section.Scan(Control, ControlLen);
+	if (Section.Scan(Control, ControlLen) == false)
+		return false;
 
 
 	Package = Section.FindS("Package");
 	Package = Section.FindS("Package");
 	Version = GetInstalledVer(Package);
 	Version = GetInstalledVer(Package);

+ 12 - 7
cmdline/apt-get.cc

@@ -828,20 +828,25 @@ static bool DoSource(CommandLine &CmdL)
 	 queued.insert(Last->Index().ArchiveURI(I->Path));
 	 queued.insert(Last->Index().ArchiveURI(I->Path));
 
 
 	 // check if we have a file with that md5 sum already localy
 	 // check if we have a file with that md5 sum already localy
-	 if(!I->Hash.empty() && FileExists(flNotDir(I->Path)))  
-	 {
-            HashString hash_string = HashString(I->Hash);
-            if(hash_string.VerifyFile(flNotDir(I->Path)))
+	 std::string localFile = flNotDir(I->Path);
+	 if (FileExists(localFile) == true)
+	    if(I->Hashes.VerifyFile(localFile) == true)
 	    {
 	    {
 	       ioprintf(c1out,_("Skipping already downloaded file '%s'\n"),
 	       ioprintf(c1out,_("Skipping already downloaded file '%s'\n"),
-			flNotDir(I->Path).c_str());
+			localFile.c_str());
 	       continue;
 	       continue;
 	    }
 	    }
+
+	 // see if we have a hash (Acquire::ForceHash is the only way to have none)
+	 if (I->Hashes.usable() == false && _config->FindB("APT::Get::AllowUnauthenticated",false) == false)
+	 {
+	    ioprintf(c1out, "Skipping download of file '%s' as requested hashsum is not available for authentication\n",
+		     localFile.c_str());
+	    continue;
 	 }
 	 }
 
 
 	 new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
 	 new pkgAcqFile(&Fetcher,Last->Index().ArchiveURI(I->Path),
-			I->Hash,I->Size,
-			Last->Index().SourceInfo(*Last,*I),Src);
+			I->Hashes, I->Size, Last->Index().SourceInfo(*Last,*I), Src);
       }
       }
    }
    }
    
    

+ 3 - 3
doc/apt.conf.5.xml

@@ -390,9 +390,9 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
      <para>The setting <literal>Acquire::http::Pipeline-Depth</literal> can be used to
      <para>The setting <literal>Acquire::http::Pipeline-Depth</literal> can be used to
      enable HTTP pipelining (RFC 2616 section 8.1.2.2) which can be beneficial e.g. on
      enable HTTP pipelining (RFC 2616 section 8.1.2.2) which can be beneficial e.g. on
      high-latency connections. It specifies how many requests are sent in a pipeline.
      high-latency connections. It specifies how many requests are sent in a pipeline.
-     Previous APT versions had a default of 10 for this setting, but the default value
-     is now 0 (= disabled) to avoid problems with the ever-growing amount of webservers
-     and proxies which choose to not conform to the HTTP/1.1 specification.</para>
+     APT tries to detect and workaround misbehaving webservers and proxies at runtime, but
+     if you know that yours does not conform to the HTTP/1.1 specification pipelining can
+     be disabled by setting the value to 0. It is enabled by default with the value 10.</para>
 
 
      <para><literal>Acquire::http::AllowRedirect</literal> controls whether APT will follow
      <para><literal>Acquire::http::AllowRedirect</literal> controls whether APT will follow
      redirects, which is enabled by default.</para>
      redirects, which is enabled by default.</para>

+ 61 - 133
ftparchive/cachedb.cc

@@ -21,11 +21,13 @@
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/debfile.h>
 #include <apt-pkg/debfile.h>
 #include <apt-pkg/gpgv.h>
 #include <apt-pkg/gpgv.h>
+#include <apt-pkg/hashes.h>
 
 
 #include <netinet/in.h>       // htonl, etc
 #include <netinet/in.h>       // htonl, etc
 #include <ctype.h>
 #include <ctype.h>
 #include <stddef.h>
 #include <stddef.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
+#include <strings.h>
 
 
 #include "cachedb.h"
 #include "cachedb.h"
 
 
@@ -206,15 +208,10 @@ bool CacheDB::GetCurStat()
 									/*}}}*/
 									/*}}}*/
 // CacheDB::GetFileInfo - Get all the info about the file		/*{{{*/
 // CacheDB::GetFileInfo - Get all the info about the file		/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
-bool CacheDB::GetFileInfo(std::string const &FileName, bool const &DoControl, 
-                          bool const &DoContents,
-                          bool const &GenContentsOnly, 
-                          bool const &DoSource,
-                          bool const &DoMD5, bool const &DoSHA1,
-                          bool const &DoSHA256, bool const &DoSHA512, 
+bool CacheDB::GetFileInfo(std::string const &FileName, bool const &DoControl, bool const &DoContents,
+				bool const &GenContentsOnly, bool const DoSource, unsigned int const DoHashes,
                           bool const &checkMtime)
                           bool const &checkMtime)
 {
 {
-   bool result = true;
    this->FileName = FileName;
    this->FileName = FileName;
 
 
    if (GetCurStat() == false)
    if (GetCurStat() == false)
@@ -222,31 +219,28 @@ bool CacheDB::GetFileInfo(std::string const &FileName, bool const &DoControl,
    OldStat = CurStat;
    OldStat = CurStat;
 
 
    if (GetFileStat(checkMtime) == false)
    if (GetFileStat(checkMtime) == false)
-      return false;	
+      return false;
 
 
    /* if mtime changed, update CurStat from disk */
    /* if mtime changed, update CurStat from disk */
    if (checkMtime == true && OldStat.mtime != CurStat.mtime)
    if (checkMtime == true && OldStat.mtime != CurStat.mtime)
       CurStat.Flags = FlSize;
       CurStat.Flags = FlSize;
 
 
    Stats.Bytes += CurStat.FileSize;
    Stats.Bytes += CurStat.FileSize;
-   Stats.Packages++;
+   ++Stats.Packages;
 
 
    if ((DoControl && LoadControl() == false)
    if ((DoControl && LoadControl() == false)
-       || (DoContents && LoadContents(GenContentsOnly) == false)
-       || (DoSource && LoadSource() == false)
-       || (DoMD5 && GetMD5(false) == false)
-       || (DoSHA1 && GetSHA1(false) == false)
-       || (DoSHA256 && GetSHA256(false) == false)
-       || (DoSHA512 && GetSHA512(false) == false) )
+	 || (DoContents && LoadContents(GenContentsOnly) == false)
+	 || (DoSource && LoadSource() == false)
+	 || (DoHashes != 0 && GetHashes(false, DoHashes) == false)
+      )
    {
    {
-      result = false;
+      return false;
    }
    }
-    
-   return result;
+
+   return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
-
-bool CacheDB::LoadSource()
+bool CacheDB::LoadSource()						/*{{{*/
 {
 {
    // Try to read the control information out of the DB.
    // Try to read the control information out of the DB.
    if ((CurStat.Flags & FlSource) == FlSource)
    if ((CurStat.Flags & FlSource) == FlSource)
@@ -276,7 +270,7 @@ bool CacheDB::LoadSource()
 
 
    return true;
    return true;
 }
 }
-
+									/*}}}*/
 // CacheDB::LoadControl - Load Control information			/*{{{*/
 // CacheDB::LoadControl - Load Control information			/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
@@ -345,7 +339,7 @@ bool CacheDB::LoadContents(bool const &GenOnly)
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
-
+// CacheDB::GetHashes - Get the hashs					/*{{{*/
 static std::string bytes2hex(uint8_t *bytes, size_t length) {
 static std::string bytes2hex(uint8_t *bytes, size_t length) {
    char buf[3];
    char buf[3];
    std::string space;
    std::string space;
@@ -375,125 +369,59 @@ static void hex2bytes(uint8_t *bytes, const char *hex, int length) {
       bytes++;
       bytes++;
    } 
    } 
 }
 }
-
-// CacheDB::GetMD5 - Get the MD5 hash					/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool CacheDB::GetMD5(bool const &GenOnly)
+bool CacheDB::GetHashes(bool const GenOnly, unsigned int const DoHashes)
 {
 {
-   // Try to read the control information out of the DB.
-   if ((CurStat.Flags & FlMD5) == FlMD5)
-   {
-      if (GenOnly == true)
-	 return true;
-      
-      MD5Res = bytes2hex(CurStat.MD5, sizeof(CurStat.MD5));
-	 return true;
-   }
-   
-   Stats.MD5Bytes += CurStat.FileSize;
-	 
-   if (OpenFile() == false)
-      return false;
+   unsigned int FlHashes = DoHashes & (Hashes::MD5SUM | Hashes::SHA1SUM | Hashes::SHA256SUM | Hashes::SHA512SUM);
+   HashesList.clear();
 
 
-   MD5Summation MD5;
-   if (Fd->Seek(0) == false || MD5.AddFD(*Fd, CurStat.FileSize) == false)
-      return false;
-   
-   MD5Res = MD5.Result();
-   hex2bytes(CurStat.MD5, MD5Res.data(), sizeof(CurStat.MD5));
-      CurStat.Flags |= FlMD5;
-   return true;
-}
-									/*}}}*/
-// CacheDB::GetSHA1 - Get the SHA1 hash					/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool CacheDB::GetSHA1(bool const &GenOnly)
-{
-   // Try to read the control information out of the DB.
-   if ((CurStat.Flags & FlSHA1) == FlSHA1)
+   if (FlHashes != 0)
    {
    {
-      if (GenOnly == true)
-	 return true;
+      if (OpenFile() == false)
+	 return false;
 
 
-      SHA1Res = bytes2hex(CurStat.SHA1, sizeof(CurStat.SHA1));
-      return true;
-   }
-   
-   Stats.SHA1Bytes += CurStat.FileSize;
-	 
-   if (OpenFile() == false)
-      return false;
+      Hashes hashes;
+      if (Fd->Seek(0) == false || hashes.AddFD(*Fd, CurStat.FileSize, FlHashes) == false)
+	 return false;
 
 
-   SHA1Summation SHA1;
-   if (Fd->Seek(0) == false || SHA1.AddFD(*Fd, CurStat.FileSize) == false)
-      return false;
-   
-   SHA1Res = SHA1.Result();
-   hex2bytes(CurStat.SHA1, SHA1Res.data(), sizeof(CurStat.SHA1));
-   CurStat.Flags |= FlSHA1;
-   return true;
-}
-									/*}}}*/
-// CacheDB::GetSHA256 - Get the SHA256 hash				/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool CacheDB::GetSHA256(bool const &GenOnly)
-{
-   // Try to read the control information out of the DB.
-   if ((CurStat.Flags & FlSHA256) == FlSHA256)
-   {
-      if (GenOnly == true)
-	 return true;
-
-      SHA256Res = bytes2hex(CurStat.SHA256, sizeof(CurStat.SHA256));
-      return true;
+      HashStringList hl = hashes.GetHashStringList();
+      for (HashStringList::const_iterator hs = hl.begin(); hs != hl.end(); ++hs)
+      {
+	 HashesList.push_back(*hs);
+	 if (strcasecmp(hs->HashType().c_str(), "SHA512") == 0)
+	 {
+	    Stats.SHA512Bytes += CurStat.FileSize;
+	    hex2bytes(CurStat.SHA512, hs->HashValue().data(), sizeof(CurStat.SHA512));
+	    CurStat.Flags |= FlSHA512;
+	 }
+	 else if (strcasecmp(hs->HashType().c_str(), "SHA256") == 0)
+	 {
+	    Stats.SHA256Bytes += CurStat.FileSize;
+	    hex2bytes(CurStat.SHA256, hs->HashValue().data(), sizeof(CurStat.SHA256));
+	    CurStat.Flags |= FlSHA256;
+	 }
+	 else if (strcasecmp(hs->HashType().c_str(), "SHA1") == 0)
+	 {
+	    Stats.SHA1Bytes += CurStat.FileSize;
+	    hex2bytes(CurStat.SHA1, hs->HashValue().data(), sizeof(CurStat.SHA1));
+	    CurStat.Flags |= FlSHA1;
+	 }
+	 else if (strcasecmp(hs->HashType().c_str(), "MD5Sum") == 0)
+	 {
+	    Stats.MD5Bytes += CurStat.FileSize;
+	    hex2bytes(CurStat.MD5, hs->HashValue().data(), sizeof(CurStat.MD5));
+	    CurStat.Flags |= FlMD5;
+	 }
+	 else
+	    return _error->Error("Got unknown unrequested hashtype %s", hs->HashType().c_str());
+      }
    }
    }
-   
-   Stats.SHA256Bytes += CurStat.FileSize;
-	 
-   if (OpenFile() == false)
-      return false;
-
-   SHA256Summation SHA256;
-   if (Fd->Seek(0) == false || SHA256.AddFD(*Fd, CurStat.FileSize) == false)
-      return false;
-   
-   SHA256Res = SHA256.Result();
-   hex2bytes(CurStat.SHA256, SHA256Res.data(), sizeof(CurStat.SHA256));
-   CurStat.Flags |= FlSHA256;
-   return true;
-}
-									/*}}}*/
-// CacheDB::GetSHA256 - Get the SHA256 hash				/*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool CacheDB::GetSHA512(bool const &GenOnly)
-{
-   // Try to read the control information out of the DB.
-   if ((CurStat.Flags & FlSHA512) == FlSHA512)
-   {
-      if (GenOnly == true)
-	 return true;
-
-      SHA512Res = bytes2hex(CurStat.SHA512, sizeof(CurStat.SHA512));
+   if (GenOnly == true)
       return true;
       return true;
-   }
-   
-   Stats.SHA512Bytes += CurStat.FileSize;
-	 
-   if (OpenFile() == false)
-      return false;
 
 
-   SHA512Summation SHA512;
-   if (Fd->Seek(0) == false || SHA512.AddFD(*Fd, CurStat.FileSize) == false)
-      return false;
-   
-   SHA512Res = SHA512.Result();
-   hex2bytes(CurStat.SHA512, SHA512Res.data(), sizeof(CurStat.SHA512));
-   CurStat.Flags |= FlSHA512;
-   return true;
+   return HashesList.push_back(HashString("MD5Sum", bytes2hex(CurStat.MD5, sizeof(CurStat.MD5)))) &&
+      HashesList.push_back(HashString("SHA1", bytes2hex(CurStat.SHA1, sizeof(CurStat.SHA1)))) &&
+      HashesList.push_back(HashString("SHA256", bytes2hex(CurStat.SHA256, sizeof(CurStat.SHA256)))) &&
+      HashesList.push_back(HashString("SHA512", bytes2hex(CurStat.SHA512, sizeof(CurStat.SHA512))));
 }
 }
 									/*}}}*/
 									/*}}}*/
 // CacheDB::Finish - Write back the cache structure			/*{{{*/
 // CacheDB::Finish - Write back the cache structure			/*{{{*/

+ 11 - 20
ftparchive/cachedb.h

@@ -12,6 +12,7 @@
 #ifndef CACHEDB_H
 #ifndef CACHEDB_H
 #define CACHEDB_H
 #define CACHEDB_H
 
 
+#include <apt-pkg/hashes.h>
 #include <apt-pkg/debfile.h>
 #include <apt-pkg/debfile.h>
 
 
 #include <db.h>
 #include <db.h>
@@ -90,11 +91,8 @@ class CacheDB
    bool LoadControl();
    bool LoadControl();
    bool LoadContents(bool const &GenOnly);
    bool LoadContents(bool const &GenOnly);
    bool LoadSource();
    bool LoadSource();
-   bool GetMD5(bool const &GenOnly);
-   bool GetSHA1(bool const &GenOnly);
-   bool GetSHA256(bool const &GenOnly);
-   bool GetSHA512(bool const &GenOnly);
-   
+   bool GetHashes(bool const GenOnly, unsigned int const DoHashes);
+
    // Stat info stored in the DB, Fixed types since it is written to disk.
    // Stat info stored in the DB, Fixed types since it is written to disk.
    enum FlagList {FlControl = (1<<0),FlMD5=(1<<1),FlContents=(1<<2),
    enum FlagList {FlControl = (1<<0),FlMD5=(1<<1),FlContents=(1<<2),
                   FlSize=(1<<3), FlSHA1=(1<<4), FlSHA256=(1<<5), 
                   FlSize=(1<<3), FlSHA1=(1<<4), FlSHA256=(1<<5), 
@@ -124,12 +122,8 @@ class CacheDB
    debDebFile::MemControlExtract Control;
    debDebFile::MemControlExtract Control;
    ContentsExtract Contents;
    ContentsExtract Contents;
    DscExtract Dsc;
    DscExtract Dsc;
+   HashStringList HashesList;
 
 
-   std::string MD5Res;
-   std::string SHA1Res;
-   std::string SHA256Res;
-   std::string SHA512Res;
-   
    // Runtime statistics
    // Runtime statistics
    struct Stats
    struct Stats
    {
    {
@@ -165,16 +159,13 @@ class CacheDB
    bool SetFile(std::string const &FileName,struct stat St,FileFd *Fd);
    bool SetFile(std::string const &FileName,struct stat St,FileFd *Fd);
 
 
    // terrible old overloaded interface
    // terrible old overloaded interface
-   bool GetFileInfo(std::string const &FileName, 
-                    bool const &DoControl, 
-                    bool const &DoContents, 
-                    bool const &GenContentsOnly, 
-                    bool const &DoSource,
-		    bool const &DoMD5, 
-                    bool const &DoSHA1, 
-                    bool const &DoSHA256, 
-                    bool const &DoSHA512, 
-                    bool const &checkMtime = false);
+   bool GetFileInfo(std::string const &FileName,
+	 bool const &DoControl,
+	 bool const &DoContents,
+	 bool const &GenContentsOnly,
+	 bool const DoSource,
+	 unsigned int const DoHashes,
+	 bool const &checkMtime = false);
 
 
    bool Finish();   
    bool Finish();   
    
    

+ 154 - 170
ftparchive/writer.cc

@@ -54,29 +54,42 @@ FTWScanner *FTWScanner::Owner;
 // SetTFRewriteData - Helper for setting rewrite lists			/*{{{*/
 // SetTFRewriteData - Helper for setting rewrite lists			/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-inline void SetTFRewriteData(struct TFRewriteData &tfrd,
-			     const char *tag,
+static inline TFRewriteData SetTFRewriteData(const char *tag,
 			     const char *rewrite,
 			     const char *rewrite,
 			     const char *newtag = 0)
 			     const char *newtag = 0)
 {
 {
-    tfrd.Tag = tag;
-    tfrd.Rewrite = rewrite;
-    tfrd.NewTag = newtag;
+   TFRewriteData tfrd;
+   tfrd.Tag = tag;
+   tfrd.Rewrite = rewrite;
+   tfrd.NewTag = newtag;
+   return tfrd;
+}
+									/*}}}*/
+// ConfigToDoHashes - which hashes to generate				/*{{{*/
+static void SingleConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf, unsigned int const Flag)
+{
+   if (_config->FindB(Conf, true) == true)
+      DoHashes |= Flag;
+   else
+      DoHashes &= ~Flag;
+}
+static void ConfigToDoHashes(unsigned int &DoHashes, std::string const &Conf)
+{
+   SingleConfigToDoHashes(DoHashes, Conf + "::MD5", Hashes::MD5SUM);
+   SingleConfigToDoHashes(DoHashes, Conf + "::SHA1", Hashes::SHA1SUM);
+   SingleConfigToDoHashes(DoHashes, Conf + "::SHA256", Hashes::SHA256SUM);
+   SingleConfigToDoHashes(DoHashes, Conf + "::SHA512", Hashes::SHA512SUM);
 }
 }
 									/*}}}*/
 									/*}}}*/
 
 
 // FTWScanner::FTWScanner - Constructor					/*{{{*/
 // FTWScanner::FTWScanner - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-FTWScanner::FTWScanner(string const &Arch): Arch(Arch)
+FTWScanner::FTWScanner(string const &Arch): Arch(Arch), DoHashes(~0)
 {
 {
    ErrorPrinted = false;
    ErrorPrinted = false;
    NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
    NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
-
-   DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
-   DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
-   DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
-   DoSHA512 = _config->FindB("APT::FTPArchive::SHA512",true);
+   ConfigToDoHashes(DoHashes, "APT::FTPArchive");
 }
 }
 									/*}}}*/
 									/*}}}*/
 // FTWScanner::Scanner - FTW Scanner					/*{{{*/
 // FTWScanner::Scanner - FTW Scanner					/*{{{*/
@@ -327,10 +340,7 @@ PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string c
    DeLinkLimit = 0;
    DeLinkLimit = 0;
 
 
    // Process the command line options
    // Process the command line options
-   DoMD5 = _config->FindB("APT::FTPArchive::Packages::MD5",DoMD5);
-   DoSHA1 = _config->FindB("APT::FTPArchive::Packages::SHA1",DoSHA1);
-   DoSHA256 = _config->FindB("APT::FTPArchive::Packages::SHA256",DoSHA256);
-   DoSHA512 = _config->FindB("APT::FTPArchive::Packages::SHA512",DoSHA512);
+   ConfigToDoHashes(DoHashes, "APT::FTPArchive::Packages");
    DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
    DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
    DoContents = _config->FindB("APT::FTPArchive::Contents",true);
    DoContents = _config->FindB("APT::FTPArchive::Contents",true);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
@@ -385,12 +395,12 @@ bool FTWScanner::SetExts(string const &Vals)
 bool PackagesWriter::DoPackage(string FileName)
 bool PackagesWriter::DoPackage(string FileName)
 {      
 {      
    // Pull all the data we need form the DB
    // Pull all the data we need form the DB
-   if (Db.GetFileInfo(FileName, 
-                      true,   /* DoControl */
-                      DoContents, 
-                      true,   /* GenContentsOnly */
-                      false,  /* DoSource */
-                      DoMD5, DoSHA1, DoSHA256, DoSHA512, DoAlwaysStat) == false)
+   if (Db.GetFileInfo(FileName,
+	    true, /* DoControl */
+	    DoContents,
+	    true, /* GenContentsOnly */
+	    false, /* DoSource */
+	    DoHashes, DoAlwaysStat) == false)
    {
    {
      return false;
      return false;
    }
    }
@@ -454,30 +464,27 @@ bool PackagesWriter::DoPackage(string FileName)
    }
    }
 
 
    // This lists all the changes to the fields we are going to make.
    // This lists all the changes to the fields we are going to make.
-   // (7 hardcoded + maintainer + suggests + end marker)
-   TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
-
-   unsigned int End = 0;
-   SetTFRewriteData(Changes[End++], "Size", Size);
-   if (DoMD5 == true)
-      SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
-   if (DoSHA1 == true)
-      SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
-   if (DoSHA256 == true)
-      SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
-   if (DoSHA512 == true)
-      SetTFRewriteData(Changes[End++], "SHA512", Db.SHA512Res.c_str());
-   SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
-   SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
-   SetTFRewriteData(Changes[End++], "Status", 0);
-   SetTFRewriteData(Changes[End++], "Optional", 0);
+   std::vector<TFRewriteData> Changes;
+
+   Changes.push_back(SetTFRewriteData("Size", Size));
+   for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
+   {
+      if (hs->HashType() == "MD5Sum")
+	 Changes.push_back(SetTFRewriteData("MD5sum", hs->HashValue().c_str()));
+      else
+	 Changes.push_back(SetTFRewriteData(hs->HashType().c_str(), hs->HashValue().c_str()));
+   }
+   Changes.push_back(SetTFRewriteData("Filename", NewFileName.c_str()));
+   Changes.push_back(SetTFRewriteData("Priority", OverItem->Priority.c_str()));
+   Changes.push_back(SetTFRewriteData("Status", 0));
+   Changes.push_back(SetTFRewriteData("Optional", 0));
 
 
    string DescriptionMd5;
    string DescriptionMd5;
    if (LongDescription == false) {
    if (LongDescription == false) {
       MD5Summation descmd5;
       MD5Summation descmd5;
       descmd5.Add(desc.c_str());
       descmd5.Add(desc.c_str());
       DescriptionMd5 = descmd5.Result().Value();
       DescriptionMd5 = descmd5.Result().Value();
-      SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
+      Changes.push_back(SetTFRewriteData("Description-md5", DescriptionMd5.c_str()));
       if (TransWriter != NULL)
       if (TransWriter != NULL)
 	 TransWriter->DoPackage(Package, desc, DescriptionMd5);
 	 TransWriter->DoPackage(Package, desc, DescriptionMd5);
    }
    }
@@ -492,12 +499,12 @@ bool PackagesWriter::DoPackage(string FileName)
 	 NewLine(1);
 	 NewLine(1);
 	 ioprintf(c1out, _("  %s maintainer is %s not %s\n"),
 	 ioprintf(c1out, _("  %s maintainer is %s not %s\n"),
 	       Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
 	       Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
-      }      
+      }
    }
    }
-   
+
    if (NewMaint.empty() == false)
    if (NewMaint.empty() == false)
-      SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
-   
+      Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
+
    /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
    /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
       dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
       dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
       but dpkg does this append bit. So we do the append bit, at least that way the
       but dpkg does this append bit. So we do the append bit, at least that way the
@@ -508,17 +515,17 @@ bool PackagesWriter::DoPackage(string FileName)
    {
    {
       if (Tags.FindS("Suggests").empty() == false)
       if (Tags.FindS("Suggests").empty() == false)
 	 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
 	 OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
-      SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
+      Changes.push_back(SetTFRewriteData("Suggests", OptionalStr.c_str()));
    }
    }
 
 
-   for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin(); 
+   for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin();
         I != OverItem->FieldOverride.end(); ++I)
         I != OverItem->FieldOverride.end(); ++I)
-      SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
+      Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
 
 
-   SetTFRewriteData(Changes[End++], 0, 0);
+   Changes.push_back(SetTFRewriteData( 0, 0));
 
 
    // Rewrite and store the fields.
    // Rewrite and store the fields.
-   if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
+   if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes.data()) == false)
       return false;
       return false;
    fprintf(Output,"\n");
    fprintf(Output,"\n");
 
 
@@ -589,10 +596,7 @@ SourcesWriter::SourcesWriter(string const &DB, string const &BOverrides,string c
    BufSize = 0;
    BufSize = 0;
    
    
    // Process the command line options
    // Process the command line options
-   DoMD5 = _config->FindB("APT::FTPArchive::Sources::MD5",DoMD5);
-   DoSHA1 = _config->FindB("APT::FTPArchive::Sources::SHA1",DoSHA1);
-   DoSHA256 = _config->FindB("APT::FTPArchive::Sources::SHA256",DoSHA256);
-   DoSHA512 = _config->FindB("APT::FTPArchive::Sources::SHA512",DoSHA512);
+   ConfigToDoHashes(DoHashes, "APT::FTPArchive::Sources");
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
    DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
    DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
 
 
@@ -614,17 +618,25 @@ SourcesWriter::SourcesWriter(string const &DB, string const &BOverrides,string c
 }
 }
 									/*}}}*/
 									/*}}}*/
 // SourcesWriter::DoPackage - Process a single package			/*{{{*/
 // SourcesWriter::DoPackage - Process a single package			/*{{{*/
-// ---------------------------------------------------------------------
-/* */
+static std::ostream& addDscHash(std::ostream &out, unsigned int const DoHashes,
+      Hashes::SupportedHashes const DoIt, pkgTagSection &Tags, char const * const FieldName,
+      HashString const * const Hash, unsigned long long Size, std::string FileName)
+{
+   if ((DoHashes & DoIt) != DoIt || Tags.Exists(FieldName) == false || Hash == NULL)
+      return out;
+   out << "\n " << Hash->HashValue() << " " << Size << " " << FileName
+      << "\n " << Tags.FindS(FieldName);
+   return out;
+}
 bool SourcesWriter::DoPackage(string FileName)
 bool SourcesWriter::DoPackage(string FileName)
 {
 {
    // Pull all the data we need form the DB
    // Pull all the data we need form the DB
    if (Db.GetFileInfo(FileName,
    if (Db.GetFileInfo(FileName,
-                      false,  /* DoControl */
-                      false,  /* DoContents */
-                      false,  /* GenContentsOnly */
-                      true,   /* DoSource */
-                      DoMD5, DoSHA1, DoSHA256, DoSHA512, DoAlwaysStat) == false)
+	    false, /* DoControl */
+	    false, /* DoContents */
+	    false, /* GenContentsOnly */
+	    true, /* DoSource */
+	    DoHashes, DoAlwaysStat) == false)
    {
    {
       return false;
       return false;
    }
    }
@@ -712,29 +724,19 @@ bool SourcesWriter::DoPackage(string FileName)
 	 *SOverItem = *OverItem;
 	 *SOverItem = *OverItem;
       }
       }
    }
    }
-   
+
    // Add the dsc to the files hash list
    // Add the dsc to the files hash list
    string const strippedName = flNotDir(FileName);
    string const strippedName = flNotDir(FileName);
    std::ostringstream ostreamFiles;
    std::ostringstream ostreamFiles;
-   if (DoMD5 == true && Tags.Exists("Files"))
-      ostreamFiles << "\n " << Db.MD5Res.c_str() << " " << St.st_size << " "
-		   << strippedName << "\n " << Tags.FindS("Files");
+   addDscHash(ostreamFiles, DoHashes, Hashes::MD5SUM, Tags, "Files", Db.HashesList.find("MD5Sum"), St.st_size, strippedName);
    string const Files = ostreamFiles.str();
    string const Files = ostreamFiles.str();
 
 
    std::ostringstream ostreamSha1;
    std::ostringstream ostreamSha1;
-   if (DoSHA1 == true && Tags.Exists("Checksums-Sha1"))
-      ostreamSha1 << "\n " << string(Db.SHA1Res.c_str()) << " " << St.st_size << " "
-		   << strippedName << "\n " << Tags.FindS("Checksums-Sha1");
-
+   addDscHash(ostreamSha1, DoHashes, Hashes::SHA1SUM, Tags, "Checksums-Sha1", Db.HashesList.find("SHA1"), St.st_size, strippedName);
    std::ostringstream ostreamSha256;
    std::ostringstream ostreamSha256;
-   if (DoSHA256 == true && Tags.Exists("Checksums-Sha256"))
-      ostreamSha256 << "\n " << string(Db.SHA256Res.c_str()) << " " << St.st_size << " "
-		   << strippedName << "\n " << Tags.FindS("Checksums-Sha256");
-
+   addDscHash(ostreamSha256, DoHashes, Hashes::SHA256SUM, Tags, "Checksums-Sha256", Db.HashesList.find("SHA256"), St.st_size, strippedName);
    std::ostringstream ostreamSha512;
    std::ostringstream ostreamSha512;
-   if (DoSHA512 == true && Tags.Exists("Checksums-Sha512"))
-      ostreamSha512 << "\n " << string(Db.SHA512Res.c_str()) << " " << St.st_size << " "
-		   << strippedName << "\n " << Tags.FindS("Checksums-Sha512");
+   addDscHash(ostreamSha512, DoHashes, Hashes::SHA512SUM, Tags, "Checksums-Sha512", Db.HashesList.find("SHA512"), St.st_size, strippedName);
 
 
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
    string NewFileName;
@@ -766,35 +768,54 @@ bool SourcesWriter::DoPackage(string FileName)
       string OriginalPath = Directory + ParseJnk;
       string OriginalPath = Directory + ParseJnk;
 
 
       // Add missing hashes to source files
       // Add missing hashes to source files
-      if ((DoSHA1 == true && !Tags.Exists("Checksums-Sha1")) ||
-          (DoSHA256 == true && !Tags.Exists("Checksums-Sha256")) ||
-          (DoSHA512 == true && !Tags.Exists("Checksums-Sha512")))
+      if (((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM && !Tags.Exists("Checksums-Sha1")) ||
+          ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM && !Tags.Exists("Checksums-Sha256")) ||
+          ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM && !Tags.Exists("Checksums-Sha512")))
       {
       {
-         if (Db.GetFileInfo(OriginalPath, 
+         if (Db.GetFileInfo(OriginalPath,
                             false, /* DoControl */
                             false, /* DoControl */
                             false, /* DoContents */
                             false, /* DoContents */
                             false, /* GenContentsOnly */
                             false, /* GenContentsOnly */
                             false, /* DoSource */
                             false, /* DoSource */
-                            DoMD5, DoSHA1, DoSHA256, DoSHA512,
+                            DoHashes,
                             DoAlwaysStat) == false)
                             DoAlwaysStat) == false)
          {
          {
             return _error->Error("Error getting file info");
             return _error->Error("Error getting file info");
          }
          }
 
 
-         if (DoSHA1 == true && !Tags.Exists("Checksums-Sha1"))
-            ostreamSha1 << "\n " << string(Db.SHA1Res) << " "
-               << Db.GetFileSize() << " " << ParseJnk;
-
-         if (DoSHA256 == true && !Tags.Exists("Checksums-Sha256"))
-            ostreamSha256 << "\n " << string(Db.SHA256Res) << " "
-               << Db.GetFileSize() << " " << ParseJnk;
-
-         if (DoSHA512 == true && !Tags.Exists("Checksums-Sha512"))
-            ostreamSha512 << "\n " << string(Db.SHA512Res) << " "
-               << Db.GetFileSize() << " " << ParseJnk;
+         for (HashStringList::const_iterator hs = Db.HashesList.begin(); hs != Db.HashesList.end(); ++hs)
+	 {
+	    if (hs->HashType() == "MD5Sum")
+	       continue;
+	    char const * fieldname;
+	    std::ostream * out;
+	    if (hs->HashType() == "SHA1")
+	    {
+	       fieldname = "Checksums-Sha1";
+	       out = &ostreamSha1;
+	    }
+	    else if (hs->HashType() == "SHA256")
+	    {
+	       fieldname = "Checksums-Sha256";
+	       out = &ostreamSha256;
+	    }
+	    else if (hs->HashType() == "SHA512")
+	    {
+	       fieldname = "Checksums-Sha512";
+	       out = &ostreamSha512;
+	    }
+	    else
+	    {
+	       _error->Warning("Ignoring unknown Checksumtype %s in SourcesWriter::DoPackages", hs->HashType().c_str());
+	       continue;
+	    }
+	    if (Tags.Exists(fieldname) == true)
+	       continue;
+	    (*out) << "\n " << hs->HashValue() << " " << Db.GetFileSize() << " " << ParseJnk;
+	 }
 
 
-         // write back the GetFileInfo() stats data 
-         Db.Finish();
+	 // write back the GetFileInfo() stats data 
+	 Db.Finish();
       }
       }
 
 
       // Perform the delinking operation
       // Perform the delinking operation
@@ -820,22 +841,21 @@ bool SourcesWriter::DoPackage(string FileName)
 
 
    // This lists all the changes to the fields we are going to make.
    // This lists all the changes to the fields we are going to make.
    // (5 hardcoded + checksums + maintainer + end marker)
    // (5 hardcoded + checksums + maintainer + end marker)
-   TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
+   std::vector<TFRewriteData> Changes;
 
 
-   unsigned int End = 0;
-   SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
+   Changes.push_back(SetTFRewriteData("Source",Package.c_str(),"Package"));
    if (Files.empty() == false)
    if (Files.empty() == false)
-      SetTFRewriteData(Changes[End++],"Files",Files.c_str());
+      Changes.push_back(SetTFRewriteData("Files",Files.c_str()));
    if (ChecksumsSha1.empty() == false)
    if (ChecksumsSha1.empty() == false)
-      SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1.c_str());
+      Changes.push_back(SetTFRewriteData("Checksums-Sha1",ChecksumsSha1.c_str()));
    if (ChecksumsSha256.empty() == false)
    if (ChecksumsSha256.empty() == false)
-      SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256.c_str());
+      Changes.push_back(SetTFRewriteData("Checksums-Sha256",ChecksumsSha256.c_str()));
    if (ChecksumsSha512.empty() == false)
    if (ChecksumsSha512.empty() == false)
-      SetTFRewriteData(Changes[End++],"Checksums-Sha512",ChecksumsSha512.c_str());
+      Changes.push_back(SetTFRewriteData("Checksums-Sha512",ChecksumsSha512.c_str()));
    if (Directory != "./")
    if (Directory != "./")
-      SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
-   SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
-   SetTFRewriteData(Changes[End++],"Status",0);
+      Changes.push_back(SetTFRewriteData("Directory",Directory.c_str()));
+   Changes.push_back(SetTFRewriteData("Priority",BestPrio.c_str()));
+   Changes.push_back(SetTFRewriteData("Status",0));
 
 
    // Rewrite the maintainer field if necessary
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
    bool MaintFailed;
@@ -850,16 +870,16 @@ bool SourcesWriter::DoPackage(string FileName)
       }      
       }      
    }
    }
    if (NewMaint.empty() == false)
    if (NewMaint.empty() == false)
-      SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
+      Changes.push_back(SetTFRewriteData("Maintainer", NewMaint.c_str()));
    
    
    for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin(); 
    for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin(); 
         I != SOverItem->FieldOverride.end(); ++I)
         I != SOverItem->FieldOverride.end(); ++I)
-      SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
+      Changes.push_back(SetTFRewriteData(I->first.c_str(),I->second.c_str()));
 
 
-   SetTFRewriteData(Changes[End++], 0, 0);
+   Changes.push_back(SetTFRewriteData(0, 0));
       
       
    // Rewrite and store the fields.
    // Rewrite and store the fields.
-   if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
+   if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes.data()) == false)
       return false;
       return false;
    fprintf(Output,"\n");
    fprintf(Output,"\n");
 
 
@@ -886,15 +906,13 @@ ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
    determine what the package name is. */
    determine what the package name is. */
 bool ContentsWriter::DoPackage(string FileName, string Package)
 bool ContentsWriter::DoPackage(string FileName, string Package)
 {
 {
-   if (!Db.GetFileInfo(FileName, 
-                       Package.empty(), /* DoControl */
-                       true,            /* DoContents */
-                       false,           /* GenContentsOnly */
-                       false,           /* DoSource */
-                       false,           /* DoMD5 */
-                       false,           /* DoSHA1 */
-                       false,           /* DoSHA256 */
-                       false))          /* DoSHA512 */
+   if (!Db.GetFileInfo(FileName,
+	    Package.empty(), /* DoControl */
+	    true, /* DoContents */
+	    false, /* GenContentsOnly */
+	    false, /* DoSource */
+	    0, /* DoHashes */
+	    false /* checkMtime */))
    {
    {
       return false;
       return false;
    }
    }
@@ -1030,9 +1048,7 @@ ReleaseWriter::ReleaseWriter(string const &/*DB*/)
       fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
       fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
    }
    }
 
 
-   DoMD5 = _config->FindB("APT::FTPArchive::Release::MD5",DoMD5);
-   DoSHA1 = _config->FindB("APT::FTPArchive::Release::SHA1",DoSHA1);
-   DoSHA256 = _config->FindB("APT::FTPArchive::Release::SHA256",DoSHA256);
+   ConfigToDoHashes(DoHashes, "APT::FTPArchive::Release");
 }
 }
 									/*}}}*/
 									/*}}}*/
 // ReleaseWriter::DoPackage - Process a single package			/*{{{*/
 // ReleaseWriter::DoPackage - Process a single package			/*{{{*/
@@ -1066,15 +1082,8 @@ bool ReleaseWriter::DoPackage(string FileName)
    CheckSums[NewFileName].size = fd.Size();
    CheckSums[NewFileName].size = fd.Size();
 
 
    Hashes hs;
    Hashes hs;
-   hs.AddFD(fd, 0, DoMD5, DoSHA1, DoSHA256, DoSHA512);
-   if (DoMD5 == true)
-      CheckSums[NewFileName].MD5 = hs.MD5.Result();
-   if (DoSHA1 == true)
-      CheckSums[NewFileName].SHA1 = hs.SHA1.Result();
-   if (DoSHA256 == true)
-      CheckSums[NewFileName].SHA256 = hs.SHA256.Result();
-   if (DoSHA512 == true)
-      CheckSums[NewFileName].SHA512 = hs.SHA512.Result();
+   hs.AddFD(fd, 0, DoHashes);
+   CheckSums[NewFileName].Hashes = hs.GetHashStringList();
    fd.Close();
    fd.Close();
 
 
    return true;
    return true;
@@ -1083,54 +1092,29 @@ bool ReleaseWriter::DoPackage(string FileName)
 									/*}}}*/
 									/*}}}*/
 // ReleaseWriter::Finish - Output the checksums				/*{{{*/
 // ReleaseWriter::Finish - Output the checksums				/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
-void ReleaseWriter::Finish()
+static void printChecksumTypeRecord(FILE * const Output, char const * const Type, map<string, ReleaseWriter::CheckSum> const &CheckSums)
 {
 {
-   if (DoMD5 == true)
-   {
-      fprintf(Output, "MD5Sum:\n");
-      for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
-	  I != CheckSums.end(); ++I)
-      {
-	 fprintf(Output, " %s %16llu %s\n",
-		 (*I).second.MD5.c_str(),
-		 (*I).second.size,
-		 (*I).first.c_str());
-      }
-   }
-   if (DoSHA1 == true)
-   {
-      fprintf(Output, "SHA1:\n");
-      for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
-	  I != CheckSums.end(); ++I)
-      {
-	 fprintf(Output, " %s %16llu %s\n",
-		 (*I).second.SHA1.c_str(),
-		 (*I).second.size,
-		 (*I).first.c_str());
-      }
-   }
-   if (DoSHA256 == true)
-   {
-      fprintf(Output, "SHA256:\n");
-      for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
+      fprintf(Output, "%s:\n", Type);
+      for(map<string,ReleaseWriter::CheckSum>::const_iterator I = CheckSums.begin();
 	  I != CheckSums.end(); ++I)
 	  I != CheckSums.end(); ++I)
       {
       {
+	 HashString const * const hs = I->second.Hashes.find(Type);
+	 if (hs == NULL)
+	    continue;
 	 fprintf(Output, " %s %16llu %s\n",
 	 fprintf(Output, " %s %16llu %s\n",
-		 (*I).second.SHA256.c_str(),
+		 hs->HashValue().c_str(),
 		 (*I).second.size,
 		 (*I).second.size,
 		 (*I).first.c_str());
 		 (*I).first.c_str());
       }
       }
-   }
-
-   fprintf(Output, "SHA512:\n");
-   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
-       I != CheckSums.end();
-       ++I)
-   {
-      fprintf(Output, " %s %16llu %s\n",
-              (*I).second.SHA512.c_str(),
-              (*I).second.size,
-              (*I).first.c_str());
-   }
-
+}
+void ReleaseWriter::Finish()
+{
+   if ((DoHashes & Hashes::MD5SUM) == Hashes::MD5SUM)
+      printChecksumTypeRecord(Output, "MD5Sum", CheckSums);
+   if ((DoHashes & Hashes::SHA1SUM) == Hashes::SHA1SUM)
+      printChecksumTypeRecord(Output, "SHA1", CheckSums);
+   if ((DoHashes & Hashes::SHA256SUM) == Hashes::SHA256SUM)
+      printChecksumTypeRecord(Output, "SHA256", CheckSums);
+   if ((DoHashes & Hashes::SHA512SUM) == Hashes::SHA512SUM)
+      printChecksumTypeRecord(Output, "SHA512", CheckSums);
 }
 }

+ 5 - 9
ftparchive/writer.h

@@ -13,6 +13,8 @@
 #ifndef WRITER_H
 #ifndef WRITER_H
 #define WRITER_H
 #define WRITER_H
 
 
+#include <apt-pkg/hashes.h>
+
 #include <string>
 #include <string>
 #include <stdio.h>
 #include <stdio.h>
 #include <iostream>
 #include <iostream>
@@ -61,10 +63,7 @@ class FTWScanner
    }
    }
    
    
    public:
    public:
-   bool DoMD5;
-   bool DoSHA1;
-   bool DoSHA256;
-   bool DoSHA512;
+   unsigned int DoHashes;
 
 
    unsigned long DeLinkLimit;
    unsigned long DeLinkLimit;
    string InternalPrefix;
    string InternalPrefix;
@@ -195,17 +194,14 @@ public:
    string PathPrefix;
    string PathPrefix;
    string DirStrip;
    string DirStrip;
 
 
-protected:
    struct CheckSum
    struct CheckSum
    {
    {
-      string MD5;
-      string SHA1;
-      string SHA256;
-      string SHA512;
+      HashStringList Hashes;
       // Limited by FileFd::Size()
       // Limited by FileFd::Size()
       unsigned long long size;
       unsigned long long size;
       ~CheckSum() {};
       ~CheckSum() {};
    };
    };
+protected:
    map<string,struct CheckSum> CheckSums;
    map<string,struct CheckSum> CheckSums;
 };
 };
 
 

+ 42 - 4
methods/server.cc

@@ -392,9 +392,16 @@ bool ServerMethod::Fetch(FetchItem *)
    for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; 
    for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; 
 	I = I->Next, Depth++)
 	I = I->Next, Depth++)
    {
    {
-      // If pipelining is disabled, we only queue 1 request
-      if (Server->Pipeline == false && Depth >= 0)
-	 break;
+      if (Depth >= 0)
+      {
+	 // If pipelining is disabled, we only queue 1 request
+	 if (Server->Pipeline == false)
+	    break;
+	 // if we have no hashes, do at most one such request
+	 // as we can't fixup pipeling misbehaviors otherwise
+	 else if (I->ExpectedHashes.usable() == false)
+	    break;
+      }
       
       
       // Make sure we stick with the same server
       // Make sure we stick with the same server
       if (Server->Comp(I->Uri) == false)
       if (Server->Comp(I->Uri) == false)
@@ -546,7 +553,38 @@ int ServerMethod::Loop()
 	    // Send status to APT
 	    // Send status to APT
 	    if (Result == true)
 	    if (Result == true)
 	    {
 	    {
-	       Res.TakeHashes(*Server->GetHashes());
+	       Hashes * const resultHashes = Server->GetHashes();
+	       HashStringList const hashList = resultHashes->GetHashStringList();
+	       if (PipelineDepth != 0 && Queue->ExpectedHashes.usable() == true && Queue->ExpectedHashes != hashList)
+	       {
+		  // we did not get the expected hash… mhhh:
+		  // could it be that server/proxy messed up pipelining?
+		  FetchItem * BeforeI = Queue;
+		  for (FetchItem *I = Queue->Next; I != 0 && I != QueueBack; I = I->Next)
+		  {
+		     if (I->ExpectedHashes.usable() == true && I->ExpectedHashes == hashList)
+		     {
+			// yes, he did! Disable pipelining and rewrite queue
+			if (Server->Pipeline == true)
+			{
+			   // FIXME: fake a warning message as we have no proper way of communicating here
+			   std::string out;
+			   strprintf(out, _("Automatically disabled %s due to incorrect response from server/proxy. (man 5 apt.conf)"), "Acquire::http::PipelineDepth");
+			   std::cerr << "W: " << out << std::endl;
+			   Server->Pipeline = false;
+			   // we keep the PipelineDepth value so that the rest of the queue can be fixed up as well
+			}
+			Rename(Res.Filename, I->DestFile);
+			Res.Filename = I->DestFile;
+			BeforeI->Next = I->Next;
+			I->Next = Queue;
+			Queue = I;
+			break;
+		     }
+		     BeforeI = I;
+		  }
+	       }
+	       Res.TakeHashes(*resultHashes);
 	       URIDone(Res);
 	       URIDone(Res);
 	    }
 	    }
 	    else
 	    else

+ 1 - 1
methods/server.h

@@ -140,7 +140,7 @@ class ServerMethod : public pkgAcqMethod
    virtual ServerState * CreateServerState(URI uri) = 0;
    virtual ServerState * CreateServerState(URI uri) = 0;
    virtual void RotateDNS() = 0;
    virtual void RotateDNS() = 0;
 
 
-   ServerMethod(const char *Ver,unsigned long Flags = 0) : pkgAcqMethod(Ver, Flags), Server(NULL), File(NULL), PipelineDepth(0), AllowRedirect(false), Debug(false) {};
+   ServerMethod(const char *Ver,unsigned long Flags = 0) : pkgAcqMethod(Ver, Flags), Server(NULL), File(NULL), PipelineDepth(10), AllowRedirect(false), Debug(false) {};
    virtual ~ServerMethod() {};
    virtual ~ServerMethod() {};
 };
 };
 
 

+ 7 - 7
test/integration/framework

@@ -36,7 +36,7 @@ msgndebug() { echo -n "${CDEBUG}D: $1${CNORMAL}"; }
 msgtest() {
 msgtest() {
 	while [ -n "$1" ]; do
 	while [ -n "$1" ]; do
 		echo -n "${CINFO}$1${CCMD} "
 		echo -n "${CINFO}$1${CCMD} "
-		echo -n "$(echo "$2" | sed -e 's/^aptc/apt-c/' -e 's/^aptg/apt-g/' -e 's/^aptf/apt-f/')${CINFO} "
+		echo -n "$(echo "$2" | sed -e 's#^apt\([cgfs]\)#apt-\1#')${CINFO} "
 		shift
 		shift
 		if [ -n "$1" ]; then shift; else break; fi
 		if [ -n "$1" ]; then shift; else break; fi
 	done
 	done
@@ -102,10 +102,10 @@ runapt() {
 	local CMD="$1"
 	local CMD="$1"
 	shift
 	shift
 	case $CMD in
 	case $CMD in
-	sh|aptitude|*/*) ;;
+	sh|aptitude|*/*|command) ;;
 	*) CMD="${BUILDDIRECTORY}/$CMD";;
 	*) CMD="${BUILDDIRECTORY}/$CMD";;
 	esac
 	esac
-	MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY} $CMD "$@"
+	MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${LIBRARYPATH} $CMD "$@"
 }
 }
 aptconfig() { runapt apt-config "$@"; }
 aptconfig() { runapt apt-config "$@"; }
 aptcache() { runapt apt-cache "$@"; }
 aptcache() { runapt apt-cache "$@"; }
@@ -114,6 +114,7 @@ aptget() { runapt apt-get "$@"; }
 aptftparchive() { runapt apt-ftparchive "$@"; }
 aptftparchive() { runapt apt-ftparchive "$@"; }
 aptkey() { runapt apt-key "$@"; }
 aptkey() { runapt apt-key "$@"; }
 aptmark() { runapt apt-mark "$@"; }
 aptmark() { runapt apt-mark "$@"; }
+aptsortpkgs() { runapt apt-sortpkgs "$@"; }
 apt() { runapt apt "$@"; }
 apt() { runapt apt "$@"; }
 apthelper() { runapt "${APTHELPERBINDIR}/apt-helper" "$@"; }
 apthelper() { runapt "${APTHELPERBINDIR}/apt-helper" "$@"; }
 aptwebserver() { runapt "${APTWEBSERVERBINDIR}/aptwebserver" "$@"; }
 aptwebserver() { runapt "${APTWEBSERVERBINDIR}/aptwebserver" "$@"; }
@@ -127,11 +128,9 @@ dpkgcheckbuilddeps() {
 	command dpkg-checkbuilddeps --admindir=${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg "$@"
 	command dpkg-checkbuilddeps --admindir=${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg "$@"
 }
 }
 gdb() {
 gdb() {
-	echo "gdb: run »$*«"
-	CMD="$1"
+	local CMD="$1"
 	shift
 	shift
-
-	APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${LIBRARYPATH} command gdb ${BUILDDIRECTORY}/$CMD --args ${BUILDDIRECTORY}/$CMD "$@"
+	runapt command gdb --quiet -ex run "${BUILDDIRECTORY}/$CMD" --args "${BUILDDIRECTORY}/$CMD" "$@"
 }
 }
 gpg() {
 gpg() {
 	# see apt-key for the whole trickery. Setup is done in setupenvironment
 	# see apt-key for the whole trickery. Setup is done in setupenvironment
@@ -226,6 +225,7 @@ setupenvironment() {
 	fi
 	fi
 	echo "DPKG::options:: \"--log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log\";" >> aptconfig.conf
 	echo "DPKG::options:: \"--log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log\";" >> aptconfig.conf
 	echo 'quiet::NoUpdate "true";' >> aptconfig.conf
 	echo 'quiet::NoUpdate "true";' >> aptconfig.conf
+	echo 'quiet::NoStatistic "true";' >> aptconfig.conf
 	echo "Acquire::https::CaInfo \"${TESTDIR}/apt.pem\";" > rootdir/etc/apt/apt.conf.d/99https
 	echo "Acquire::https::CaInfo \"${TESTDIR}/apt.pem\";" > rootdir/etc/apt/apt.conf.d/99https
         echo "Apt::Cmd::Disable-Script-Warning \"1\";" > rootdir/etc/apt/apt.conf.d/apt-binary
         echo "Apt::Cmd::Disable-Script-Warning \"1\";" > rootdir/etc/apt/apt.conf.d/apt-binary
 	configcompression '.' 'gz' #'bz2' 'lzma' 'xz'
 	configcompression '.' 'gz' #'bz2' 'lzma' 'xz'

+ 2 - 2
test/integration/test-apt-ftparchive-cachedb

@@ -5,7 +5,7 @@ ensure_correct_packages_file() {
     testequal "Package: foo
     testequal "Package: foo
 Priority: optional
 Priority: optional
 Section: others
 Section: others
-Installed-Size: 29
+$(dpkg-deb -I ./aptarchive/pool/main/foo_1_i386.deb | grep 'Installed-Size:' | sed 's#^ ##')
 Maintainer: Joe Sixpack <joe@example.org>
 Maintainer: Joe Sixpack <joe@example.org>
 Architecture: i386
 Architecture: i386
 Version: 1
 Version: 1
@@ -25,7 +25,7 @@ usr/share/doc/foo/copyright				    others/foo" cat ./aptarchive/dists/test/Conte
 TESTDIR=$(readlink -f $(dirname $0))
 TESTDIR=$(readlink -f $(dirname $0))
 . $TESTDIR/framework
 . $TESTDIR/framework
 setupenvironment
 setupenvironment
-configarchitecture "i386"
+configarchitecture 'i386'
 
 
 mkdir -p aptarchive/dists/test/main/i18n/
 mkdir -p aptarchive/dists/test/main/i18n/
 mkdir -p aptarchive/dists/test/main/source/
 mkdir -p aptarchive/dists/test/main/source/

+ 10 - 5
test/integration/test-apt-ftparchive-src-cachedb

@@ -3,9 +3,9 @@ set -e
 
 
 assert_correct_sources_file() {
 assert_correct_sources_file() {
     testequal "Package: bar
     testequal "Package: bar
-Binary: bar
-Version: 1.0
 Architecture: all
 Architecture: all
+Version: 1.0
+Binary: bar
 Format: 3.0 (native)
 Format: 3.0 (native)
 Directory: pool/main
 Directory: pool/main
 Files:
 Files:
@@ -24,9 +24,9 @@ Checksums-Sha512:
  cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 bar_1.0.tar.gz
  cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 bar_1.0.tar.gz
 
 
 Package: foo
 Package: foo
-Binary: foo
-Version: 1.0
 Architecture: all
 Architecture: all
+Version: 1.0
+Binary: foo
 Format: 3.0 (native)
 Format: 3.0 (native)
 Directory: pool/main
 Directory: pool/main
 Files:
 Files:
@@ -43,7 +43,7 @@ Checksums-Sha256:
 Checksums-Sha512:
 Checksums-Sha512:
  3da0240fd764657c2f3661b4d750578a9a99b0580591b133756379d48117ebda87a5ed2467f513200d6e7eaf51422cbe91c15720eef7fb4bba2cc8ff81ebc547 171 foo_1.0.dsc
  3da0240fd764657c2f3661b4d750578a9a99b0580591b133756379d48117ebda87a5ed2467f513200d6e7eaf51422cbe91c15720eef7fb4bba2cc8ff81ebc547 171 foo_1.0.dsc
  cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 foo_1.0.tar.gz
  cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 foo_1.0.tar.gz
-" cat ./aptarchive/dists/test/main/source/Sources
+" aptsortpkgs ./aptarchive/dists/test/main/source/Sources
 }
 }
 
 
 create_source_files() {
 create_source_files() {
@@ -177,6 +177,11 @@ assert_correct_sources_file
 mkdir aptarchive/pool/invalid
 mkdir aptarchive/pool/invalid
 printf "meep" > aptarchive/pool/invalid/invalid_1.0.dsc
 printf "meep" > aptarchive/pool/invalid/invalid_1.0.dsc
 testequal "
 testequal "
+E: Could not find a record in the DSC 'aptarchive/pool/invalid/invalid_1.0.dsc'" aptftparchive sources aptarchive/pool/invalid
+rm -f aptarchive/pool/invalid/invalid_1.0.dsc
+
+printf "meep: yes" > aptarchive/pool/invalid/invalid_1.0.dsc
+testequal "
 E: Could not find a Source entry in the DSC 'aptarchive/pool/invalid/invalid_1.0.dsc'" aptftparchive sources aptarchive/pool/invalid
 E: Could not find a Source entry in the DSC 'aptarchive/pool/invalid/invalid_1.0.dsc'" aptftparchive sources aptarchive/pool/invalid
 rm -f aptarchive/pool/invalid/invalid_1.0.dsc
 rm -f aptarchive/pool/invalid/invalid_1.0.dsc
 
 

+ 24 - 24
test/integration/test-apt-sources-deb822

@@ -23,14 +23,14 @@ Description: summay
 
 
 msgtest 'Test sources.list' 'old style'
 msgtest 'Test sources.list' 'old style'
 echo "deb http://ftp.debian.org/debian stable main" > $SOURCES
 echo "deb http://ftp.debian.org/debian stable main" > $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 
 
 msgtest 'Test sources.list' 'simple deb822'
 msgtest 'Test sources.list' 'simple deb822'
 echo "$BASE"  > $SOURCES
 echo "$BASE"  > $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 
 
 
 
@@ -39,29 +39,29 @@ msgtest 'Test deb822 with' 'two entries'
 echo "$BASE" > $SOURCES
 echo "$BASE" > $SOURCES
 echo "" >> $SOURCES
 echo "" >> $SOURCES
 echo "$BASE" | sed  s/stable/unstable/  >> $SOURCES
 echo "$BASE" | sed  s/stable/unstable/  >> $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
-'http://ftp.debian.org/debian/dists/unstable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_unstable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 :
+'http://ftp.debian.org/debian/dists/unstable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_unstable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/unstable/InRelease' ftp.debian.org_debian_dists_unstable_InRelease 0 "  aptget update --print-uris
 'http://ftp.debian.org/debian/dists/unstable/InRelease' ftp.debian.org_debian_dists_unstable_InRelease 0 "  aptget update --print-uris
 
 
 # two suite entries
 # two suite entries
 msgtest 'Test deb822 with' 'two Suite entries'
 msgtest 'Test deb822 with' 'two Suite entries'
 echo "$BASE"  | sed -e "s/stable/stable unstable/" > $SOURCES
 echo "$BASE"  | sed -e "s/stable/stable unstable/" > $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 
-'http://ftp.debian.org/debian/dists/unstable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_unstable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 :
+'http://ftp.debian.org/debian/dists/unstable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_unstable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/unstable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_unstable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/unstable/InRelease' ftp.debian.org_debian_dists_unstable_InRelease 0 "  aptget update --print-uris 
 'http://ftp.debian.org/debian/dists/unstable/InRelease' ftp.debian.org_debian_dists_unstable_InRelease 0 "  aptget update --print-uris 
 
 
 msgtest 'Test deb822' 'architecture option'
 msgtest 'Test deb822' 'architecture option'
 echo "$BASE" > $SOURCES
 echo "$BASE" > $SOURCES
 echo "Architectures: amd64 armel" >> $SOURCES
 echo "Architectures: amd64 armel" >> $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-amd64/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-amd64_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/binary-armel/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-armel_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/binary-amd64/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-amd64_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-armel/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-armel_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris
 
 
 
 
@@ -85,19 +85,19 @@ testempty aptget update --print-uris
 # multiple URIs
 # multiple URIs
 msgtest 'Test deb822 sources.list file which has' 'Multiple URIs work'
 msgtest 'Test deb822 sources.list file which has' 'Multiple URIs work'
 echo "$BASE"  | sed -e 's#http://ftp.debian.org/debian#http://ftp.debian.org/debian http://ftp.de.debian.org/debian#' > $SOURCES
 echo "$BASE"  | sed -e 's#http://ftp.debian.org/debian#http://ftp.debian.org/debian http://ftp.de.debian.org/debian#' > $SOURCES
-testequal --nomsg  "'http://ftp.de.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.de.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.de.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.de.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg  "'http://ftp.de.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.de.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.de.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.de.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.de.debian.org/debian/dists/stable/InRelease' ftp.de.debian.org_debian_dists_stable_InRelease 0 
 'http://ftp.de.debian.org/debian/dists/stable/InRelease' ftp.de.debian.org_debian_dists_stable_InRelease 0 
-'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris 
 
 
 # multiple Type in one field
 # multiple Type in one field
 msgtest 'Test deb822 sources.list file which has' 'Multiple Types work'
 msgtest 'Test deb822 sources.list file which has' 'Multiple Types work'
 echo "$BASE"  | sed -e 's#Types: deb#Types: deb deb-src#' > $SOURCES
 echo "$BASE"  | sed -e 's#Types: deb#Types: deb deb-src#' > $SOURCES
-testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/source/Sources.bz2' ftp.debian.org_debian_dists_stable_main_source_Sources 0 :
-'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 :
-'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 :
+testequal --nomsg "'http://ftp.debian.org/debian/dists/stable/main/source/Sources.bz2' ftp.debian.org_debian_dists_stable_main_source_Sources 0 
+'http://ftp.debian.org/debian/dists/stable/main/binary-i386/Packages.bz2' ftp.debian.org_debian_dists_stable_main_binary-i386_Packages 0 
+'http://ftp.debian.org/debian/dists/stable/main/i18n/Translation-en.bz2' ftp.debian.org_debian_dists_stable_main_i18n_Translation-en 0 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris 
 'http://ftp.debian.org/debian/dists/stable/InRelease' ftp.debian.org_debian_dists_stable_InRelease 0 "  aptget update --print-uris 
 
 
 # a Suite
 # a Suite
@@ -107,6 +107,6 @@ Types: deb
 URIs: http://emacs.naquadah.org
 URIs: http://emacs.naquadah.org
 Suites: stable/
 Suites: stable/
 EOF
 EOF
-testequal --nomsg "'http://emacs.naquadah.org/stable/Packages.bz2' emacs.naquadah.org_stable_Packages 0 :
-'http://emacs.naquadah.org/stable/en.bz2' emacs.naquadah.org_stable_en 0 :
+testequal --nomsg "'http://emacs.naquadah.org/stable/Packages.bz2' emacs.naquadah.org_stable_Packages 0 
+'http://emacs.naquadah.org/stable/en.bz2' emacs.naquadah.org_stable_en 0 
 'http://emacs.naquadah.org/stable/InRelease' emacs.naquadah.org_stable_InRelease 0 "  aptget update --print-uris 
 'http://emacs.naquadah.org/stable/InRelease' emacs.naquadah.org_stable_InRelease 0 "  aptget update --print-uris 

+ 1 - 1
test/integration/test-bug-595691-empty-and-broken-archive-files

@@ -13,7 +13,7 @@ setupflataptarchive
 testaptgetupdate() {
 testaptgetupdate() {
 	rm -rf rootdir/var/lib/apt
 	rm -rf rootdir/var/lib/apt
 	aptget update 2>> testaptgetupdate.diff >> testaptgetupdate.diff || true
 	aptget update 2>> testaptgetupdate.diff >> testaptgetupdate.diff || true
-	sed -i -e '/^Fetched / d' -e '/Ign / d' -e '/Release/ d' -e 's#Get:[0-9]\+ #Get: #' -e 's#\[[0-9]* [kMGTPY]*B\]#\[\]#' testaptgetupdate.diff
+	sed -i -e '/Ign / d' -e '/Release/ d' -e 's#Get:[0-9]\+ #Get: #' -e 's#\[[0-9]* [kMGTPY]*B\]#\[\]#' testaptgetupdate.diff
 	GIVEN="$1"
 	GIVEN="$1"
 	shift
 	shift
 	msgtest "Test for correctness of" "apt-get update with $*"
 	msgtest "Test for correctness of" "apt-get update with $*"

+ 3 - 6
test/integration/test-bug-712435-missing-descriptions

@@ -87,13 +87,10 @@ $DESCRIPTION
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 " aptcache show apt-normal
 " aptcache show apt-normal
 
 
-# displaying the translated Description would be equally valid,
-# but we assume only one description is in a Packages file and
-# so we prefer "Description" over "Description-*" currently.
 for variant in 'below' 'middle' 'top'; do
 for variant in 'below' 'middle' 'top'; do
 	testequal "Package: apt-both-$variant
 	testequal "Package: apt-both-$variant
 $PACKAGESTANZA
 $PACKAGESTANZA
-$DESCRIPTION
+$TRANSDESCRIPTION
 Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
 Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
 " aptcache show apt-both-$variant
 " aptcache show apt-both-$variant
 done
 done
@@ -122,7 +119,7 @@ X-Some-Flag: yes
 
 
 testequal "Package: apt-intermixed2
 testequal "Package: apt-intermixed2
 $PACKAGESTANZA
 $PACKAGESTANZA
-$DESCRIPTION
+$TRANSDESCRIPTION
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 X-Some-Flag: yes
 X-Some-Flag: yes
 X-Foo-Flag: Something with a Description
 X-Foo-Flag: Something with a Description
@@ -131,7 +128,7 @@ X-Bar-Flag: no
 
 
 testequal "Package: apt-intermixed3
 testequal "Package: apt-intermixed3
 $PACKAGESTANZA
 $PACKAGESTANZA
-$DESCRIPTION
+$TRANSDESCRIPTION
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 X-Some-Flag: yes
 X-Some-Flag: yes
 X-Foo-Flag: Something with a Description
 X-Foo-Flag: Something with a Description

+ 4 - 4
test/integration/test-bug-722207-print-uris-even-if-very-quiet

@@ -16,10 +16,10 @@ setupaptarchive
 
 
 APTARCHIVE=$(readlink -f ./aptarchive)
 APTARCHIVE=$(readlink -f ./aptarchive)
 
 
-testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 MD5Sum:" aptget upgrade -qq --print-uris
-testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 MD5Sum:" aptget dist-upgrade -qq --print-uris
-testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 MD5Sum:" aptget install apt -qq --print-uris
-testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 MD5Sum:" aptget download apt -qq --print-uris
+testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 " aptget upgrade -qq --print-uris
+testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 " aptget dist-upgrade -qq --print-uris
+testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 " aptget install apt -qq --print-uris
+testequal "'file://${APTARCHIVE}/pool/main/apt/apt_2_all.deb' apt_2_all.deb 0 " aptget download apt -qq --print-uris
 testequal "'file://${APTARCHIVE}/apt_2.dsc' apt_2.dsc 0 MD5Sum:d41d8cd98f00b204e9800998ecf8427e
 testequal "'file://${APTARCHIVE}/apt_2.dsc' apt_2.dsc 0 MD5Sum:d41d8cd98f00b204e9800998ecf8427e
 'file://${APTARCHIVE}/apt_2.tar.gz' apt_2.tar.gz 0 MD5Sum:d41d8cd98f00b204e9800998ecf8427e" aptget source apt -qq --print-uris
 'file://${APTARCHIVE}/apt_2.tar.gz' apt_2.tar.gz 0 MD5Sum:d41d8cd98f00b204e9800998ecf8427e" aptget source apt -qq --print-uris
 testequal "'http://packages.debian.org/changelogs/pool/main/apt/apt_2/changelog'" aptget changelog apt -qq --print-uris
 testequal "'http://packages.debian.org/changelogs/pool/main/apt/apt_2/changelog'" aptget changelog apt -qq --print-uris

+ 29 - 0
test/integration/test-bug-745036-new-foreign-invalidates-cache

@@ -0,0 +1,29 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+insertpackage 'unstable' 'cool-foo' 'amd64' '1.0' 'Depends: foo'
+insertpackage 'unstable' 'foo' 'amd64' '1.0' 'Multi-Arch: foreign'
+insertinstalledpackage 'cool-foo' 'amd64' '1.0' 'Depends: foo'
+insertinstalledpackage 'foo' 'amd64' '1.0' 'Multi-Arch: foreign'
+
+setupaptarchive
+
+testsuccess aptget check -s
+
+configarchitecture 'amd64' 'i386'
+testequal 'E: The package cache was built for different architectures: amd64 vs amd64,i386' aptget check -s -o pkgCacheFile::Generate=false
+
+testsuccess aptget check -s
+
+insertinstalledpackage 'awesome-foo' 'i386' '1.0' 'Depends: foo'
+
+testsuccess aptget check -s
+
+testsuccess aptget update --no-download
+
+testsuccess aptget check -s

+ 51 - 0
test/integration/test-bug-747261-arch-specific-conflicts

@@ -0,0 +1,51 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64' 'sparc' 'armel'
+
+msgtest 'Check that dpkg supports' 'arch-specific dependencies'
+set +e
+# this fails always, the question is just how it fails
+dpkg-checkbuilddeps -d 'foobar:barfoo' /dev/null 2>/dev/null >/dev/null
+RETURNCODE=$?
+set -e
+if [ "$RETURNCODE" != '1' ]; then
+	dpkg-checkbuilddeps -d 'foobar:barfoo' /dev/null || true
+	echo "Command had returncode: $RETURNCODE"
+	msgskip
+	exit 0
+else
+	msgpass
+fi
+
+buildsimplenativepackage 'libc6' 'amd64,sparc,armel' '1' 'stable' 'Multi-Arch: same'
+buildsimplenativepackage 'libc6-i386' 'amd64' '1' 'stable' 'Conflicts: libc6:sparc'
+
+setupaptarchive
+
+testsuccess aptget install 'libc6:amd64' 'libc6:sparc' -y
+testdpkginstalled 'libc6:amd64' 'libc6:sparc'
+testdpkgnotinstalled 'libc6-i386' 'libc6:armel'
+
+testsuccess aptget install libc6-i386 -y
+testdpkginstalled 'libc6:amd64' 'libc6-i386'
+testdpkgnotinstalled 'libc6:sparc' 'libc6:armel'
+
+testsuccess aptget install libc6:armel -y
+testdpkginstalled 'libc6:amd64' 'libc6:armel' 'libc6-i386'
+testdpkgnotinstalled 'libc6:sparc'
+
+testsuccess aptget install libc6:sparc -y
+testdpkginstalled 'libc6:amd64' 'libc6:armel' 'libc6:sparc'
+testdpkgnotinstalled 'libc6-i386'
+
+testsuccess aptget purge 'libc6:*' 'libc6-i386' -y
+testdpkgnotinstalled 'libc6:amd64' 'libc6:armel' 'libc6:sparc' 'libc6-i386'
+
+# check that (the actually simpler) single arch is fine, too
+configarchitecture 'amd64'
+testfailure aptget install libc6:sparc -s
+testsuccess aptget install libc6 libc6-i386 -y

+ 0 - 77
test/integration/test-debsrc-hashes

@@ -1,77 +0,0 @@
-#!/bin/sh
-set -e
-
-TESTDIR=$(readlink -f $(dirname $0))
-. $TESTDIR/framework
-
-setupenvironment
-configarchitecture "i386"
-
-# pkg-sha256-bad has a bad SHA sum, but good MD5 sum.  If apt is
-# checking the best available hash (as it should), this will trigger
-# a hash mismatch.
-
-cat > aptarchive/Sources <<EOF
-Package: pkg-md5-ok
-Binary: pkg-md5-ok
-Version: 1.0
-Maintainer: Joe Sixpack <joe@example.org>
-Architecture: i386
-Files:
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-ok_1.0.dsc
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-md5-ok_1.0.tar.gz
-
-Package: pkg-sha256-ok
-Binary: pkg-sha256-ok
-Version: 1.0
-Maintainer: Joe Sixpack <joe@example.org>
-Architecture: i386
-Files:
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-ok_1.0.dsc
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-ok_1.0.tar.gz
-Checksums-Sha1:
- da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-ok_1.0.dsc
- da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-ok_1.0.tar.gz
-Checksums-Sha256:
- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-ok_1.0.dsc
- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 pkg-sha256-ok_1.0.tar.gz
-
-Package: pkg-sha256-bad
-Binary: pkg-sha256-bad
-Version: 1.0
-Maintainer: Joe Sixpack <joe@example.org>
-Architecture: i386
-Files:
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-bad_1.0.dsc
- d41d8cd98f00b204e9800998ecf8427e 0 pkg-sha256-bad_1.0.tar.gz
-Checksums-Sha1:
- da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-bad_1.0.dsc
- da39a3ee5e6b4b0d3255bfef95601890afd80709 0 pkg-sha256-bad_1.0.tar.gz
-Checksums-Sha256:
- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 pkg-sha256-bad_1.0.dsc
- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 pkg-sha256-bad_1.0.tar.gz
-EOF
-
-# create fetchable files
-for x in "pkg-md5-ok" "pkg-sha256-ok" "pkg-sha256-bad"; do
-    touch aptarchive/${x}_1.0.dsc
-    touch aptarchive/${x}_1.0.tar.gz
-done
-
-testok() {
-    msgtest "Test for hash ok of" "$*"
-    $* 2>&1 | grep "Download complete" > /dev/null && msgpass || msgfail
-}
-
-testmismatch() {
-    msgtest "Test for hash mismatch of" "$*"
-    $* 2>&1 | grep "Hash Sum mismatch" > /dev/null && msgpass || msgfail
-}
-
-setupaptarchive
-changetowebserver
-aptget update -qq
-
-testok aptget source -d pkg-md5-ok
-testok aptget source -d pkg-sha256-ok
-testmismatch aptget source -d pkg-sha256-bad

+ 43 - 0
test/integration/test-http-pipeline-messup

@@ -0,0 +1,43 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture "i386"
+
+buildsimplenativepackage 'pkga' 'all' '1.0' 'stable'
+buildsimplenativepackage 'pkgb' 'all' '1.0' 'stable'
+buildsimplenativepackage 'pkgc' 'all' '1.0' 'stable'
+buildsimplenativepackage 'pkgd' 'all' '1.0' 'stable'
+
+setupaptarchive --no-update
+
+# simulate (and be a predictable) pipeline mess-up by the server/proxy
+changetowebserver \
+	-o 'aptwebserver::overwrite::.*pkga.*::filename=/pool/pkgd_1.0_all.deb' \
+	-o 'aptwebserver::overwrite::.*pkgc.*::filename=/pool/pkgb_1.0_all.deb' \
+	-o 'aptwebserver::overwrite::.*pkgb.*::filename=/pool/pkgc_1.0_all.deb' \
+	-o 'aptwebserver::overwrite::.*pkgd.*::filename=/pool/pkga_1.0_all.deb'
+
+testsuccess aptget update -o Debug::Acquire::http=1 -o Debug::pkgAcquire::Worker=1
+
+# messup is bigger than pipeline: checks if fixup isn't trying to hard
+testfailure aptget download pkga pkgb pkgc pkgd "$@" -o Acquire::http::Pipeline-Depth=2
+testfailure test -f pkga_1.0_all.deb
+
+# ensure that pipeling is enabled for rest of this test
+echo 'Acquire::http::Pipeline-Depth 10;' > rootdir/etc/apt/apt.conf.d/99enable-pipeline
+
+# the output is a bit strange: it looks like it has downloaded pkga 4 times
+testsuccess aptget download pkga pkgb pkgc pkgd -o Debug::Acquire::http=1 -o Debug::pkgAcquire::Worker=1
+for pkg in 'pkga' 'pkgb' 'pkgc' 'pkgd'; do
+	testsuccess test -f ${pkg}_1.0_all.deb
+	testsuccess cmp incoming/${pkg}_1.0_all.deb ${pkg}_1.0_all.deb
+	rm -f ${pkg}_1.0_all.deb
+done
+
+# while hashes will pass (as none are available), sizes will not match, so failure
+# checks that no hashes means that pipeline depth is ignored as we can't fixup
+testfailure aptget download pkga pkgb pkgc pkgd "$@" --allow-unauthenticated -o Acquire::ForceHash=ROT26

+ 1 - 1
test/integration/test-kernel-helper-autoremove

@@ -9,7 +9,7 @@ configarchitecture 'amd64'
 # the executed script would use the installed apt-config,
 # the executed script would use the installed apt-config,
 # which is outside of our control
 # which is outside of our control
 msgtest 'Check that the installed apt-config supports' '--no-empty'
 msgtest 'Check that the installed apt-config supports' '--no-empty'
-if apt-config dump --no-empty >/dev/null 2>&1; then
+if /usr/bin/apt-config dump --no-empty >/dev/null 2>&1; then
 	msgpass
 	msgpass
 else
 else
 	msgskip
 	msgskip

+ 0 - 6
test/integration/test-pdiff-usage

@@ -13,12 +13,6 @@ changetowebserver
 
 
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 
 
-echo '#!/bin/sh
-touch merge-was-used
-/usr/bin/diffindex-rred "$@"' > extrred
-chmod +x extrred
-echo 'Dir::Bin::rred "./extrred";' > rootdir/etc/apt/apt.conf.d/99rred
-
 wasmergeused() {
 wasmergeused() {
 	msgtest 'Test for successful execution of' "$*"
 	msgtest 'Test for successful execution of' "$*"
 	local OUTPUT=$(mktemp)
 	local OUTPUT=$(mktemp)

+ 262 - 0
test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum

@@ -0,0 +1,262 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture 'native'
+
+cat > aptarchive/Sources <<EOF
+Package: pkg-md5-ok
+Binary: pkg-md5-ok
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-md5-ok_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-md5-ok_1.0.tar.gz
+
+Package: pkg-sha256-ok
+Binary: pkg-sha256-ok
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-sha256-ok_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-sha256-ok_1.0.tar.gz
+Checksums-Sha1:
+ 324f464e6151a92cf57b26ef95dcfcf2059a8c44 3 pkg-sha256-ok_1.0.dsc
+ 680254bad1d7ca0d65ec46aaa315d363abf6a50a 3 pkg-sha256-ok_1.0.tar.gz
+Checksums-Sha256:
+ 943d3bf22ac661fb0f59bc4ff68cc12b04ff17a838dfcc2537008eb9c7f3770a 3 pkg-sha256-ok_1.0.dsc
+ 90aebae315675cbf04612de4f7d5874850f48e0b8dd82becbeaa47ca93f5ebfb 3 pkg-sha256-ok_1.0.tar.gz
+
+Package: pkg-sha256-bad
+Binary: pkg-sha256-bad
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-sha256-bad_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-sha256-bad_1.0.tar.gz
+Checksums-Sha1:
+ 324f464e6151a92cf57b26ef95dcfcf2059a8c44 3 pkg-sha256-bad_1.0.dsc
+ 680254bad1d7ca0d65ec46aaa315d363abf6a50a 3 pkg-sha256-bad_1.0.tar.gz
+Checksums-Sha256:
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3 pkg-sha256-bad_1.0.dsc
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3 pkg-sha256-bad_1.0.tar.gz
+
+Package: pkg-no-md5
+Binary: pkg-no-md5
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Checksums-Sha1:
+ 324f464e6151a92cf57b26ef95dcfcf2059a8c44 3 pkg-no-md5_1.0.dsc
+ 680254bad1d7ca0d65ec46aaa315d363abf6a50a 3 pkg-no-md5_1.0.tar.gz
+Checksums-Sha256:
+ 943d3bf22ac661fb0f59bc4ff68cc12b04ff17a838dfcc2537008eb9c7f3770a 3 pkg-no-md5_1.0.dsc
+ 90aebae315675cbf04612de4f7d5874850f48e0b8dd82becbeaa47ca93f5ebfb 3 pkg-no-md5_1.0.tar.gz
+
+Package: pkg-mixed-ok
+Binary: pkg-mixed-ok
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Checksums-Sha1:
+ 680254bad1d7ca0d65ec46aaa315d363abf6a50a 3 pkg-mixed-ok_1.0.tar.gz
+Checksums-Sha256:
+ 943d3bf22ac661fb0f59bc4ff68cc12b04ff17a838dfcc2537008eb9c7f3770a 3 pkg-mixed-ok_1.0.dsc
+
+Package: pkg-mixed-sha1-bad
+Binary: pkg-mixed-sha1-bad
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Checksums-Sha1:
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3 pkg-mixed-sha1-bad_1.0.dsc
+Checksums-Sha256:
+ 90aebae315675cbf04612de4f7d5874850f48e0b8dd82becbeaa47ca93f5ebfb 3 pkg-mixed-sha1-bad_1.0.tar.gz
+
+Package: pkg-mixed-sha2-bad
+Binary: pkg-mixed-sha2-bad
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Checksums-Sha1:
+ 324f464e6151a92cf57b26ef95dcfcf2059a8c44 3 pkg-mixed-sha2-bad_1.0.dsc
+Checksums-Sha256:
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3 pkg-mixed-sha2-bad_1.0.tar.gz
+
+Package: pkg-md5-disagree
+Binary: pkg-md5-disagree
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-md5-disagree_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-md5-disagree_1.0.tar.gz
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3 pkg-md5-disagree_1.0.dsc
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3 pkg-md5-disagree_1.0.tar.gz
+
+Package: pkg-md5-agree
+Binary: pkg-md5-agree
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-md5-agree_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-md5-agree_1.0.tar.gz
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-md5-agree_1.0.tar.gz
+ 9604ba9427a280db542279d9ed78400b 3 pkg-md5-agree_1.0.dsc
+
+Package: pkg-sha256-disagree
+Binary: pkg-sha256-disagree
+Version: 1.0
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: all
+Files:
+ 9604ba9427a280db542279d9ed78400b 3 pkg-sha256-disagree_1.0.dsc
+ db5570bf61464b46e2bde31ed61a7dc6 3 pkg-sha256-disagree_1.0.tar.gz
+Checksums-Sha1:
+ 324f464e6151a92cf57b26ef95dcfcf2059a8c44 3 pkg-sha256-disagree_1.0.dsc
+ 680254bad1d7ca0d65ec46aaa315d363abf6a50a 3 pkg-sha256-disagree_1.0.tar.gz
+Checksums-Sha256:
+ 943d3bf22ac661fb0f59bc4ff68cc12b04ff17a838dfcc2537008eb9c7f3770a 3 pkg-sha256-disagree_1.0.dsc
+ 90aebae315675cbf04612de4f7d5874850f48e0b8dd82becbeaa47ca93f5ebfb 3 pkg-sha256-disagree_1.0.tar.gz
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3 pkg-sha256-disagree_1.0.dsc
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3 pkg-sha256-disagree_1.0.tar.gz
+EOF
+
+# create fetchable files
+for x in 'pkg-md5-ok' 'pkg-sha256-ok' 'pkg-sha256-bad' 'pkg-no-md5' \
+	 'pkg-mixed-ok' 'pkg-mixed-sha1-bad' 'pkg-mixed-sha2-bad' \
+	 'pkg-md5-agree' 'pkg-md5-disagree' 'pkg-sha256-disagree'; do
+	echo -n 'dsc' > aptarchive/${x}_1.0.dsc
+	echo -n 'tar' > aptarchive/${x}_1.0.tar.gz
+done
+
+setupaptarchive
+changetowebserver
+testsuccess aptget update
+
+testok() {
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+	testequal "Reading package lists...
+Building dependency tree...
+Need to get 6 B of source archives.
+Get:1 http://localhost:8080/  $1 1.0 (dsc) [3 B]
+Get:2 http://localhost:8080/  $1 1.0 (tar) [3 B]
+Download complete and in download only mode" aptget source -d "$@"
+	msgtest 'Files were successfully downloaded for' "$1"
+	testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+}
+
+testkeep() {
+	echo -n 'dsc' > ${1}_1.0.dsc
+	echo -n 'tar' > ${1}_1.0.tar.gz
+	testequal "Reading package lists...
+Building dependency tree...
+Skipping already downloaded file '${1}_1.0.dsc'
+Skipping already downloaded file '${1}_1.0.tar.gz'
+Need to get 0 B of source archives.
+Download complete and in download only mode" aptget source -d "$@"
+	msgtest 'Files already downloaded are kept for' "$1"
+	testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+}
+
+testmismatch() {
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+	testequal "Reading package lists...
+Building dependency tree...
+Need to get 6 B of source archives.
+Get:1 http://localhost:8080/  $1 1.0 (dsc) [3 B]
+Get:2 http://localhost:8080/  $1 1.0 (tar) [3 B]
+E: Failed to fetch http://localhost:8080/${1}_1.0.dsc  Hash Sum mismatch
+
+E: Failed to fetch http://localhost:8080/${1}_1.0.tar.gz  Hash Sum mismatch
+
+E: Failed to fetch some archives." aptget source -d "$@"
+	msgtest 'Files were not download as they have hashsum mismatches for' "$1"
+	testfailure --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz
+
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+	testequal "Reading package lists...
+Building dependency tree...
+Skipping download of file 'pkg-sha256-bad_1.0.dsc' as requested hashsum is not available for authentication
+Skipping download of file 'pkg-sha256-bad_1.0.tar.gz' as requested hashsum is not available for authentication
+Need to get 0 B of source archives.
+Download complete and in download only mode" aptget source -d "$@" -o Acquire::ForceHash=ROT26
+	msgtest 'Files were not download as hash is unavailable for' "$1"
+	testfailure --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz
+
+	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
+	testequal "Reading package lists...
+Building dependency tree...
+Need to get 6 B of source archives.
+Get:1 http://localhost:8080/  $1 1.0 (dsc) [3 B]
+Get:2 http://localhost:8080/  $1 1.0 (tar) [3 B]
+Download complete and in download only mode" aptget source --allow-unauthenticated -d "$@" -o Acquire::ForceHash=ROT26
+	msgtest 'Files were downloaded unauthenticated as user allowed it' "$1"
+	testsuccess --nomsg test -e ${1}_1.0.dsc -a -e ${1}_1.0.tar.gz
+}
+
+testok pkg-md5-ok
+testkeep pkg-md5-ok
+testok pkg-sha256-ok
+testkeep pkg-sha256-ok
+
+# pkg-sha256-bad has a bad SHA sum, but good MD5 sum.  If apt is
+# checking the best available hash (as it should), this will trigger
+# a hash mismatch.
+testmismatch pkg-sha256-bad
+testmismatch pkg-sha256-bad
+testok pkg-sha256-bad -o Acquire::ForceHash=MD5Sum
+
+# not having MD5 sum doesn't mean the file doesn't exist at all …
+testok pkg-no-md5
+testok pkg-no-md5 -o Acquire::ForceHash=SHA256
+testequal "Reading package lists...
+Building dependency tree...
+Skipping download of file 'pkg-no-md5_1.0.dsc' as requested hashsum is not available for authentication
+Skipping download of file 'pkg-no-md5_1.0.tar.gz' as requested hashsum is not available for authentication
+Need to get 0 B of source archives.
+Download complete and in download only mode" aptget source -d pkg-no-md5 -o Acquire::ForceHash=MD5Sum
+msgtest 'Files were not download as MD5 is not available for this package' 'pkg-no-md5'
+testfailure --nomsg test -e pkg-no-md5_1.0.dsc -a -e pkg-no-md5_1.0.tar.gz
+
+# deal with cases in which we haven't for all files the same checksum type
+# mostly pathologic as this shouldn't happen, but just to be sure
+testok pkg-mixed-ok
+testequal 'Reading package lists...
+Building dependency tree...
+Need to get 6 B of source archives.
+Get:1 http://localhost:8080/  pkg-mixed-sha1-bad 1.0 (tar) [3 B]
+Get:2 http://localhost:8080/  pkg-mixed-sha1-bad 1.0 (dsc) [3 B]
+E: Failed to fetch http://localhost:8080/pkg-mixed-sha1-bad_1.0.dsc  Hash Sum mismatch
+
+E: Failed to fetch some archives.' aptget source -d pkg-mixed-sha1-bad
+msgtest 'Only tar file is downloaded as the dsc has hashsum mismatch' 'pkg-mixed-sha1-bad'
+testsuccess --nomsg test ! -e pkg-mixed-sha1-bad_1.0.dsc -a -e pkg-mixed-sha1-bad_1.0.tar.gz
+testequal 'Reading package lists...
+Building dependency tree...
+Need to get 6 B of source archives.
+Get:1 http://localhost:8080/  pkg-mixed-sha2-bad 1.0 (tar) [3 B]
+Get:2 http://localhost:8080/  pkg-mixed-sha2-bad 1.0 (dsc) [3 B]
+E: Failed to fetch http://localhost:8080/pkg-mixed-sha2-bad_1.0.tar.gz  Hash Sum mismatch
+
+E: Failed to fetch some archives.' aptget source -d pkg-mixed-sha2-bad
+msgtest 'Only dsc file is downloaded as the tar has hashsum mismatch' 'pkg-mixed-sha2-bad'
+testsuccess --nomsg test -e pkg-mixed-sha2-bad_1.0.dsc -a ! -e pkg-mixed-sha2-bad_1.0.tar.gz
+
+# it gets even more pathologic: multiple entries for one file, some even disagreeing!
+testok pkg-md5-agree
+testequal 'Reading package lists...
+Building dependency tree...
+E: Error parsing checksum in Files of source package pkg-md5-disagree' aptget source -d pkg-md5-disagree
+testequal 'Reading package lists...
+Building dependency tree...
+E: Error parsing checksum in Checksums-SHA256 of source package pkg-sha256-disagree' aptget source -d pkg-sha256-disagree

+ 1 - 1
test/integration/test-ubuntu-bug-346386-apt-get-update-paywall

@@ -22,7 +22,7 @@ Config-Item: Acquire::http::DependOnSTDIN=0
 600 Acquire URI
 600 Acquire URI
 URI: http://localhost:8080/holygrail
 URI: http://localhost:8080/holygrail
 Filename: knights-talking
 Filename: knights-talking
-' | LD_LIBRARY_PATH=${BUILDDIRECTORY} ${METHODSDIR}/http >/dev/null 2>&1 && msgpass || msgfail
+' | runapt ${METHODSDIR}/http >/dev/null 2>&1 && msgpass || msgfail
 testfileequal knights-talking 'ni ni ni'
 testfileequal knights-talking 'ni ni ni'
 
 
 ensure_n_canary_strings_in_dir() {
 ensure_n_canary_strings_in_dir() {

+ 129 - 20
test/libapt/hashsums_test.cc

@@ -1,5 +1,6 @@
 #include <config.h>
 #include <config.h>
 
 
+#include <apt-pkg/configuration.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/sha1.h>
 #include <apt-pkg/sha1.h>
 #include <apt-pkg/sha2.h>
 #include <apt-pkg/sha2.h>
@@ -166,20 +167,26 @@ TEST(HashSumsTest, FileBased)
    {
    {
       Hashes hashes;
       Hashes hashes;
       hashes.AddFD(fd.Fd());
       hashes.AddFD(fd.Fd());
-      EXPECT_EQ(md5.Value(), hashes.MD5.Result().Value());
-      EXPECT_EQ(sha1.Value(), hashes.SHA1.Result().Value());
-      EXPECT_EQ(sha256.Value(), hashes.SHA256.Result().Value());
-      EXPECT_EQ(sha512.Value(), hashes.SHA512.Result().Value());
+      HashStringList list = hashes.GetHashStringList();
+      EXPECT_FALSE(list.empty());
+      EXPECT_EQ(4, list.size());
+      EXPECT_EQ(md5.Value(), list.find("MD5Sum")->HashValue());
+      EXPECT_EQ(sha1.Value(), list.find("SHA1")->HashValue());
+      EXPECT_EQ(sha256.Value(), list.find("SHA256")->HashValue());
+      EXPECT_EQ(sha512.Value(), list.find("SHA512")->HashValue());
    }
    }
    unsigned long sz = fd.FileSize();
    unsigned long sz = fd.FileSize();
    fd.Seek(0);
    fd.Seek(0);
    {
    {
       Hashes hashes;
       Hashes hashes;
       hashes.AddFD(fd.Fd(), sz);
       hashes.AddFD(fd.Fd(), sz);
-      EXPECT_EQ(md5.Value(), hashes.MD5.Result().Value());
-      EXPECT_EQ(sha1.Value(), hashes.SHA1.Result().Value());
-      EXPECT_EQ(sha256.Value(), hashes.SHA256.Result().Value());
-      EXPECT_EQ(sha512.Value(), hashes.SHA512.Result().Value());
+      HashStringList list = hashes.GetHashStringList();
+      EXPECT_FALSE(list.empty());
+      EXPECT_EQ(4, list.size());
+      EXPECT_EQ(md5.Value(), list.find("MD5Sum")->HashValue());
+      EXPECT_EQ(sha1.Value(), list.find("SHA1")->HashValue());
+      EXPECT_EQ(sha256.Value(), list.find("SHA256")->HashValue());
+      EXPECT_EQ(sha512.Value(), list.find("SHA512")->HashValue());
    }
    }
    fd.Seek(0);
    fd.Seek(0);
    {
    {
@@ -207,16 +214,118 @@ TEST(HashSumsTest, FileBased)
    }
    }
    fd.Close();
    fd.Close();
 
 
-   {
-      HashString sha2("SHA256", sha256.Value());
-      EXPECT_TRUE(sha2.VerifyFile(__FILE__));
-   }
-   {
-      HashString sha2("SHA512", sha512.Value());
-      EXPECT_TRUE(sha2.VerifyFile(__FILE__));
-   }
-   {
-      HashString sha2("SHA256:" + sha256.Value());
-      EXPECT_TRUE(sha2.VerifyFile(__FILE__));
-   }
+   HashString sha2file("SHA512", sha512.Value());
+   EXPECT_TRUE(sha2file.VerifyFile(__FILE__));
+   HashString sha2wrong("SHA512", "00000000000");
+   EXPECT_FALSE(sha2wrong.VerifyFile(__FILE__));
+   EXPECT_EQ(sha2file, sha2file);
+   EXPECT_TRUE(sha2file == sha2file);
+   EXPECT_NE(sha2file, sha2wrong);
+   EXPECT_TRUE(sha2file != sha2wrong);
+
+   HashString sha2big("SHA256", sha256.Value());
+   EXPECT_TRUE(sha2big.VerifyFile(__FILE__));
+   HashString sha2small("sha256:" + sha256.Value());
+   EXPECT_TRUE(sha2small.VerifyFile(__FILE__));
+   EXPECT_EQ(sha2big, sha2small);
+   EXPECT_TRUE(sha2big == sha2small);
+   EXPECT_FALSE(sha2big != sha2small);
+
+   HashStringList hashes;
+   EXPECT_TRUE(hashes.empty());
+   EXPECT_TRUE(hashes.push_back(sha2file));
+   EXPECT_FALSE(hashes.empty());
+   EXPECT_EQ(1, hashes.size());
+
+   HashStringList wrong;
+   EXPECT_TRUE(wrong.push_back(sha2wrong));
+   EXPECT_NE(wrong, hashes);
+   EXPECT_FALSE(wrong == hashes);
+   EXPECT_TRUE(wrong != hashes);
+
+   HashStringList similar;
+   EXPECT_TRUE(similar.push_back(sha2big));
+   EXPECT_NE(similar, hashes);
+   EXPECT_FALSE(similar == hashes);
+   EXPECT_TRUE(similar != hashes);
+
+   EXPECT_TRUE(hashes.push_back(sha2big));
+   EXPECT_EQ(2, hashes.size());
+   EXPECT_TRUE(hashes.push_back(sha2small));
+   EXPECT_EQ(2, hashes.size());
+   EXPECT_FALSE(hashes.push_back(sha2wrong));
+   EXPECT_EQ(2, hashes.size());
+   EXPECT_TRUE(hashes.VerifyFile(__FILE__));
+
+   EXPECT_EQ(similar, hashes);
+   EXPECT_TRUE(similar == hashes);
+   EXPECT_FALSE(similar != hashes);
+   similar.clear();
+   EXPECT_TRUE(similar.empty());
+   EXPECT_EQ(0, similar.size());
+   EXPECT_NE(similar, hashes);
+   EXPECT_FALSE(similar == hashes);
+   EXPECT_TRUE(similar != hashes);
+}
+TEST(HashSumsTest, HashStringList)
+{
+   _config->Clear("Acquire::ForceHash");
+
+   HashStringList list;
+   EXPECT_TRUE(list.empty());
+   EXPECT_FALSE(list.usable());
+   EXPECT_EQ(0, list.size());
+   EXPECT_EQ(NULL, list.find(NULL));
+   EXPECT_EQ(NULL, list.find(""));
+   EXPECT_EQ(NULL, list.find("MD5Sum"));
+
+   HashStringList list2;
+   EXPECT_FALSE(list == list2);
+   EXPECT_TRUE(list != list2);
+
+   Hashes hashes;
+   hashes.Add("The quick brown fox jumps over the lazy dog");
+   list = hashes.GetHashStringList();
+   EXPECT_FALSE(list.empty());
+   EXPECT_TRUE(list.usable());
+   EXPECT_EQ(4, list.size());
+   EXPECT_TRUE(NULL != list.find(NULL));
+   EXPECT_TRUE(NULL != list.find(""));
+   EXPECT_TRUE(NULL != list.find("MD5Sum"));
+   EXPECT_TRUE(NULL == list.find("ROT26"));
+
+   _config->Set("Acquire::ForceHash", "MD5Sum");
+   EXPECT_FALSE(list.empty());
+   EXPECT_TRUE(list.usable());
+   EXPECT_EQ(4, list.size());
+   EXPECT_TRUE(NULL != list.find(NULL));
+   EXPECT_TRUE(NULL != list.find(""));
+   EXPECT_TRUE(NULL != list.find("MD5Sum"));
+   EXPECT_TRUE(NULL == list.find("ROT26"));
+
+   _config->Set("Acquire::ForceHash", "ROT26");
+   EXPECT_FALSE(list.empty());
+   EXPECT_FALSE(list.usable());
+   EXPECT_EQ(4, list.size());
+   EXPECT_TRUE(NULL == list.find(NULL));
+   EXPECT_TRUE(NULL == list.find(""));
+   EXPECT_TRUE(NULL != list.find("MD5Sum"));
+   EXPECT_TRUE(NULL == list.find("ROT26"));
+
+   _config->Clear("Acquire::ForceHash");
+
+   list2.push_back(*list.find("MD5Sum"));
+   EXPECT_TRUE(list == list2);
+   EXPECT_FALSE(list != list2);
+
+   // introduce a mismatch to the list
+   list2.push_back(HashString("SHA1", "cacecbd74968bc90ea3342767e6b94f46ddbcafc"));
+   EXPECT_FALSE(list == list2);
+   EXPECT_TRUE(list != list2);
+
+   _config->Set("Acquire::ForceHash", "MD5Sum");
+   EXPECT_TRUE(list == list2);
+   EXPECT_FALSE(list != list2);
+
+   _config->Clear("Acquire::ForceHash");
 }
 }

+ 179 - 0
test/libapt/tagfile_test.cc

@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 #include <unistd.h>
 #include <unistd.h>
+#include <sstream>
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
@@ -34,3 +35,181 @@ TEST(TagFileTest,SingleField)
    // There is only one section in this tag file
    // There is only one section in this tag file
    EXPECT_FALSE(tfile.Step(section));
    EXPECT_FALSE(tfile.Step(section));
 }
 }
+
+TEST(TagFileTest,MultipleSections)
+{
+   FileFd fd;
+   createTemporaryFile("bigsection", fd, NULL, "Package: pkgA\n"
+	 "Version: 1\n"
+	 "Size: 100\n"
+	 "Description: aaa\n"
+	 " aaa\n"
+	 "\n"
+	 "Package: pkgB\n"
+	 "Version: 1\n"
+	 "Flag: no\n"
+	 "Description: bbb\n"
+	 "\n"
+	 "Package: pkgC\n"
+	 "Version: 2\n"
+	 "Flag: yes\n"
+	 "Description:\n"
+	 " ccc\n"
+	 );
+
+   pkgTagFile tfile(&fd);
+   pkgTagSection section;
+   EXPECT_FALSE(section.Exists("Version"));
+
+   EXPECT_TRUE(tfile.Step(section));
+   EXPECT_EQ(4, section.Count());
+   EXPECT_TRUE(section.Exists("Version"));
+   EXPECT_TRUE(section.Exists("Package"));
+   EXPECT_TRUE(section.Exists("Size"));
+   EXPECT_FALSE(section.Exists("Flag"));
+   EXPECT_TRUE(section.Exists("Description"));
+   EXPECT_EQ("pkgA", section.FindS("Package"));
+   EXPECT_EQ("1", section.FindS("Version"));
+   EXPECT_EQ(1, section.FindULL("Version"));
+   EXPECT_EQ(100, section.FindULL("Size"));
+   unsigned long Flags = 1;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(1, Flags);
+   Flags = 0;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(0, Flags);
+   EXPECT_EQ("aaa\n aaa", section.FindS("Description"));
+
+
+   EXPECT_TRUE(tfile.Step(section));
+   EXPECT_EQ(4, section.Count());
+   EXPECT_TRUE(section.Exists("Version"));
+   EXPECT_TRUE(section.Exists("Package"));
+   EXPECT_FALSE(section.Exists("Size"));
+   EXPECT_TRUE(section.Exists("Flag"));
+   EXPECT_TRUE(section.Exists("Description"));
+   EXPECT_EQ("pkgB", section.FindS("Package"));
+   EXPECT_EQ("1", section.FindS("Version"));
+   EXPECT_EQ(1, section.FindULL("Version"));
+   EXPECT_EQ(0, section.FindULL("Size"));
+   Flags = 1;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(0, Flags);
+   Flags = 0;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(0, Flags);
+   EXPECT_EQ("bbb", section.FindS("Description"));
+
+   EXPECT_TRUE(tfile.Step(section));
+   EXPECT_EQ(4, section.Count());
+   EXPECT_TRUE(section.Exists("Version"));
+   EXPECT_TRUE(section.Exists("Package"));
+   EXPECT_FALSE(section.Exists("Size"));
+   EXPECT_TRUE(section.Exists("Flag"));
+   EXPECT_TRUE(section.Exists("Description"));
+   EXPECT_EQ("pkgC", section.FindS("Package"));
+   EXPECT_EQ("2", section.FindS("Version"));
+   EXPECT_EQ(2, section.FindULL("Version"));
+   Flags = 0;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(1, Flags);
+   Flags = 1;
+   EXPECT_TRUE(section.FindFlag("Flag", Flags, 1));
+   EXPECT_EQ(1, Flags);
+   EXPECT_EQ("ccc", section.FindS("Description"));
+
+   // There is no section left in this tag file
+   EXPECT_FALSE(tfile.Step(section));
+}
+
+TEST(TagFileTest,BigSection)
+{
+   size_t const count = 500;
+   std::stringstream content;
+   for (size_t i = 0; i < count; ++i)
+      content << "Field-" << i << ": " << (2000 + i) << std::endl;
+
+   FileFd fd;
+   createTemporaryFile("bigsection", fd, NULL, content.str().c_str());
+
+   pkgTagFile tfile(&fd);
+   pkgTagSection section;
+   EXPECT_TRUE(tfile.Step(section));
+
+   EXPECT_EQ(count, section.Count());
+   for (size_t i = 0; i < count; ++i)
+   {
+      std::stringstream name;
+      name << "Field-" << i;
+      EXPECT_TRUE(section.Exists(name.str().c_str())) << name.str() << " does not exist";
+      EXPECT_EQ((2000 + i), section.FindULL(name.str().c_str()));
+   }
+
+   // There is only one section in this tag file
+   EXPECT_FALSE(tfile.Step(section));
+}
+
+TEST(TagFileTest, PickedUpFromPreviousCall)
+{
+   size_t const count = 500;
+   std::stringstream contentstream;
+   for (size_t i = 0; i < count; ++i)
+      contentstream << "Field-" << i << ": " << (2000 + i) << std::endl;
+   contentstream << std::endl << std::endl;
+   std::string content = contentstream.str();
+
+   pkgTagSection section;
+   EXPECT_FALSE(section.Scan(content.c_str(), content.size()/2));
+   EXPECT_NE(0, section.Count());
+   EXPECT_NE(count, section.Count());
+   EXPECT_TRUE(section.Scan(content.c_str(), content.size(), false));
+   EXPECT_EQ(count, section.Count());
+
+   for (size_t i = 0; i < count; ++i)
+   {
+      std::stringstream name;
+      name << "Field-" << i;
+      EXPECT_TRUE(section.Exists(name.str().c_str())) << name.str() << " does not exist";
+      EXPECT_EQ((2000 + i), section.FindULL(name.str().c_str()));
+   }
+}
+
+TEST(TagFileTest, SpacesEverywhere)
+{
+   std::string content =
+      "Package: pkgA\n"
+      "Package: pkgB\n"
+      "NoSpaces:yes\n"
+      "TagSpaces\t    :yes\n"
+      "ValueSpaces:   \tyes\n"
+      "BothSpaces     \t:\t   yes\n"
+      "TrailingSpaces: yes\t   \n"
+      "Naming Space: yes\n"
+      "Naming  Spaces: yes\n"
+      "Package    :   pkgC    \n"
+      "Multi-Colon::yes:\n"
+      "\n\n";
+
+   pkgTagSection section;
+   EXPECT_TRUE(section.Scan(content.c_str(), content.size()));
+   EXPECT_TRUE(section.Exists("Package"));
+   EXPECT_TRUE(section.Exists("NoSpaces"));
+   EXPECT_TRUE(section.Exists("TagSpaces"));
+   EXPECT_TRUE(section.Exists("ValueSpaces"));
+   EXPECT_TRUE(section.Exists("BothSpaces"));
+   EXPECT_TRUE(section.Exists("TrailingSpaces"));
+   EXPECT_TRUE(section.Exists("Naming Space"));
+   EXPECT_TRUE(section.Exists("Naming  Spaces"));
+   EXPECT_TRUE(section.Exists("Multi-Colon"));
+   EXPECT_EQ("pkgC", section.FindS("Package"));
+   EXPECT_EQ("yes", section.FindS("NoSpaces"));
+   EXPECT_EQ("yes", section.FindS("TagSpaces"));
+   EXPECT_EQ("yes", section.FindS("ValueSpaces"));
+   EXPECT_EQ("yes", section.FindS("BothSpaces"));
+   EXPECT_EQ("yes", section.FindS("TrailingSpaces"));
+   EXPECT_EQ("yes", section.FindS("Naming Space"));
+   EXPECT_EQ("yes", section.FindS("Naming  Spaces"));
+   EXPECT_EQ(":yes:", section.FindS("Multi-Colon"));
+   // overridden values are still present, but not really accessible
+   EXPECT_EQ(11, section.Count());
+}