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

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

Conflicts:
	debian/changelog
Michael Vogt лет назад: 13
Родитель
Сommit
8e4c3f0a42
69 измененных файлов с 3172 добавлено и 2010 удалено
  1. 37 30
      .gitignore
  2. 3 0
      apt-inst/contrib/arfile.cc
  3. 0 1
      apt-inst/contrib/extracttar.cc
  4. 1 0
      apt-inst/makefile
  5. 12 1
      apt-pkg/acquire-item.cc
  6. 4 2
      apt-pkg/algorithms.cc
  7. 4 3
      apt-pkg/algorithms.h
  8. 8 0
      apt-pkg/cdrom.cc
  9. 35 6
      apt-pkg/contrib/error.h
  10. 91 100
      apt-pkg/contrib/fileutl.cc
  11. 4 0
      apt-pkg/contrib/fileutl.h
  12. 1 1
      apt-pkg/contrib/gpgv.cc
  13. 12 3
      apt-pkg/contrib/gpgv.h
  14. 6 6
      apt-pkg/contrib/sha2_internal.cc
  15. 3 2
      apt-pkg/contrib/strutl.cc
  16. 5 1
      apt-pkg/deb/deblistparser.cc
  17. 14 6
      apt-pkg/depcache.cc
  18. 2 4
      apt-pkg/indexcopy.cc
  19. 105 119
      apt-pkg/orderlist.cc
  20. 71 46
      apt-pkg/packagemanager.cc
  21. 3 1
      apt-pkg/pkgcachegen.cc
  22. 1 1
      buildlib/copy.mak
  23. 2 2
      buildlib/library.mak
  24. 7 5
      buildlib/po4a_manpage.mak
  25. 6 1
      buildlib/podomain.mak
  26. 83 43
      cmdline/apt-cache.cc
  27. 4 9
      cmdline/apt-cdrom.cc
  28. 1 3
      cmdline/apt-get.cc
  29. 1 1
      cmdline/makefile
  30. 23 2
      debian/changelog
  31. 2 2
      debian/rules
  32. 1 2
      doc/apt.conf.5.xml
  33. 38 0
      doc/en/makefile
  34. 1 1
      doc/examples/configure-index
  35. 19 53
      doc/makefile
  36. 139 140
      doc/po/apt-doc.pot
  37. 179 180
      doc/po/de.po
  38. 161 162
      doc/po/es.po
  39. 162 163
      doc/po/fr.po
  40. 213 186
      doc/po/it.po
  41. 160 160
      doc/po/ja.po
  42. 153 153
      doc/po/pl.po
  43. 167 166
      doc/po/pt.po
  44. 153 153
      doc/po/pt_BR.po
  45. 7 9
      methods/gpgv.cc
  46. 1 1
      methods/makefile
  47. 11 4
      po/makefile
  48. 2 0
      test/integration/Packages-pdiff-usage
  49. 2 0
      test/integration/Packages-pdiff-usage-new
  50. 1 0
      test/integration/Packages-releasefile-verification
  51. 1 0
      test/integration/Packages-releasefile-verification-new
  52. 55 20
      test/integration/framework
  53. 0 38
      test/integration/skip-bug-602412-dequote-redirect
  54. 47 0
      test/integration/skip-bug-711456-apt-cdrom-multiple-cds-multiarch
  55. 2 2
      test/integration/test-apt-cdrom
  56. 2 0
      test/integration/test-bug-590041-prefer-non-virtual-packages
  57. 20 11
      test/integration/test-bug-601016-description-translation
  58. 29 0
      test/integration/test-bug-602412-dequote-redirect
  59. 33 0
      test/integration/test-bug-691453-apt-cache-search-multi-pattern
  60. 89 0
      test/integration/test-bug-712435-missing-descriptions
  61. 2 2
      test/integration/test-cve-2013-1051-InRelease-parsing
  62. 1 1
      test/integration/test-pdiff-usage
  63. 106 0
      test/integration/test-prefer-higher-priority-providers
  64. 2 2
      test/integration/test-releasefile-verification
  65. 64 0
      test/integration/test-ubuntu-bug-346386-apt-get-update-paywall
  66. 46 0
      test/integration/test-very-tight-loop-configure-with-unpacking-new-packages
  67. 543 0
      test/interactive-helper/aptwebserver.cc
  68. 8 0
      test/interactive-helper/makefile
  69. 1 0
      test/libapt/makefile

+ 37 - 30
.gitignore

@@ -1,30 +1,37 @@
-aclocal.m4
-autom4te.cache/
-build/
-buildlib/config.guess
-buildlib/config.sub
-configure
-debian/copyright
-doc/de/
-doc/es/
-doc/fr/
-doc/it/
-doc/ja/
-doc/pl/
-doc/pt/
-doc/pt_BR/
-doc/examples/sources.list
-marks.bzr
-marks.git
-debian/*.log
-debian/*.substvars
-debian/*.debhelper
-debian/apt-doc
-debian/apt
-debian/apt-utils
-debian/libapt-pkg4.12
-debian/libapt-inst1.5
-debian/libapt-pkg-dev
-debian/libapt-pkg-doc
-debian/apt-transport-https
-debian/files
+# build artifacts
+/aclocal.m4
+/autom4te.cache/
+/build/
+/buildlib/config.guess
+/buildlib/config.sub
+/configure
+
+# copy of COPYING
+/debian/copyright
+
+# generated files in the process to build all
+# apt man pages and other documentation
+/doc/*/
+!/doc/examples/
+!/doc/po/
+!/doc/en/makefile
+
+# package building artifacts
+/debian/*.log
+/debian/*.substvars
+/debian/*.debhelper
+/debian/files
+/debian/apt/
+/debian/apt-doc/
+/debian/apt-utils/
+/debian/libapt-pkg*/
+/debian/libapt-inst*/
+/debian/libapt-pkg-dev/
+/debian/libapt-pkg-doc/
+/debian/apt-transport-https/
+
+# generated from *.in files
+/doc/examples/sources.list
+/debian/libapt-pkg*.install
+/debian/libapt-inst*.install
+/debian/libapt-pkg-dev.install

+ 3 - 0
apt-inst/contrib/arfile.cc

@@ -106,7 +106,10 @@ bool ARArchive::LoadHeaders()
 	    return _error->Error(_("Invalid archive member header"));
 	 }
 	 if (File.Read(S,Len) == false)
+	 {
+	    delete Memb;
 	    return false;
+	 }
 	 S[Len] = 0;
 	 Memb->Name = S;
 	 Memb->Size -= Len;

+ 0 - 1
apt-inst/contrib/extracttar.cc

@@ -63,7 +63,6 @@ ExtractTar::ExtractTar(FileFd &Fd,unsigned long Max,string DecompressionProgram)
 
 {
    GZPid = -1;
-   InFd = -1;
    Eof = false;
 }
 									/*}}}*/

+ 1 - 0
apt-inst/makefile

@@ -18,6 +18,7 @@ MAJOR=1.5
 MINOR=0
 SLIBS=$(PTHREADLIB) -lapt-pkg
 APT_DOMAIN:=libapt-inst$(MAJOR)
+LIBRARYDEPENDS=$(LIB)/libapt-pkg.so
 
 # Source code for the contributed non-core things
 SOURCE = contrib/extracttar.cc contrib/arfile.cc

+ 12 - 1
apt-pkg/acquire-item.cc

@@ -1289,7 +1289,14 @@ void pkgAcqMetaIndex::RetrievalDone(string Message)			/*{{{*/
       string FinalFile = _config->FindDir("Dir::State::lists");
       FinalFile += URItoFileName(RealURI);
       if (SigFile == DestFile)
+      {
 	 SigFile = FinalFile;
+	 // constructor of pkgAcqMetaClearSig moved it out of the way,
+	 // now move it back in on IMS hit for the 'old' file
+	 string const OldClearSig = DestFile + ".reverify";
+	 if (RealFileExists(OldClearSig) == true)
+	    Rename(OldClearSig, FinalFile);
+      }
       DestFile = FinalFile;
    }
    Complete = true;
@@ -1595,7 +1602,11 @@ string pkgAcqMetaClearSig::Custom600Headers()
 
    struct stat Buf;
    if (stat(Final.c_str(),&Buf) != 0)
-      return "\nIndex-File: true\nFail-Ignore: true\n";
+   {
+      Final = DestFile + ".reverify";
+      if (stat(Final.c_str(),&Buf) != 0)
+	 return "\nIndex-File: true\nFail-Ignore: true\n";
+   }
 
    return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }

+ 4 - 2
apt-pkg/algorithms.cc

@@ -1444,9 +1444,11 @@ bool pkgProblemResolver::ResolveByKeepInternal()
    return true;
 }
 									/*}}}*/
-// ProblemResolver::InstallProtect - Install all protected packages	/*{{{*/
+// ProblemResolver::InstallProtect - deprecated cpu-eating no-op	/*{{{*/
 // ---------------------------------------------------------------------
-/* This is used to make sure protected packages are installed */
+/* Actions issued with FromUser bit set are protected from further
+   modification (expect by other calls with FromUser set) nowadays , so we
+   don't need to reissue actions here, they are already set in stone. */
 void pkgProblemResolver::InstallProtect()
 {
    pkgDepCache::ActionGroup group(Cache);

+ 4 - 3
apt-pkg/algorithms.h

@@ -36,6 +36,8 @@
 
 #include <iostream>
 
+#include <apt-pkg/macros.h>
+
 #ifndef APT_8_CLEANER_HEADERS
 #include <apt-pkg/acquire.h>
 using std::ostream;
@@ -132,9 +134,8 @@ class pkgProblemResolver						/*{{{*/
    // Try to resolve problems only by using keep
    bool ResolveByKeep();
 
-   // Install all protected packages   
-   void InstallProtect();   
-   
+   __deprecated void InstallProtect();
+
    pkgProblemResolver(pkgDepCache *Cache);
    ~pkgProblemResolver();
 };

+ 8 - 0
apt-pkg/cdrom.cc

@@ -829,6 +829,14 @@ bool pkgCdrom::Add(pkgCdromStatus *log)					/*{{{*/
       log->Update(msg.str());
       log->Update(_("Copying package lists..."), STEP_COPY);
    }
+
+   // check for existence and possibly create state directory for copying
+   string const listDir = _config->FindDir("Dir::State::lists");
+   string const partialListDir = listDir + "partial/";
+   if (CreateAPTDirectoryIfNeeded(_config->FindDir("Dir::State"), partialListDir) == false &&
+       CreateAPTDirectoryIfNeeded(listDir, partialListDir) == false)
+      return _error->Errno("cdrom", _("List directory %spartial is missing."), listDir.c_str());
+
    // take care of the signatures and copy them if they are ok
    // (we do this before PackageCopy as it modifies "List" and "SourceList")
    SigVerify SignVerify;

+ 35 - 6
apt-pkg/contrib/error.h

@@ -123,6 +123,25 @@ public:									/*{{{*/
 	bool InsertErrno(MsgType const &type, const char* Function,
 			 const char* Description,...) __like_printf(4) __cold;
 
+	/** \brief adds an errno message with the given type
+	 *
+	 * args needs to be initialized with va_start and terminated
+	 * with va_end by the caller. msgSize is also an out-parameter
+	 * in case the msgSize was not enough to store the complete message.
+	 *
+	 * \param type of the error message
+	 * \param Function which failed
+	 * \param Description is the format string for args
+	 * \param args list from a printf-like function
+	 * \param errsv is the errno the error is for
+	 * \param msgSize is the size of the char[] used to store message
+	 * \return true if the message was added, false if not - the caller
+	 * should call this method again in that case
+	 */
+	bool InsertErrno(MsgType type, const char* Function,
+			 const char* Description, va_list &args,
+			 int const errsv, size_t &msgSize);
+
 	/** \brief add an fatal error message to the list
 	 *
 	 *  Most of the stuff we consider as "error" is also "fatal" for
@@ -185,6 +204,22 @@ public:									/*{{{*/
 	 */
 	bool Insert(MsgType const &type, const char* Description,...) __like_printf(3) __cold;
 
+	/** \brief adds an error message with the given type
+	 *
+	 * args needs to be initialized with va_start and terminated
+	 * with va_end by the caller. msgSize is also an out-parameter
+	 * in case the msgSize was not enough to store the complete message.
+	 *
+	 * \param type of the error message
+	 * \param Description is the format string for args
+	 * \param args list from a printf-like function
+	 * \param msgSize is the size of the char[] used to store message
+	 * \return true if the message was added, false if not - the caller
+	 * should call this method again in that case
+	 */
+	bool Insert(MsgType type, const char* Description,
+			 va_list &args, size_t &msgSize) __cold;
+
 	/** \brief is an error in the list?
 	 *
 	 *  \return \b true if an error is included in the list, \b false otherwise
@@ -305,12 +340,6 @@ private:								/*{{{*/
 	};
 
 	std::list<MsgStack> Stacks;
-
-	bool InsertErrno(MsgType type, const char* Function,
-			 const char* Description, va_list &args,
-			 int const errsv, size_t &msgSize);
-	bool Insert(MsgType type, const char* Description,
-			 va_list &args, size_t &msgSize);
 									/*}}}*/
 };
 									/*}}}*/

+ 91 - 100
apt-pkg/contrib/fileutl.cc

@@ -184,7 +184,8 @@ bool RunScripts(const char *Cnf)
 /* The caller is expected to set things so that failure causes erasure */
 bool CopyFile(FileFd &From,FileFd &To)
 {
-   if (From.IsOpen() == false || To.IsOpen() == false)
+   if (From.IsOpen() == false || To.IsOpen() == false ||
+	 From.Failed() == true || To.Failed() == true)
       return false;
    
    // Buffered copy between fds
@@ -861,7 +862,7 @@ bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress,
       return Open(FileName, ReadOnly, Gzip, Perms);
 
    if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
-      return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
+      return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
 
    std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
    std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
@@ -914,17 +915,17 @@ bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress,
       case Auto:
       case Extension:
 	 // Unreachable
-	 return _error->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
+	 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
       }
       for (; compressor != compressors.end(); ++compressor)
 	 if (compressor->Name == name)
 	    break;
       if (compressor == compressors.end())
-	 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+	 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
    }
 
    if (compressor == compressors.end())
-      return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
+      return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
    return Open(FileName, Mode, *compressor, Perms);
 }
 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
@@ -933,9 +934,9 @@ bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Co
    Flags = AutoClose;
 
    if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
-      return _error->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
+      return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
    if ((Mode & ReadWrite) == 0)
-      return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
+      return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
 
    if ((Mode & Atomic) == Atomic)
    {
@@ -981,7 +982,7 @@ bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Co
 	 close (iFd);
 	 iFd = -1;
       }
-      return _error->Errno("open",_("Could not open file %s"), FileName.c_str());
+      return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
    }
 
    SetCloseExec(iFd,true);
@@ -1010,14 +1011,19 @@ bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compre
    case Xz: name = "xz"; break;
    case Auto:
    case Extension:
-      return _error->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
+      if (AutoClose == true && Fd != -1)
+	 close(Fd);
+      return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
    }
    for (; compressor != compressors.end(); ++compressor)
       if (compressor->Name == name)
 	 break;
    if (compressor == compressors.end())
-      return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
-
+   {
+      if (AutoClose == true && Fd != -1)
+	 close(Fd);
+      return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+   }
    return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
 }
 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
@@ -1039,11 +1045,21 @@ bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration:
    else
       iFd = Fd;
    this->FileName = "";
-   if (OpenInternDescriptor(Mode, compressor) == false)
+   if (Fd == -1 || OpenInternDescriptor(Mode, compressor) == false)
    {
-      if (AutoClose)
+      if (iFd != -1 && (
+#ifdef HAVE_ZLIB
+	compressor.Name == "gzip" ||
+#endif
+#ifdef HAVE_BZ2
+	compressor.Name == "bzip2" ||
+#endif
+	AutoClose == true))
+      {
 	 close (iFd);
-      return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
+	 iFd = -1;
+      }
+      return FileFdError(_("Could not open file descriptor %d"), Fd);
    }
    return true;
 }
@@ -1105,10 +1121,7 @@ bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::C
       ExecWait(d->compressor_pid, "FileFdCompressor", true);
 
    if ((Mode & ReadWrite) == ReadWrite)
-   {
-      Flags |= Fail;
-      return _error->Error("ReadWrite mode is not supported for file %s", FileName.c_str());
-   }
+      return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
 
    bool const Comp = (Mode & WriteOnly) == WriteOnly;
    if (Comp == false)
@@ -1131,10 +1144,7 @@ bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::C
    // Create a data pipe
    int Pipe[2] = {-1,-1};
    if (pipe(Pipe) != 0)
-   {
-      Flags |= Fail;
-      return _error->Errno("pipe",_("Failed to create subprocess IPC"));
-   }
+      return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
    for (int J = 0; J != 2; J++)
       SetCloseExec(Pipe[J],true);
 
@@ -1244,14 +1254,13 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
       {
 	 if (errno == EINTR)
 	    continue;
-	 Flags |= Fail;
 #ifdef HAVE_ZLIB
 	 if (d != NULL && d->gz != NULL)
 	 {
 	    int err;
 	    char const * const errmsg = gzerror(d->gz, &err);
 	    if (err != Z_ERRNO)
-	       return _error->Error("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
+	       return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
 	 }
 #endif
 #ifdef HAVE_BZ2
@@ -1260,10 +1269,10 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
 	    int err;
 	    char const * const errmsg = BZ2_bzerror(d->bz2, &err);
 	    if (err != BZ_IO_ERROR)
-	       return _error->Error("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
+	       return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
 	 }
 #endif
-	 return _error->Errno("read",_("Read error"));
+	 return FileFdErrno("read",_("Read error"));
       }
       
       To = (char *)To + Res;
@@ -1284,9 +1293,8 @@ bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
       Flags |= HitEof;
       return true;
    }
-   
-   Flags |= Fail;
-   return _error->Error(_("read, still have %llu to read but none left"), Size);
+
+   return FileFdError(_("read, still have %llu to read but none left"), Size);
 }
 									/*}}}*/
 // FileFd::ReadLine - Read a complete line from the file		/*{{{*/
@@ -1342,14 +1350,13 @@ bool FileFd::Write(const void *From,unsigned long long Size)
 	 continue;
       if (Res < 0)
       {
-	 Flags |= Fail;
 #ifdef HAVE_ZLIB
 	 if (d != NULL && d->gz != NULL)
 	 {
 	    int err;
 	    char const * const errmsg = gzerror(d->gz, &err);
 	    if (err != Z_ERRNO)
-	       return _error->Error("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
+	       return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
 	 }
 #endif
 #ifdef HAVE_BZ2
@@ -1358,10 +1365,10 @@ bool FileFd::Write(const void *From,unsigned long long Size)
 	    int err;
 	    char const * const errmsg = BZ2_bzerror(d->bz2, &err);
 	    if (err != BZ_IO_ERROR)
-	       return _error->Error("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
+	       return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
 	 }
 #endif
-	 return _error->Errno("write",_("Write error"));
+	 return FileFdErrno("write",_("Write error"));
       }
       
       From = (char *)From + Res;
@@ -1373,9 +1380,8 @@ bool FileFd::Write(const void *From,unsigned long long Size)
    
    if (Size == 0)
       return true;
-   
-   Flags |= Fail;
-   return _error->Error(_("write, still have %llu to write but couldn't"), Size);
+
+   return FileFdError(_("write, still have %llu to write but couldn't"), Size);
 }
 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
 {
@@ -1419,10 +1425,7 @@ bool FileFd::Seek(unsigned long long To)
 	 return Skip(To - seekpos);
 
       if ((d->openmode & ReadOnly) != ReadOnly)
-      {
-	 Flags |= Fail;
-	 return _error->Error("Reopen is only implemented for read-only files!");
-      }
+	 return FileFdError("Reopen is only implemented for read-only files!");
 #ifdef HAVE_BZ2
      if (d->bz2 != NULL) 
      {
@@ -1443,17 +1446,11 @@ bool FileFd::Seek(unsigned long long To)
 	    if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
 	       iFd = d->compressed_fd;
 	 if (iFd < 0)
-	 {
-	    Flags |= Fail;
-	    return _error->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
-	 }
+	    return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
       }
 
       if (OpenInternDescriptor(d->openmode, d->compressor) == false)
-      {
-	 Flags |= Fail;
-	 return _error->Error("Seek on file %s because it couldn't be reopened", FileName.c_str());
-      }
+	 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
 
       if (To != 0)
 	 return Skip(To);
@@ -1469,10 +1466,7 @@ bool FileFd::Seek(unsigned long long To)
 #endif
       res = lseek(iFd,To,SEEK_SET);
    if (res != (signed)To)
-   {
-      Flags |= Fail;
-      return _error->Error("Unable to seek to %llu", To);
-   }
+      return FileFdError("Unable to seek to %llu", To);
 
    if (d != NULL)
       d->seekpos = To;
@@ -1496,10 +1490,7 @@ bool FileFd::Skip(unsigned long long Over)
       {
 	 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
 	 if (Read(buffer, toread) == false)
-	 {
-	    Flags |= Fail;
-	    return _error->Error("Unable to seek ahead %llu",Over);
-	 }
+	    return FileFdError("Unable to seek ahead %llu",Over);
 	 Over -= toread;
       }
       return true;
@@ -1513,10 +1504,7 @@ bool FileFd::Skip(unsigned long long Over)
 #endif
       res = lseek(iFd,Over,SEEK_CUR);
    if (res < 0)
-   {
-      Flags |= Fail;
-      return _error->Error("Unable to seek ahead %llu",Over);
-   }
+      return FileFdError("Unable to seek ahead %llu",Over);
    if (d != NULL)
       d->seekpos = res;
 
@@ -1530,17 +1518,11 @@ bool FileFd::Truncate(unsigned long long To)
 {
 #if defined HAVE_ZLIB || defined HAVE_BZ2
    if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
-   {
-      Flags |= Fail;
-      return _error->Error("Truncating compressed files is not implemented (%s)", FileName.c_str());
-   }
+      return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
 #endif
    if (ftruncate(iFd,To) != 0)
-   {
-      Flags |= Fail;
-      return _error->Error("Unable to truncate to %llu",To);
-   }
-   
+      return FileFdError("Unable to truncate to %llu",To);
+
    return true;
 }
 									/*}}}*/
@@ -1568,10 +1550,7 @@ unsigned long long FileFd::Tell()
 #endif
      Res = lseek(iFd,0,SEEK_CUR);
    if (Res == (off_t)-1)
-   {
-      Flags |= Fail;
-      _error->Errno("lseek","Failed to determine the current file position");
-   }
+      FileFdErrno("lseek","Failed to determine the current file position");
    if (d != NULL)
       d->seekpos = Res;
    return Res;
@@ -1584,10 +1563,7 @@ unsigned long long FileFd::FileSize()
 {
    struct stat Buf;
    if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
-   {
-      Flags |= Fail;
-      return _error->Errno("fstat","Unable to determine the file size");
-   }
+      return FileFdErrno("fstat","Unable to determine the file size");
 
    // for compressor pipes st_size is undefined and at 'best' zero
    if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
@@ -1597,10 +1573,7 @@ unsigned long long FileFd::FileSize()
       if (d != NULL)
 	 d->pipe = true;
       if (stat(FileName.c_str(), &Buf) != 0)
-      {
-	 Flags |= Fail;
-	 return _error->Errno("stat","Unable to determine the file size");
-      }
+	 return FileFdErrno("stat","Unable to determine the file size");
    }
 
    return Buf.st_size;
@@ -1642,16 +1615,10 @@ unsigned long long FileFd::Size()
 	* bits of the file */
        // FIXME: Size for gz-files is limited by 32bit… no largefile support
        if (lseek(iFd, -4, SEEK_END) < 0)
-       {
-	  Flags |= Fail;
-	  return _error->Errno("lseek","Unable to seek to end of gzipped file");
-       }
+	  return FileFdErrno("lseek","Unable to seek to end of gzipped file");
        size = 0L;
        if (read(iFd, &size, 4) != 4)
-       {
-	  Flags |= Fail;
-	  return _error->Errno("read","Unable to read original size of gzipped file");
-       }
+	  return FileFdErrno("read","Unable to read original size of gzipped file");
 
 #ifdef WORDS_BIGENDIAN
        uint32_t tmp_size = size;
@@ -1661,10 +1628,7 @@ unsigned long long FileFd::Size()
 #endif
 
        if (lseek(iFd, oldPos, SEEK_SET) < 0)
-       {
-	  Flags |= Fail;
-	  return _error->Errno("lseek","Unable to seek in gzipped file");
-       }
+	  return FileFdErrno("lseek","Unable to seek in gzipped file");
 
        return size;
    }
@@ -1681,8 +1645,7 @@ time_t FileFd::ModificationTime()
    struct stat Buf;
    if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
    {
-      Flags |= Fail;
-      _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
+      FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
       return 0;
    }
 
@@ -1695,8 +1658,7 @@ time_t FileFd::ModificationTime()
 	 d->pipe = true;
       if (stat(FileName.c_str(), &Buf) != 0)
       {
-	 Flags |= Fail;
-	 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
+	 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
 	 return 0;
       }
    }
@@ -1752,11 +1714,40 @@ bool FileFd::Close()
 bool FileFd::Sync()
 {
    if (fsync(iFd) != 0)
+      return FileFdErrno("sync",_("Problem syncing the file"));
+   return true;
+}
+									/*}}}*/
+// FileFd::FileFdErrno - set Fail and call _error->Errno		*{{{*/
+bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
+{
+   Flags |= Fail;
+   va_list args;
+   size_t msgSize = 400;
+   int const errsv = errno;
+   while (true)
    {
-      Flags |= Fail;
-      return _error->Errno("sync",_("Problem syncing the file"));
+      va_start(args,Description);
+      if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
+	 break;
+      va_end(args);
    }
-   return true;
+   return false;
+}
+									/*}}}*/
+// FileFd::FileFdError - set Fail and call _error->Error		*{{{*/
+bool FileFd::FileFdError(const char *Description,...) {
+   Flags |= Fail;
+   va_list args;
+   size_t msgSize = 400;
+   while (true)
+   {
+      va_start(args,Description);
+      if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
+	 break;
+      va_end(args);
+   }
+   return false;
 }
 									/*}}}*/
 

+ 4 - 0
apt-pkg/contrib/fileutl.h

@@ -149,6 +149,10 @@ class FileFd
    private:
    FileFdPrivate* d;
    bool OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor);
+
+   // private helpers to set Fail flag and call _error->Error
+   bool FileFdErrno(const char* Function, const char* Description,...) __like_printf(3) __cold;
+   bool FileFdError(const char* Description,...) __like_printf(2) __cold;
 };
 
 bool RunScripts(const char *Cnf);

+ 1 - 1
apt-pkg/contrib/gpgv.cc

@@ -154,7 +154,7 @@ void ExecGPGV(std::string const &File, std::string const &FileGPG,
 	 if (sigFd != -1)
 	    unlink(data);
 	 ioprintf(std::cerr, "Splitting up %s into data and signature failed", File.c_str());
-	 exit(EINTERNAL);
+	 exit(112);
       }
       Args.push_back(sig);
       Args.push_back(data);

+ 12 - 3
apt-pkg/contrib/gpgv.h

@@ -23,9 +23,18 @@
 /** \brief generates and run the command to verify a file with gpgv
  *
  * If File and FileSig specify the same file it is assumed that we
- * deal with a clear-signed message. In that case the file will be
- * rewritten to be in a good-known format without uneeded whitespaces
- * and additional messages (unsigned or signed).
+ * deal with a clear-signed message. Note that the method will accept
+ * and validate files which include additional (unsigned) messages
+ * without complaining. Do NOT open files accepted by this method
+ * for reading. Use #OpenMaybeClearSignedFile to access the message
+ * instead to ensure you are only reading signed data.
+ *
+ * The method does not return, but has some noteable exit-codes:
+ * 111 signals an internal error like the inability to execute gpgv,
+ * 112 indicates a clear-signed file which doesn't include a message,
+ *  which can happen if APT is run while on a network requiring
+ *  authentication before usage (e.g. in hotels)
+ * All other exit-codes are passed-through from gpgv.
  *
  * @param File is the message (unsigned or clear-signed)
  * @param FileSig is the signature (detached or clear-signed)

+ 6 - 6
apt-pkg/contrib/sha2_internal.cc

@@ -632,7 +632,7 @@ void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
 	}
 
 	/* Clean up state data: */
-	MEMSET_BZERO(context, sizeof(context));
+	MEMSET_BZERO(context, sizeof(*context));
 	usedspace = 0;
 }
 
@@ -653,7 +653,7 @@ char *SHA256_End(SHA256_CTX* context, char buffer[]) {
 		}
 		*buffer = (char)0;
 	} else {
-		MEMSET_BZERO(context, sizeof(context));
+		MEMSET_BZERO(context, sizeof(*context));
 	}
 	MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
 	return buffer;
@@ -969,7 +969,7 @@ void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
 	}
 
 	/* Zero out state data */
-	MEMSET_BZERO(context, sizeof(context));
+	MEMSET_BZERO(context, sizeof(*context));
 }
 
 char *SHA512_End(SHA512_CTX* context, char buffer[]) {
@@ -989,7 +989,7 @@ char *SHA512_End(SHA512_CTX* context, char buffer[]) {
 		}
 		*buffer = (char)0;
 	} else {
-		MEMSET_BZERO(context, sizeof(context));
+		MEMSET_BZERO(context, sizeof(*context));
 	}
 	MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);
 	return buffer;
@@ -1044,7 +1044,7 @@ void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
 	}
 
 	/* Zero out state data */
-	MEMSET_BZERO(context, sizeof(context));
+	MEMSET_BZERO(context, sizeof(*context));
 }
 
 char *SHA384_End(SHA384_CTX* context, char buffer[]) {
@@ -1064,7 +1064,7 @@ char *SHA384_End(SHA384_CTX* context, char buffer[]) {
 		}
 		*buffer = (char)0;
 	} else {
-		MEMSET_BZERO(context, sizeof(context));
+		MEMSET_BZERO(context, sizeof(*context));
 	}
 	MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);
 	return buffer;

+ 3 - 2
apt-pkg/contrib/strutl.cc

@@ -758,7 +758,8 @@ bool ReadMessages(int Fd, vector<string> &List)
       // Look for the end of the message
       for (char *I = Buffer; I + 1 < End; I++)
       {
-	 if (I[0] != '\n' || I[1] != '\n')
+	 if (I[1] != '\n' ||
+	       (I[0] != '\n' && strncmp(I, "\r\n\r\n", 4) != 0))
 	    continue;
 	 
 	 // Pull the message out
@@ -766,7 +767,7 @@ bool ReadMessages(int Fd, vector<string> &List)
 	 PartialMessage += Message;
 
 	 // Fix up the buffer
-	 for (; I < End && *I == '\n'; I++);
+	 for (; I < End && (*I == '\n' || *I == '\r'); ++I);
 	 End -= I-Buffer;	 
 	 memmove(Buffer,I,End-Buffer);
 	 I = Buffer;

+ 5 - 1
apt-pkg/deb/deblistparser.cc

@@ -219,8 +219,12 @@ MD5SumValue debListParser::Description_md5()
    string const value = Section.FindS("Description-md5");
    if (value.empty() == true)
    {
+      std::string const desc = Description() + "\n";
+      if (desc == "\n")
+	 return MD5SumValue();
+
       MD5Summation md5;
-      md5.Add((Description() + "\n").c_str());
+      md5.Add(desc.c_str());
       return md5.Result();
    }
    else if (likely(value.size() == 32))

+ 14 - 6
apt-pkg/depcache.cc

@@ -1004,7 +1004,7 @@ struct CompareProviders {
       }
       // higher priority seems like a good idea
       if (AV->Priority != BV->Priority)
-	 return AV->Priority < BV->Priority;
+	 return AV->Priority > BV->Priority;
       // prefer native architecture
       if (strcmp(A.Arch(), B.Arch()) != 0)
       {
@@ -1200,16 +1200,23 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
 	    verlist.insert(Cand);
 	 }
 	 CompareProviders comp(Start);
-	 APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp);
 
-	 if (InstVer != verlist.end())
-	 {
+	 do {
+	    APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp);
+
+	    if (InstVer == verlist.end())
+	       break;
+
 	    pkgCache::PkgIterator InstPkg = InstVer.ParentPkg();
 	    if(DebugAutoInstall == true)
 	       std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name()
 			 << " as " << Start.DepType() << " of " << Pkg.Name()
 			 << std::endl;
-	    MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps);
+	    if (MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps) == false)
+	    {
+	       verlist.erase(InstVer);
+	       continue;
+	    }
 	    // now check if we should consider it a automatic dependency or not
 	    if(InstPkg->CurrentVer == 0 && Pkg->Section != 0 && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section()))
 	    {
@@ -1218,7 +1225,8 @@ bool pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
                             << Start.DepType() << " of pkg in APT::Never-MarkAuto-Sections)" << std::endl;
 	       MarkAuto(InstPkg, false);
 	    }
-	 }
+	    break;
+	 } while(true);
 	 continue;
       }
       /* Negative dependencies have no or-group

+ 2 - 4
apt-pkg/indexcopy.cc

@@ -544,11 +544,9 @@ bool SigVerify::CopyMetaIndex(string CDROM, string CDName,		/*{{{*/
       FileFd Rel;
       Target.Open(TargetF,FileFd::WriteAtomic);
       Rel.Open(prefix + file,FileFd::ReadOnly);
-      if (_error->PendingError() == true)
-	 return false;
       if (CopyFile(Rel,Target) == false)
-	 return false;
-   
+	 return _error->Error("Copying of '%s' for '%s' from '%s' failed", file.c_str(), CDName.c_str(), prefix.c_str());
+
       return true;
 }
 									/*}}}*/

+ 105 - 119
apt-pkg/orderlist.cc

@@ -301,9 +301,8 @@ bool pkgOrderList::OrderConfigure()
 /* Higher scores order earlier */
 int pkgOrderList::Score(PkgIterator Pkg)
 {
-   static int const ScoreDelete = _config->FindI("OrderList::Score::Delete", 500);
-
-   // Removal is always done first
+   // Removals should be done after we dealt with essentials
+   static int const ScoreDelete = _config->FindI("OrderList::Score::Delete", 100);
    if (Cache[Pkg].Delete() == true)
       return ScoreDelete;
 
@@ -893,149 +892,136 @@ bool pkgOrderList::DepConfigure(DepIterator D)
 									/*}}}*/
 // OrderList::DepRemove - Removal ordering				/*{{{*/
 // ---------------------------------------------------------------------
-/* Removal visits all reverse depends. It considers if the dependency
-   of the Now state version to see if it is okay with removing this
-   package. This check should always fail, but is provided for symetery
-   with the other critical handlers.
- 
-   Loops are preprocessed and logged. Removal loops can also be
-   detected in the critical handler. They are characterized by an
-   old version of A depending on B but the new version of A conflicting
-   with B, thus either A or B must break to install. */
-bool pkgOrderList::DepRemove(DepIterator D)
+/* Checks all given dependencies if they are broken by the remove of a
+   package and if so fix it by visiting another provider or or-group
+   member to ensure that the dependee keeps working which is especially
+   important for Immediate packages like e.g. those depending on an
+   awk implementation. If the dependency can't be fixed with another
+   package this means an upgrade of the package will solve the problem. */
+bool pkgOrderList::DepRemove(DepIterator Broken)
 {
-   if (D.Reverse() == false)
+   if (Broken.Reverse() == false)
       return true;
-   for (; D.end() == false; ++D)
-      if (D->Type == pkgCache::Dep::Depends || D->Type == pkgCache::Dep::PreDepends)
+
+   for (; Broken.end() == false; ++Broken)
+   {
+      if (Broken->Type != pkgCache::Dep::Depends &&
+	    Broken->Type != pkgCache::Dep::PreDepends)
+	 continue;
+
+      PkgIterator BrokenPkg = Broken.ParentPkg();
+      // uninstalled packages can't break via a remove
+      if (BrokenPkg->CurrentVer == 0)
+	 continue;
+
+      // if its already added, we can't do anything useful
+      if (IsFlag(BrokenPkg, AddPending) == true || IsFlag(BrokenPkg, Added) == true)
+	 continue;
+
+      // if the dependee is going to be removed, visit it now
+      if (Cache[BrokenPkg].Delete() == true)
+	 return VisitNode(BrokenPkg, "Remove-Dependee");
+
+      // The package stays around, so find out how this is possible
+      for (DepIterator D = BrokenPkg.CurrentVer().DependsList(); D.end() == false;)
       {
-	 // Duplication elimination, consider the current version only
-	 if (D.ParentPkg().CurrentVer() != D.ParentVer())
+	 // only important or-groups need fixing
+	 if (D->Type != pkgCache::Dep::Depends &&
+	       D->Type != pkgCache::Dep::PreDepends)
+	 {
+	    ++D;
 	    continue;
+	 }
 
-	 /* We wish to see if the dep on the parent package is okay
-	    in the removed (install) state of the target pkg. */
-	 bool tryFixDeps = false;
-	 if (CheckDep(D) == true)
+	 // Start is the beginning of the or-group, D is the first one after or
+	 DepIterator Start = D;
+	 bool foundBroken = false;
+	 for (bool LastOR = true; D.end() == false && LastOR == true; ++D)
 	 {
-	    // We want to catch loops with the code below.
-	    if (IsFlag(D.ParentPkg(),AddPending) == false)
-	       continue;
+	    LastOR = (D->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or;
+	    if (D == Broken)
+	       foundBroken = true;
 	 }
-	 else
-	    tryFixDeps = true;
 
-	 // This is the loop detection
-	 if (IsFlag(D.ParentPkg(),Added) == true || 
-	     IsFlag(D.ParentPkg(),AddPending) == true)
-	 {
-	    if (IsFlag(D.ParentPkg(),AddPending) == true)
-	       AddLoop(D);
+	 // this or-group isn't the broken one: keep searching
+	 if (foundBroken == false)
 	    continue;
+
+	 // iterate over all members of the or-group searching for a ready replacement
+	 bool readyReplacement = false;
+	 for (DepIterator OrMember = Start; OrMember != D && readyReplacement == false; ++OrMember)
+	 {
+	    Version ** Replacements = OrMember.AllTargets();
+	    for (Version **R = Replacements; *R != 0; ++R)
+	    {
+	       VerIterator Ver(Cache,*R);
+	       // only currently installed packages can be a replacement
+	       PkgIterator RPkg = Ver.ParentPkg();
+	       if (RPkg.CurrentVer() != Ver)
+		  continue;
+
+	       // packages going to be removed can't be a replacement
+	       if (Cache[RPkg].Delete() == true)
+		  continue;
+
+	       readyReplacement = true;
+	       break;
+	    }
+	    delete[] Replacements;
 	 }
 
-	 if (tryFixDeps == true)
+	 // something else is ready to take over, do nothing
+	 if (readyReplacement == true)
+	    continue;
+
+	 // see if we can visit a replacement
+	 bool visitReplacement = false;
+	 for (DepIterator OrMember = Start; OrMember != D && visitReplacement == false; ++OrMember)
 	 {
-	    for (pkgCache::DepIterator F = D.ParentPkg().CurrentVer().DependsList();
-		 F.end() == false; ++F)
+	    Version ** Replacements = OrMember.AllTargets();
+	    for (Version **R = Replacements; *R != 0; ++R)
 	    {
-	       if (F->Type != pkgCache::Dep::Depends && F->Type != pkgCache::Dep::PreDepends)
+	       VerIterator Ver(Cache,*R);
+	       // consider only versions we plan to install
+	       PkgIterator RPkg = Ver.ParentPkg();
+	       if (Cache[RPkg].Install() == false || Cache[RPkg].InstallVer != Ver)
 		  continue;
-	       // Check the Providers
-	       if (F.TargetPkg()->ProvidesList != 0)
-	       {
-		  pkgCache::PrvIterator Prov = F.TargetPkg().ProvidesList();
-		  for (; Prov.end() == false; ++Prov)
-		  {
-		     pkgCache::PkgIterator const P = Prov.OwnerPkg();
-		     if (IsFlag(P, InList) == true &&
-			 IsFlag(P, AddPending) == true &&
-			 IsFlag(P, Added) == false &&
-			 Cache[P].InstallVer == 0)
-			break;
-		  }
-		  if (Prov.end() == false)
-		     for (pkgCache::PrvIterator Prv = F.TargetPkg().ProvidesList();
-			  Prv.end() == false; ++Prv)
-		     {
-			pkgCache::PkgIterator const P = Prv.OwnerPkg();
-			if (IsFlag(P, InList) == true &&
-			    IsFlag(P, AddPending) == false &&
-			    Cache[P].InstallVer != 0 &&
-			    VisitNode(P, "Remove-P") == true)
-			{
-			   Flag(P, Immediate);
-			   tryFixDeps = false;
-			   break;
-			}
-		     }
-		  if (tryFixDeps == false)
-		     break;
-	       }
 
-	       // Check for Or groups
-	       if ((F->CompareOp & pkgCache::Dep::Or) != pkgCache::Dep::Or)
+	       // loops are not going to help us, so don't create them
+	       if (IsFlag(RPkg, AddPending) == true)
 		  continue;
-	       // Lets see if the package is part of the Or group
-	       pkgCache::DepIterator S = F;
-	       for (; S.end() == false; ++S)
+
+	       if (IsMissing(RPkg) == true)
+		  continue;
+
+	       visitReplacement = true;
+	       if (IsFlag(BrokenPkg, Immediate) == false)
 	       {
-		  if (S.TargetPkg() == D.TargetPkg())
+		  if (VisitNode(RPkg, "Remove-Rep") == true)
 		     break;
-		  if ((S->CompareOp & pkgCache::Dep::Or) != pkgCache::Dep::Or ||
-		      CheckDep(S)) // Or group is satisfied by another package
-		     for (;S.end() == false; ++S);
 	       }
-	       if (S.end() == true)
-		  continue;
-	       // skip to the end of the or group
-	       for (;S.end() == false && (S->CompareOp & pkgCache::Dep::Or) == pkgCache::Dep::Or; ++S);
-	       ++S;
-	       // The soon to be removed is part of the Or group
-	       // start again in the or group and find something which will serve as replacement
-	       for (; F.end() == false && F != S; ++F)
+	       else
 	       {
-		  if (IsFlag(F.TargetPkg(), InList) == true &&
-		      IsFlag(F.TargetPkg(), AddPending) == false &&
-		      Cache[F.TargetPkg()].InstallVer != 0 &&
-		      VisitNode(F.TargetPkg(), "Remove-Target") == true)
-		  {
-		     Flag(F.TargetPkg(), Immediate);
-		     tryFixDeps = false;
+		  Flag(RPkg, Immediate);
+		  if (VisitNode(RPkg, "Remove-ImmRep") == true)
 		     break;
-		  }
-		  else if (F.TargetPkg()->ProvidesList != 0)
-		  {
-		     pkgCache::PrvIterator Prv = F.TargetPkg().ProvidesList();
-		     for (; Prv.end() == false; ++Prv)
-		     {
-			if (IsFlag(Prv.OwnerPkg(), InList) == true &&
-			    IsFlag(Prv.OwnerPkg(), AddPending) == false &&
-			    Cache[Prv.OwnerPkg()].InstallVer != 0 &&
-			    VisitNode(Prv.OwnerPkg(), "Remove-Owner") == true)
-			{
-			   Flag(Prv.OwnerPkg(), Immediate);
-			   tryFixDeps = false;
-			   break;
-			}
-		     }
-		     if (Prv.end() == false)
-			break;
-		  }
 	       }
-	       if (tryFixDeps == false)
-		  break;
+	       visitReplacement = false;
 	    }
+	    delete[] Replacements;
 	 }
-
-	 // Skip over missing files
-	 if (IsMissing(D.ParentPkg()) == true)
+	 if (visitReplacement == true)
 	    continue;
-	 
-	 if (VisitNode(D.ParentPkg(), "Remove-Parent") == false)
+
+	 // the broken package in current version can't be fixed, so install new version
+	 if (IsMissing(BrokenPkg) == true)
+	    break;
+
+	 if (VisitNode(BrokenPkg, "Remove-Upgrade") == false)
 	    return false;
       }
-   
+   }
+
    return true;
 }
 									/*}}}*/

+ 71 - 46
apt-pkg/packagemanager.cc

@@ -340,6 +340,7 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
    bool Bad = false, Changed = false;
    const unsigned int max_loops = _config->FindI("APT::pkgPackageManager::MaxLoopCount", 500);
    unsigned int i=0;
+   std::list<DepIterator> needConfigure;
    do
    {
       Changed = false;
@@ -353,7 +354,7 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	    continue;
 	 Bad = true;
 
-	 // Search for dependencies which are unpacked but aren't configured yet (maybe loops)
+	 // Check for dependencies that have not been unpacked, probably due to loops.
 	 for (DepIterator Cur = Start; true; ++Cur)
 	 {
 	    SPtrArray<Version *> VList = Cur.AllTargets();
@@ -373,51 +374,66 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       }
 
 	       // Check if the version that is going to be installed will satisfy the dependency
-	       if (Cache[DepPkg].InstallVer != *I)
+	       if (Cache[DepPkg].InstallVer != *I || List->IsNow(DepPkg) == false)
 		  continue;
 
-	       if (List->IsFlag(DepPkg,pkgOrderList::UnPacked))
+	       if (PkgLoop == true)
 	       {
-		  if (List->IsFlag(DepPkg,pkgOrderList::Loop) && PkgLoop)
-		  {
-		    // This dependency has already been dealt with by another SmartConfigure on Pkg
-		    Bad = false;
-		    break;
-		  }
-		  /* Check for a loop to prevent one forming
-		       If A depends on B and B depends on A, SmartConfigure will
-		       just hop between them if this is not checked. Dont remove the
-		       loop flag after finishing however as loop is already set.
-		       This means that there is another SmartConfigure call for this
-		       package and it will remove the loop flag */
+		  if (Debug)
+		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure" << std::endl;
+	          Bad = false;
+		  break;
+	       }
+	       else
+	       {
+		  if (Debug)
+		     clog << OutputInDepth(Depth) << "Unpacking " << DepPkg.FullName() << " to avoid loop " << Cur << endl;
 		  if (PkgLoop == false)
 		     List->Flag(Pkg,pkgOrderList::Loop);
-		  if (SmartConfigure(DepPkg, Depth + 1) == true)
+		  if (SmartUnPack(DepPkg, true, Depth + 1) == true)
 		  {
 		     Bad = false;
 		     if (List->IsFlag(DepPkg,pkgOrderList::Loop) == false)
-			Changed = true;
+		        Changed = true;
 		  }
 		  if (PkgLoop == false)
-		    List->RmFlag(Pkg,pkgOrderList::Loop);
-		  // If SmartConfigure was succesfull, Bad is false, so break
+		     List->RmFlag(Pkg,pkgOrderList::Loop);
 		  if (Bad == false)
 		     break;
 	       }
-	       else if (List->IsFlag(DepPkg,pkgOrderList::Configured))
-	       {
-		  Bad = false;
-		  break;
-	       }
 	    }
-	    if (Cur == End)
+
+	    if (Cur == End || Bad == false)
 	       break;
-         }
+	 }
 
 	 if (Bad == false)
 	    continue;
 
-	 // Check for dependencies that have not been unpacked, probably due to loops.
+	 needConfigure.push_back(Start);
+      }
+      if (i++ > max_loops)
+         return _error->Error("Internal error: MaxLoopCount reached in SmartUnPack (1) for %s, aborting", Pkg.FullName().c_str());
+   } while (Changed == true);
+
+   Bad = false, Changed = false, i = 0;
+   do
+   {
+      Changed = false;
+      for (std::list<DepIterator>::const_iterator D = needConfigure.begin(); D != needConfigure.end(); ++D)
+      {
+	 // Compute a single dependency element (glob or) without modifying D
+	 pkgCache::DepIterator Start, End;
+	 {
+	    pkgCache::DepIterator Discard = *D;
+	    Discard.GlobOr(Start,End);
+	 }
+
+	 if (End->Type != pkgCache::Dep::Depends)
+	    continue;
+	 Bad = true;
+
+	 // Search for dependencies which are unpacked but aren't configured yet (maybe loops)
 	 for (DepIterator Cur = Start; true; ++Cur)
 	 {
 	    SPtrArray<Version *> VList = Cur.AllTargets();
@@ -428,44 +444,53 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 	       PkgIterator DepPkg = Ver.ParentPkg();
 
 	       // Check if the version that is going to be installed will satisfy the dependency
-	       if (Cache[DepPkg].InstallVer != *I || List->IsNow(DepPkg) == false)
+	       if (Cache[DepPkg].InstallVer != *I)
 		  continue;
 
-	       if (PkgLoop == true)
-	       {
-		  if (Debug)
-		     std::clog << OutputInDepth(Depth) << "Package " << Pkg << " loops in SmartConfigure" << std::endl;
-	          Bad = false;
-		  break;
-	       }
-	       else
+	       if (List->IsFlag(DepPkg,pkgOrderList::UnPacked))
 	       {
-		  if (Debug)
-		     clog << OutputInDepth(Depth) << "Unpacking " << DepPkg.FullName() << " to avoid loop " << Cur << endl;
+		  if (List->IsFlag(DepPkg,pkgOrderList::Loop) && PkgLoop)
+		  {
+		    // This dependency has already been dealt with by another SmartConfigure on Pkg
+		    Bad = false;
+		    break;
+		  }
+		  /* Check for a loop to prevent one forming
+		       If A depends on B and B depends on A, SmartConfigure will
+		       just hop between them if this is not checked. Dont remove the
+		       loop flag after finishing however as loop is already set.
+		       This means that there is another SmartConfigure call for this
+		       package and it will remove the loop flag */
 		  if (PkgLoop == false)
 		     List->Flag(Pkg,pkgOrderList::Loop);
-		  if (SmartUnPack(DepPkg, true, Depth + 1) == true)
+		  if (SmartConfigure(DepPkg, Depth + 1) == true)
 		  {
 		     Bad = false;
 		     if (List->IsFlag(DepPkg,pkgOrderList::Loop) == false)
-		        Changed = true;
+			Changed = true;
 		  }
 		  if (PkgLoop == false)
-		     List->RmFlag(Pkg,pkgOrderList::Loop);
+		    List->RmFlag(Pkg,pkgOrderList::Loop);
+		  // If SmartConfigure was succesfull, Bad is false, so break
 		  if (Bad == false)
 		     break;
 	       }
+	       else if (List->IsFlag(DepPkg,pkgOrderList::Configured))
+	       {
+		  Bad = false;
+		  break;
+	       }
 	    }
-
-	    if (Cur == End)
+	    if (Cur == End || Bad == false)
 	       break;
-	 }
+         }
+
 
 	 if (Bad == true && Changed == false && Debug == true)
-	    std::clog << OutputInDepth(Depth) << "Could not satisfy " << Start << std::endl;
+	    std::clog << OutputInDepth(Depth) << "Could not satisfy " << *D << std::endl;
       }
       if (i++ > max_loops)
-         return _error->Error("Internal error: MaxLoopCount reached in SmartUnPack for %s, aborting", Pkg.FullName().c_str());
+         return _error->Error("Internal error: MaxLoopCount reached in SmartUnPack (2) for %s, aborting", Pkg.FullName().c_str());
    } while (Changed == true);
    
    if (Bad) {

+ 3 - 1
apt-pkg/pkgcachegen.cc

@@ -296,6 +296,8 @@ bool pkgCacheGenerator::MergeListPackage(ListParser &List, pkgCache::PkgIterator
 
    // Find the right version to write the description
    MD5SumValue CurMd5 = List.Description_md5();
+   if (CurMd5.Value().empty() == true || List.Description().empty() == true)
+      return true;
    std::string CurLang = List.DescriptionLanguage();
 
    for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
@@ -480,7 +482,7 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
 
    /* Record the Description (it is not translated) */
    MD5SumValue CurMd5 = List.Description_md5();
-   if (CurMd5.Value().empty() == true)
+   if (CurMd5.Value().empty() == true || List.Description().empty() == true)
       return true;
    std::string CurLang = List.DescriptionLanguage();
 

+ 1 - 1
buildlib/copy.mak

@@ -21,7 +21,7 @@ veryclean: veryclean/$(LOCAL)
 
 MKDIRS += $(dir $($(LOCAL)-LIST))
 
-$($(LOCAL)-LIST) : $(TO)/% : % dirs
+$($(LOCAL)-LIST) : $(TO)/% : %
 	echo Installing $< to $(@D)
 	cp $< $(@D)
 

+ 2 - 2
buildlib/library.mak

@@ -51,7 +51,7 @@ $(LIB)/lib$(LIBRARY).so: $(LIB)/lib$(LIBRARY).so.$(MAJOR).$(MINOR)
 	ln -sf $(<F) $@
 
 # The binary build rule
-$(LIB)/lib$(LIBRARY).so.$(MAJOR).$(MINOR): $($(LOCAL)-HEADERS) $($(LOCAL)-OBJS)
+$(LIB)/lib$(LIBRARY).so.$(MAJOR).$(MINOR): $($(LOCAL)-HEADERS) $($(LOCAL)-OBJS) $(LIBRARYDEPENDS)
 	-rm -f $(LIB)/lib$($(@F)-LIBRARY)*.so* 2> /dev/null
 	echo Building shared library $@
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) $(PICFLAGS) $(LFLAGS) $(LFLAGS_SO)\
@@ -61,7 +61,7 @@ $(LIB)/lib$(LIBRARY).so.$(MAJOR).$(MINOR): $($(LOCAL)-HEADERS) $($(LOCAL)-OBJS)
 
 # Compilation rules
 vpath %.cc $(SUBDIRS)
-$(OBJ)/%.opic: %.cc
+$(OBJ)/%.opic: %.cc $(LIBRARYDEPENDS)
 	echo Compiling $< to $@
 	$(CXX) -c $(INLINEDEPFLAG) $(CPPFLAGS) $(CXXFLAGS) $(PICFLAGS) -o $@ $<
 	$(DoDep)

+ 7 - 5
buildlib/po4a_manpage.mak

@@ -25,6 +25,7 @@ $(LOCAL)-LIST := $(SOURCE)
 
 # Install generation hooks
 manpages: $($(LOCAL)-LIST)
+clean: clean/$(LOCAL)
 veryclean: veryclean/$(LOCAL)
 
 apt-verbatim.ent: ../apt-verbatim.ent
@@ -37,15 +38,16 @@ manpage-style.xsl: ../manpage-style.xsl
 $($(LOCAL)-LIST) :: % : %.xml $(STYLESHEET) $(INCLUDES)
 	echo Creating man page $@
 	$(XSLTPROC) -o $@ $(STYLESHEET) $< || exit 200 # why xsltproc doesn't respect the -o flag here???
-	test -f $(subst .$(LC),,$@) || echo FIXME: xsltproc respect the -o flag now, workaround can be removed
+	test -f $(subst .$(LC),,$@) || echo 'FIXME: xsltproc respects the -o flag now, workaround can be removed'
 	mv -f $(subst .$(LC),,$@) $@
 
 # Clean rule
-.PHONY: veryclean/$(LOCAL)
+.PHONY: clean/$(LOCAL) veryclean/$(LOCAL)
+clean/$(LOCAL):
+	rm -f $($(@F)-LIST) apt.ent apt-verbatim.ent
 veryclean/$(LOCAL):
-	-rm -rf $($(@F)-LIST) apt.ent apt-verbatim.ent apt.$(LC).8 \
-		$(addsuffix .xml,$($(@F)-LIST)) \
-		offline.$(LC).sgml guide.$(LC).sgml
+	# we are nuking the directory we are working in as it is auto-generated
+	rm -rf $(shell readlink -f .)
 
 HAVE_PO4A=yes
 endif

+ 6 - 1
buildlib/podomain.mak

@@ -4,6 +4,8 @@
 # declared domain of the make file. It also arranges to set the DOMAIN 
 # CPPFLAG for the compilation.
 
+ifneq ($(APT_DOMAIN),none)
+
 MY_DOMAIN := $(PACKAGE)
 ifdef APT_DOMAIN
 $($(LOCAL)-OBJS): CPPFLAGS := $(CPPFLAGS) -DAPT_DOMAIN='"$(APT_DOMAIN)"'
@@ -13,10 +15,13 @@ endif
 MKDIRS += $(PO_DOMAINS)/$(MY_DOMAIN)
 $(PO_DOMAINS)/$(MY_DOMAIN)/$(LOCAL).$(TYPE)list: SRC := $(addprefix $(SUBDIR)/,$(SOURCE))
 $(PO_DOMAINS)/$(MY_DOMAIN)/$(LOCAL).$(TYPE)list: makefile dirs
-	(echo $(SRC) | xargs -n1 echo) > $@
+	(echo $(SRC) | xargs -n1 echo) > $@.tmp
+	cmp --silent $@.tmp $@ || mv $@.tmp $@
 startup binary program clean update-po: $(PO_DOMAINS)/$(MY_DOMAIN)/$(LOCAL).$(TYPE)list
 
 veryclean: veryclean/$(LOCAL)
 veryclean/po/$(LOCAL): LIST := $(PO_DOMAINS)/$(MY_DOMAIN)/$(LOCAL).$(TYPE)list
 veryclean/po/$(LOCAL):
 	rm -f $(LIST)
+
+endif

+ 83 - 43
cmdline/apt-cache.cc

@@ -1161,7 +1161,11 @@ bool DisplayRecord(pkgCacheFile &CacheFile, pkgCache::VerIterator V)
    }
 
    // Get a pointer to start of Description field
-   const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "Description:");
+   const unsigned char *DescP = (unsigned char*)strstr((char*)Buffer, "\nDescription");
+   if (DescP != NULL)
+      ++DescP;
+   else
+      DescP = Buffer + V.FileList()->Size;
 
    // Write all but Description
    if (fwrite(Buffer,1,DescP - Buffer,stdout) < (size_t)(DescP - Buffer))
@@ -1173,25 +1177,38 @@ bool DisplayRecord(pkgCacheFile &CacheFile, pkgCache::VerIterator V)
    // Show the right description
    pkgRecords Recs(*Cache);
    pkgCache::DescIterator Desc = V.TranslatedDescription();
-   pkgRecords::Parser &P = Recs.Lookup(Desc.FileList());
-   cout << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc();
-
-   // Find the first field after the description (if there is any)
-   for(DescP++;DescP != &Buffer[V.FileList()->Size];DescP++) 
+   if (Desc.end() == false)
    {
-      if(*DescP == '\n' && *(DescP+1) != ' ') 
+      pkgRecords::Parser &P = Recs.Lookup(Desc.FileList());
+      cout << "Description" << ( (strcmp(Desc.LanguageCode(),"") != 0) ? "-" : "" ) << Desc.LanguageCode() << ": " << P.LongDesc();
+      cout << std::endl << "Description-md5: " << Desc.md5() << std::endl;
+
+      // Find the first field after the description (if there is any)
+      while ((DescP = (unsigned char*)strchr((char*)DescP, '\n')) != NULL)
       {
-	 // write the rest of the buffer
-	 const unsigned char *end=&Buffer[V.FileList()->Size];
-	 if (fwrite(DescP,1,end-DescP,stdout) < (size_t)(end-DescP)) 
-	 {
-	    delete [] Buffer;
-	    return false;
-	 }
+	 if (DescP[1] == ' ')
+	    DescP += 2;
+	 else if (strncmp((char*)DescP, "\nDescription", strlen("\nDescription")) == 0)
+	    DescP += strlen("\nDescription");
+	 else
+	    break;
+      }
+      if (DescP != NULL)
+	 ++DescP;
+   }
+   // if we have no translation, we found a lonely Description-md5, so don't skip it
 
-	 break;
+   if (DescP != NULL)
+   {
+      // write the rest of the buffer
+      const unsigned char *end=&Buffer[V.FileList()->Size];
+      if (fwrite(DescP,1,end-DescP,stdout) < (size_t)(end-DescP))
+      {
+	 delete [] Buffer;
+	 return false;
       }
    }
+
    // write a final newline (after the description)
    cout<<endl;
    delete [] Buffer;
@@ -1203,7 +1220,7 @@ bool DisplayRecord(pkgCacheFile &CacheFile, pkgCache::VerIterator V)
 struct ExDescFile
 {
    pkgCache::DescFile *Df;
-   bool NameMatch;
+   map_ptrloc ID;
 };
 
 // Search - Perform a search						/*{{{*/
@@ -1246,37 +1263,48 @@ bool Search(CommandLine &CmdL)
       return false;
    }
    
-   ExDescFile *DFList = new ExDescFile[Cache->HeaderP->GroupCount+1];
-   memset(DFList,0,sizeof(*DFList)*Cache->HeaderP->GroupCount+1);
+   size_t const descCount = Cache->HeaderP->GroupCount + 1;
+   ExDescFile *DFList = new ExDescFile[descCount];
+   memset(DFList,0,sizeof(*DFList) * descCount);
+
+   bool PatternMatch[descCount * NumPatterns];
+   memset(PatternMatch,false,sizeof(PatternMatch));
 
    // Map versions that we want to write out onto the VerList array.
    for (pkgCache::GrpIterator G = Cache->GrpBegin(); G.end() == false; ++G)
    {
-      if (DFList[G->ID].NameMatch == true)
-	 continue;
-
-      DFList[G->ID].NameMatch = true;
-      for (unsigned I = 0; I != NumPatterns; I++)
+      size_t const PatternOffset = G->ID * NumPatterns;
+      size_t unmatched = 0, matched = 0;
+      for (unsigned I = 0; I < NumPatterns; ++I)
       {
-	 if (regexec(&Patterns[I],G.Name(),0,0,0) == 0)
-	    continue;
-	 DFList[G->ID].NameMatch = false;
-	 break;
+	 if (PatternMatch[PatternOffset + I] == true)
+	    ++matched;
+	 else if (regexec(&Patterns[I],G.Name(),0,0,0) == 0)
+	    PatternMatch[PatternOffset + I] = true;
+	 else
+	    ++unmatched;
       }
-        
-      // Doing names only, drop any that dont match..
-      if (NamesOnly == true && DFList[G->ID].NameMatch == false)
+
+      // already dealt with this package?
+      if (matched == NumPatterns)
 	 continue;
-	 
+
+      // Doing names only, drop any that don't match..
+      if (NamesOnly == true && unmatched == NumPatterns)
+	 continue;
+
       // Find the proper version to use
       pkgCache::PkgIterator P = G.FindPreferredPkg();
       if (P.end() == true)
 	 continue;
       pkgCache::VerIterator V = Plcy->GetCandidateVer(P);
       if (V.end() == false)
+      {
 	 DFList[G->ID].Df = V.TranslatedDescription().FileList();
+	 DFList[G->ID].ID = G->ID;
+      }
 
-      if (DFList[G->ID].NameMatch == false)
+      if (unmatched == NumPatterns)
 	 continue;
 
       // Include all the packages that provide matching names too
@@ -1288,33 +1316,45 @@ bool Search(CommandLine &CmdL)
 
 	 unsigned long id = Prv.OwnerPkg().Group()->ID;
 	 DFList[id].Df = V.TranslatedDescription().FileList();
-	 DFList[id].NameMatch = true;
+	 DFList[id].ID = id;
+
+	 size_t const PrvPatternOffset = id * NumPatterns;
+	 for (unsigned I = 0; I < NumPatterns; ++I)
+	    PatternMatch[PrvPatternOffset + I] = PatternMatch[PatternOffset + I];
       }
    }
-   
+
    LocalitySort(&DFList->Df,Cache->HeaderP->GroupCount,sizeof(*DFList));
 
    // Create the text record parser
    pkgRecords Recs(*Cache);
    // Iterate over all the version records and check them
-   for (ExDescFile *J = DFList; J->Df != 0; J++)
+   for (ExDescFile *J = DFList; J->Df != 0; ++J)
    {
       pkgRecords::Parser &P = Recs.Lookup(pkgCache::DescFileIterator(*Cache,J->Df));
+      size_t const PatternOffset = J->ID * NumPatterns;
 
-      if (J->NameMatch == false && NamesOnly == false)
+      if (NamesOnly == false)
       {
 	 string const LongDesc = P.LongDesc();
-	 J->NameMatch = true;
-	 for (unsigned I = 0; I != NumPatterns; I++)
+	 for (unsigned I = 0; I < NumPatterns; ++I)
 	 {
-	    if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0)
+	    if (PatternMatch[PatternOffset + I] == true)
 	       continue;
-	    J->NameMatch = false;
-	    break;
+	    else if (regexec(&Patterns[I],LongDesc.c_str(),0,0,0) == 0)
+	       PatternMatch[PatternOffset + I] = true;
 	 }
       }
-      
-      if (J->NameMatch == true)
+
+      bool matchedAll = true;
+      for (unsigned I = 0; I < NumPatterns; ++I)
+	 if (PatternMatch[PatternOffset + I] == false)
+	 {
+	    matchedAll = false;
+	    break;
+	 }
+
+      if (matchedAll == true)
       {
 	 if (ShowFull == true)
 	 {

+ 4 - 9
cmdline/apt-cdrom.cc

@@ -150,14 +150,12 @@ bool DoAdd(CommandLine &)
    bool res = true;
 
    bool AutoDetect = _config->FindB("Acquire::cdrom::AutoDetect", true);
+   unsigned int count = 0;
    if (AutoDetect && UdevCdroms.Dlopen())
-   {
-      unsigned int count = 0;
       while (AutoDetectCdrom(UdevCdroms, count))
 	 res &= cdrom.Add(&log);
-   } else {
+   if (count == 0)
       res = cdrom.Add(&log);
-   }
 
    if(res)
       cout << _("Repeat this process for the rest of the CDs in your set.") << endl;
@@ -178,15 +176,12 @@ bool DoIdent(CommandLine &)
 
    bool AutoDetect = _config->FindB("Acquire::cdrom::AutoDetect");
 
+   unsigned int count = 0;
    if (AutoDetect && UdevCdroms.Dlopen())
-   {
-      unsigned int count = 0;
       while (AutoDetectCdrom(UdevCdroms, count))
 	 res &= cdrom.Ident(ident, &log);
-   } else {
+   if (count == 0)
       return cdrom.Ident(ident, &log);
-   }
- 
    return res;
 }
 									/*}}}*/

+ 1 - 3
cmdline/apt-get.cc

@@ -1962,7 +1962,6 @@ bool DoInstall(CommandLine &CmdL)
       if (Fix != NULL)
       {
 	 // Call the scored problem resolver
-	 Fix->InstallProtect();
 	 Fix->Resolve(true);
 	 delete Fix;
       }
@@ -3123,8 +3122,7 @@ bool DoBuildDep(CommandLine &CmdL)
             }
 	 }	       
       }
-      
-      Fix.InstallProtect();
+
       if (Fix.Resolve(true) == false)
 	 _error->Discard();
       

+ 1 - 1
cmdline/makefile

@@ -14,7 +14,7 @@ include $(PROGRAM_H)
 
 # The apt-get program
 PROGRAM=apt-get
-SLIBS = -lapt-pkg -lutil $(INTLLIBS)
+SLIBS = -lapt-pkg $(INTLLIBS)
 LIB_MAKES = apt-pkg/makefile
 SOURCE = apt-get.cc acqprogress.cc
 include $(PROGRAM_H)

+ 23 - 2
debian/changelog

@@ -4,7 +4,29 @@ apt (0.9.9) unstable; urgency=low
   * improve debug output for the Debug::pkgProblemResolver and
     Debug::pkgDepCache::AutoInstall
 
- -- Michael Vogt <michael.vogt@ubuntu.com>  Tue, 02 Jul 2013 08:26:17 +0200
+  [ David Kalnischkies ]
+  * build the en manpages in subdirectory doc/en
+  * remove -ldl from cdrom and -lutil from apt-get linkage
+  * rewrite pkgOrderList::DepRemove to stop incorrect immediate setting
+    (Closes: 645713)
+  * prefer Essentials over Removals in ordering score
+  * fix priority sorting by prefering higher in MarkInstall
+  * try all providers in order if uninstallable in MarkInstall
+  * do unpacks before configures in SmartConfigure (Closes: #707578)
+  * fix support for multiple patterns in apt-cache search (Closes: #691453)
+  * set Fail flag in FileFd on all errors consistently
+  * don't explicitly init ExtractTar InFd with invalid fd
+  * OpenDescriptor should autoclose fd always on error (Closes: #704608)
+  * fail in CopyFile if the FileFds have error flag set
+  * ensure state-dir exists before coyping cdrom files
+  * fix file location for configure-index.gz in apt.conf(5) (Closes: #711921)
+  * handle missing "Description" in apt-cache show (Closes: #712435)
+  * try defaults if auto-detection failed in apt-cdrom (Closes: #712433)
+  * support \n and \r\n line endings in ReadMessages
+  * do not redownload unchanged InRelease files
+  * trigger NODATA error for invalid InRelease files (Closes: #712486)
+
+ -- David Kalnischkies <kalnischkies@gmail.com>  Sun, 09 Jun 2013 15:06:24 +0200
 
 apt (0.9.8.2) unstable; urgency=low
 
@@ -25,7 +47,6 @@ apt (0.9.8.2) unstable; urgency=low
   * Fix crash when the "mirror" method does not find any entry
     (closes: #699303)
 
-
   [ Johan Kiviniemi ]
   * cmdline/apt-key:
     - Create new keyrings with mode 0644 instead of 0600.

+ 2 - 2
debian/rules

@@ -203,7 +203,7 @@ apt: build build-manpages
 	dh_bugfiles -p$@
 	dh_lintian -p$@
 	dh_installexamples -p$@ $(BLD)/docs/examples/*
-	dh_installman -p$@ $(wildcard $(patsubst %,doc/%.[158],$(apt_MANPAGES)) $(patsubst %,doc/*/%.*.[158],$(apt_MANPAGES)))
+	dh_installman -p$@ $(wildcard $(patsubst %,doc/en/%.[158],$(apt_MANPAGES)) $(patsubst %,doc/*/%.*.[158],$(apt_MANPAGES)))
 	dh_installcron -p$@
 	dh_installdocs -p$@
 	dh_installchangelogs -p$@
@@ -253,7 +253,7 @@ apt-utils: build build-manpages
 	dh_installexamples -p$@
 
 	# Install the man pages..
-	dh_installman -p$@ $(wildcard $(patsubst %,doc/%.[158],$(apt-utils_MANPAGES)) $(patsubst %,doc/*/%.*.[158],$(apt-utils_MANPAGES)))
+	dh_installman -p$@ $(wildcard $(patsubst %,doc/en/%.[158],$(apt-utils_MANPAGES)) $(patsubst %,doc/*/%.*.[158],$(apt-utils_MANPAGES)))
 
 	dh_installchangelogs -p$@
 	dh_strip -p$@

+ 1 - 2
doc/apt.conf.5.xml

@@ -98,8 +98,7 @@ APT {
 DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
 </programlisting></informalexample>
 
-   <para>In general the sample configuration file in 
-   <filename>&docdir;examples/apt.conf</filename> &configureindex;
+   <para>In general the sample configuration file &configureindex;
    is a good guide for how it should look.</para>
 
    <para>Case is not significant in names of configuration items, so in the

+ 38 - 0
doc/en/makefile

@@ -0,0 +1,38 @@
+# -*- make -*-
+BASE=../..
+SUBDIR=doc/en
+
+# Bring in the default rules
+include ../../buildlib/defaults.mak
+
+# Do not use XMLTO, build the manpages directly with XSLTPROC
+ifdef XSLTPROC
+# generate a list of accepted man page translations
+SOURCE = $(patsubst ../%.xml,%,$(wildcard ../*.?.xml))
+INCLUDES = ../apt.ent ../apt-verbatim.ent
+STYLESHEET=../manpage-style.xsl
+
+LOCAL := manpage-$(firstword $(SOURCE))
+$(LOCAL)-LIST := $(SOURCE)
+
+# Install generation hooks
+manpages: $($(LOCAL)-LIST) apt.8
+
+$($(LOCAL)-LIST) :: % : ../%.xml $(STYLESHEET) $(INCLUDES)
+	echo Creating man page $@
+	$(XSLTPROC) -o $@ $(STYLESHEET) $<
+
+apt.8: ../apt.8
+	cp -a ../apt.8 apt.8
+
+# Clean rule
+.PHONY: clean/$(LOCAL)
+veryclean: clean/$(LOCAL)
+clean: clean/$(LOCAL)
+clean/$(LOCAL):
+	-rm -rf $($(@F)-LIST) apt.8
+endif
+
+# Chain to the manpage rule
+SOURCE = apt.8
+include $(MANPAGE_H)

+ 1 - 1
doc/examples/configure-index

@@ -225,7 +225,7 @@ Acquire
 	Verify-Peer "false";
 	SslCert "/etc/apt/some.pem";
 	CaPath  "/etc/ssl/certs";
-	Verify-Host" "true";
+	Verify-Host "true";
 	AllowRedirect  "true";
 
 	Timeout "120";

+ 19 - 53
doc/makefile

@@ -1,7 +1,6 @@
 # -*- make -*-
 BASE=..
 SUBDIR=doc
-SUBDIRS= $(dir $(wildcard */makefile))
 
 # Bring in the default rules
 include ../buildlib/defaults.mak
@@ -13,34 +12,6 @@ include $(DEBIANDOC_H)
 
 doc: manpages debiandoc
 
-# Do not use XMLTO, build the manpages directly with XSLTPROC
-ifdef XSLTPROC
-# generate a list of accepted man page translations
-SOURCE = $(patsubst %.xml,%,$(wildcard *.?.xml))
-INCLUDES = apt.ent apt-verbatim.ent
-STYLESHEET=manpage-style.xsl
-
-LOCAL := manpage-$(firstword $(SOURCE))
-$(LOCAL)-LIST := $(SOURCE)
-
-# Install generation hooks
-manpages: $($(LOCAL)-LIST)
-
-$($(LOCAL)-LIST) :: % : %.xml $(STYLESHEET) $(INCLUDES)
-	echo Creating man page $@
-	$(XSLTPROC) -o $@ $(STYLESHEET) $<
-
-# Clean rule
-.PHONY: veryclean/$(LOCAL)
-veryclean: veryclean/$(LOCAL)
-veryclean/$(LOCAL):
-	-rm -rf $($(@F)-LIST)
-endif
-
-# Chain to the manpage rule
-SOURCE = apt.8
-include $(MANPAGE_H)
-
 examples/sources.list: examples/sources.list.in apt-verbatim.ent
 	sed -e 's#&stable-codename;#$(shell grep --max-count=1 '^<!ENTITY stable-codename "' apt-verbatim.ent | cut -d'"' -f 2)#g' examples/sources.list.in > examples/sources.list
 
@@ -50,42 +21,43 @@ TO = $(DOC)
 TARGET = binary
 include $(COPY_H)
 
-.PHONY: clean clean-subdirs veryclean veryclean-subdirs all binary doc stats
+.PHONY: clean clean/subdirs veryclean veryclean/subdirs manpages/subdirs debiandoc/subdirs all binary doc stats
 
-clean: clean-subdirs
-veryclean: veryclean-subdirs
+clean: clean/subdirs
+veryclean: veryclean/subdirs
+manpages: manpages/subdirs
+debiandoc: debiandoc/subdirs
 
-clean-subdirs:
-	for dir in $(SUBDIRS); do\
-		$(MAKE) -C $$dir clean; \
-	done
+DOCUMENTATIONPO = $(patsubst %.po,%,$(notdir $(wildcard po/*.po)))
+DOCDIRLIST = $(addsuffix /makefile,$(DOCUMENTATIONPO))
 
-veryclean-subdirs:
-	for dir in $(SUBDIRS); do\
-		rm -rf $$dir; \
+dirs: $(DOCDIRLIST)
+$(DOCDIRLIST) :: %/makefile : lang.makefile
+	test -d $(dir $@) || mkdir $(dir $@)
+	sed "s#@@LANG@@#$(subst /,,$(dir $@))#" $< > $@
+
+debiandoc/subdirs manpages/subdirs clean/subdirs veryclean/subdirs:
+	for dir in en $(dir $(DOCDIRLIST)); do \
+		$(MAKE) -C $$dir $(patsubst %/subdirs,%,$@); \
 	done
 
 stats:
 	for i in po/*.po; do echo -n "$$i: "; msgfmt --output-file=/dev/null --statistics $$i; done
 
 ifdef PO4A
-DOCUMENTATIONPO = $(patsubst %.po,%,$(notdir $(wildcard po/*.po)))
 MANPAGEPOLIST = $(addprefix manpages-translation-,$(DOCUMENTATIONPO))
 DEBIANDOCPOLIST = $(addprefix debiandoc-translation-,$(DOCUMENTATIONPO))
 
-MANPAGEDIRLIST = $(addsuffix /makefile,$(DOCUMENTATIONPO))
+.PHONY: update-po po4a $(MANPAGEPOLIST) $(DEBIANDOCPOLIST) $(DOCDIRLIST)
 
-.PHONY: update-po po4a $(MANPAGEPOLIST) $(MANPAGEDIRLIST)
-
-manpages: $(MANPAGEPOLIST)
-debiandoc: $(DEBIANDOCPOLIST)
-po4a: $(MANPAGEPOLIST) $(DEBIANDOCPOLIST)
+po4a: manpages/subdirs debiandoc/subdirs
 
 update-po:
 	po4a --previous --no-backups --force --no-translations \
 		--package-name='$(PACKAGE)-doc' --package-version='$(PACKAGE_VERSION)' \
 		--msgid-bugs-address='$(PACKAGE_MAIL)' po4a.conf
 
+manpages/subdirs: $(MANPAGEPOLIST)
 $(MANPAGEPOLIST) :: manpages-translation-% : %/makefile po4a.conf
 	# first line is for apt.8 (see Bug#696923)
 	po4a --previous --no-backups --translate-only $(dir $<)apt.ent \
@@ -100,19 +72,13 @@ $(MANPAGEPOLIST) :: manpages-translation-% : %/makefile po4a.conf
 		$(patsubst %,--translate-only $(dir $<)%,$(patsubst %.8.xml,%.$(subst /,,$(dir $<)).8.xml,$(wildcard *.8.xml))) \
 		--package-name='$(PACKAGE)-doc' --package-version='$(PACKAGE_VERSION)' \
 		--msgid-bugs-address='$(PACKAGE_MAIL)' po4a.conf
-	$(MAKE) -C $(dir $<) manpages
 
+debiandoc/subdirs: $(DEBIANDOCPOLIST)
 $(DEBIANDOCPOLIST) :: debiandoc-translation-% : %/makefile po4a.conf
 	po4a --previous --no-backups --translate-only $(dir $<)apt.ent \
 		$(patsubst %,--translate-only $(dir $<)%,$(patsubst %.sgml,%.$(subst /,,$(dir $<)).sgml,$(wildcard *.sgml))) \
 		--package-name='$(PACKAGE)-doc' --package-version='$(PACKAGE_VERSION)' \
 		--msgid-bugs-address='$(PACKAGE_MAIL)' po4a.conf
-	$(MAKE) -C $(dir $<) debiandoc
-
-dirs: $(MANPAGEDIRLIST)
-$(MANPAGEDIRLIST) :: %/makefile : lang.makefile
-	test -d $(dir $@) || mkdir $(dir $@)
-	sed "s#@@LANG@@#$(subst /,,$(dir $@))#" $< > $@
 endif
 
 ifdef DOXYGEN

Разница между файлами не показана из-за своего большого размера
+ 139 - 140
doc/po/apt-doc.pot


Разница между файлами не показана из-за своего большого размера
+ 179 - 180
doc/po/de.po


Разница между файлами не показана из-за своего большого размера
+ 161 - 162
doc/po/es.po


Разница между файлами не показана из-за своего большого размера
+ 162 - 163
doc/po/fr.po


Разница между файлами не показана из-за своего большого размера
+ 213 - 186
doc/po/it.po


Разница между файлами не показана из-за своего большого размера
+ 160 - 160
doc/po/ja.po


Разница между файлами не показана из-за своего большого размера
+ 153 - 153
doc/po/pl.po


Разница между файлами не показана из-за своего большого размера
+ 167 - 166
doc/po/pt.po


Разница между файлами не показана из-за своего большого размера
+ 153 - 153
doc/po/pt_BR.po


+ 7 - 9
methods/gpgv.cc

@@ -55,9 +55,6 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
 					 vector<string> &NoPubKeySigners)
 {
    bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
-   // setup a (empty) stringstream for formating the return value
-   std::stringstream ret;
-   ret.str("");
 
    if (Debug == true)
       std::clog << "inside VerifyGetSigners" << std::endl;
@@ -170,18 +167,19 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
       return "";
    }
    else if (WEXITSTATUS(status) == 1)
-   {
       return _("At least one invalid signature was encountered.");
-   }
    else if (WEXITSTATUS(status) == 111)
+      return _("Could not execute 'gpgv' to verify signature (is gpgv installed?)");
+   else if (WEXITSTATUS(status) == 112)
    {
-      ioprintf(ret, _("Could not execute 'gpgv' to verify signature (is gpgv installed?)"));
-      return ret.str();
+      // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
+      std::string errmsg;
+      //TRANSLATORS: %s is a single techy word like 'NODATA'
+      strprintf(errmsg, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
+      return errmsg;
    }
    else
-   {
       return _("Unknown error executing gpgv");
-   }
 }
 
 bool GPGVMethod::Fetch(FetchItem *Itm)

+ 1 - 1
methods/makefile

@@ -39,7 +39,7 @@ include $(PROGRAM_H)
 
 # The cdrom method
 PROGRAM=cdrom
-SLIBS = -lapt-pkg -ldl $(INTLLIBS)
+SLIBS = -lapt-pkg $(INTLLIBS)
 LIB_MAKES = apt-pkg/makefile
 SOURCE = cdrom.cc
 include $(PROGRAM_H)

+ 11 - 4
po/makefile

@@ -30,17 +30,23 @@ GETDOMAIN = $(word 1,$(subst /, ,$(1)))
 # and produce the .pot file.
 $(POTFILES) : $(PO)/%.pot :
 	echo "Generating POT file $@"
-	echo $@ : $(wildcard $(PO)/domains/$*/*.*list) $(addprefix $(BASE)/,$(shell cat $(wildcard $(PO)/domains/$*/*.srclist))) > $@.d
+	echo $@ : $(wildcard $(PO)/domains/$*/*.*list) $(addprefix $(BASE)/,$(shell cat $(wildcard $(PO)/domains/$*/*.srclist))) > $@.d.tmp
+	cmp --silent $@.d.tmp $@.d || mv $@.d.tmp $@.d
 # From sh source
-	cat $(PO)/domains/$*/*.shlist 2> /dev/null | (cd $(BASE) && xargs -n1 bash --dump-po-strings) > $(PO)/domains/$*/sh.pot
+	cat $(PO)/domains/$*/*.shlist 2> /dev/null | (cd $(BASE) && xargs -n1 bash --dump-po-strings) > $(PO)/domains/$*/sh.pot.tmp
+	cmp --silent $(PO)/domains/$*/sh.pot.tmp $(PO)/domains/$*/sh.pot || \
+		mv $(PO)/domains/$*/sh.pot.tmp $(PO)/domains/$*/sh.pot
 # From C/C++ source
 	cat $(PO)/domains/$*/*.srclist > $(PO)/POTFILES_$*.in
 	$(XGETTEXT) --default-domain=$* --directory=$(BASE) \
 	  --add-comments --foreign --keyword=_ --keyword=N_ \
 	  --keyword=P_:1,2 \
-	  --files-from=$(PO)/POTFILES_$*.in -o $(PO)/domains/$*/c.pot
+	  --files-from=$(PO)/POTFILES_$*.in -o $(PO)/domains/$*/c.pot.tmp
+	cmp --silent $(PO)/domains/$*/c.pot.tmp $(PO)/domains/$*/c.pot || \
+		mv $(PO)/domains/$*/c.pot.tmp $(PO)/domains/$*/c.pot
 	rm -f $(PO)/POTFILES_$*.in
-	$(MSGCOMM) --omit-header --more-than=0 $(PO)/domains/$*/c.pot $(PO)/domains/$*/sh.pot --output=$@
+	$(MSGCOMM) --omit-header --more-than=0 $(PO)/domains/$*/c.pot $(PO)/domains/$*/sh.pot --output=$@.tmp
+	cmp --silent $@ $@.tmp || mv $@.tmp $@
 	# copy into the domain dirs to make rosetta happy
 	rm -f $(PO)/domains/$*/*.pot
 	cp $@ $(PO)/domains/$*
@@ -93,4 +99,5 @@ ifneq ($(words $(The_DFiles)),0)
 include $(The_DFiles)
 endif
 
+# otherwise the output is completely screwed
 .NOTPARALLEL:

+ 2 - 0
test/integration/Packages-pdiff-usage

@@ -19,6 +19,7 @@ Description: Advanced front-end for dpkg
  .
  APT features complete installation ordering, multiple source capability
  and several other unique features, see the Users Guide in apt-doc.
+Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c
 
 Package: oldstuff
 Version: 1.0
@@ -32,3 +33,4 @@ SHA1: 3c695e028f74d5c544deeddaaa1242desa81088c
 SHA256: b46fd1546151c545fe4bfa56a5cc0e7deaef23e2da3e4f129727fd660f28f050
 Description: some cool but old stuff
  This package will disappear in the next mirror update
+Description-md5: 1948af60eda0a41dfa9fe83f60eb8389

+ 2 - 0
test/integration/Packages-pdiff-usage-new

@@ -22,6 +22,7 @@ Description: Advanced front-end for dpkg
  .
  APT features complete installation ordering, multiple source capability
  and several other unique features, see the Users Guide in apt-doc.
+Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c
 
 Package: newstuff
 Version: 1.0
@@ -35,3 +36,4 @@ SHA1: 3c695e028f7a1ae324deeddaaa1242desa81088c
 SHA256: b46fd154615edefab321cc56a5cc0e7deaef23e2da3e4f129727fd660f28f050
 Description: some cool and shiny new stuff
  This package will appear in the next mirror update
+Description-md5: d5f89fbbc2ac69c43d7e4c9b67d82b6b

+ 1 - 0
test/integration/Packages-releasefile-verification

@@ -16,3 +16,4 @@ Description: Advanced front-end for dpkg
  .
  APT features complete installation ordering, multiple source capability
  and several other unique features, see the Users Guide in apt-doc.
+Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c

+ 1 - 0
test/integration/Packages-releasefile-verification-new

@@ -19,3 +19,4 @@ Description: Advanced front-end for dpkg
  .
  APT features complete installation ordering, multiple source capability
  and several other unique features, see the Users Guide in apt-doc.
+Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c

+ 55 - 20
test/integration/framework

@@ -38,7 +38,11 @@ msgtest() {
 }
 msgpass() { echo "${CPASS}PASS${CNORMAL}" >&2; }
 msgskip() { echo "${CWARNING}SKIP${CNORMAL}" >&2; }
-msgfail() { echo "${CFAIL}FAIL${CNORMAL}" >&2; EXIT_CODE=$((EXIT_CODE+1)); }
+msgfail() {
+	if [ $# -gt 0 ]; then echo "${CFAIL}FAIL: $*${CNORMAL}" >&2;
+	else echo "${CFAIL}FAIL${CNORMAL}" >&2; fi
+	EXIT_CODE=$((EXIT_CODE+1));
+}
 
 # enable / disable Debugging
 MSGLEVEL=${MSGLEVEL:-3}
@@ -56,9 +60,9 @@ if [ $MSGLEVEL -le 2 ]; then
 	msgpass() { echo -n " ${CPASS}P${CNORMAL}" >&2; }
 	msgskip() { echo -n " ${CWARNING}S${CNORMAL}" >&2; }
 	if [ -n "$CFAIL" ]; then
-		msgfail() { echo -n " ${CFAIL}FAIL${CNORMAL}" >&2; }
+		msgfail() { echo -n " ${CFAIL}FAIL${CNORMAL}" >&2; EXIT_CODE=$((EXIT_CODE+1)); }
 	else
-		msgfail() { echo -n " ###FAILED###" >&2; }
+		msgfail() { echo -n " ###FAILED###" >&2; EXIT_CODE=$((EXIT_CODE+1)); }
 	fi
 fi
 if [ $MSGLEVEL -le 3 ]; then
@@ -114,6 +118,9 @@ gdb() {
 	echo "gdb: run »$*«"
 	APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY} $(which gdb) ${BUILDDIRECTORY}/$1
 }
+http() {
+	LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/methods/http
+}
 
 exitwithstatus() {
         # error if we about to overflow, but ...
@@ -125,7 +132,11 @@ exitwithstatus() {
 }
 
 addtrap() {
-	CURRENTTRAP="$CURRENTTRAP $1"
+	if [ "$1" = 'prefix' ]; then
+		CURRENTTRAP="$2 $CURRENTTRAP"
+	else
+		CURRENTTRAP="$CURRENTTRAP $1"
+	fi
 	trap "$CURRENTTRAP exitwithstatus;" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
 }
 
@@ -135,8 +146,7 @@ setupenvironment() {
 	msgninfo "Preparing environment for ${CCMD}$(basename $0)${CINFO} in ${TMPWORKINGDIRECTORY}… "
 	BUILDDIRECTORY="${TESTDIRECTORY}/../../build/bin"
 	test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first"
-	local OLDWORKINGDIRECTORY=$(pwd)
-	addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY; cd $OLDWORKINGDIRECTORY;"
+	addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY;"
 	cd $TMPWORKINGDIRECTORY
 	mkdir rootdir aptarchive keys
 	cd rootdir
@@ -483,6 +493,7 @@ insertpackage() {
 	local VERSION="$4"
 	local DEPENDENCIES="$5"
 	local PRIORITY="${6:-optional}"
+	local DESCRIPTION="${7}"
 	local ARCHS=""
 	for arch in $(echo "$ARCH" | sed -e 's#,#\n#g' | sed -e "s#^native\$#$(getarchitecture 'native')#"); do
 		if [ "$arch" = 'all' -o "$arch" = 'none' ]; then
@@ -504,11 +515,16 @@ Maintainer: Joe Sixpack <joe@example.org>" >> $FILE
 			echo "Version: $VERSION
 Filename: pool/main/${NAME}/${NAME}_${VERSION}_${arch}.deb" >> $FILE
 			test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE
-			echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
+			echo -n 'Description: ' >> $FILE
+			if [ -z "$DESCRIPTION" ]; then
+				echo "an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
  If you find such a package installed on your system,
  YOU did something horribly wrong! They are autogenerated
- und used only by testcases for APT and surf no other propose…
-" >> $FILE
+ und used only by testcases for APT and surf no other propose…" >> $FILE
+			else
+				echo "$DESCRIPTION" >> $FILE
+			fi
+			echo >> $FILE
 		done
 	done
 }
@@ -589,9 +605,12 @@ buildaptarchivefromfiles() {
 		cat ${line} | bzip2 > ${line}.bz2
 		cat ${line} | xz --format=lzma > ${line}.lzma
 		cat ${line} | xz > ${line}.xz
+		if [ -n "$1" ]; then
+			touch -d "$1" ${line}.gz ${line}.bz2 ${line}.lzma ${line}.xz
+		fi
 		msgdone "info"
 	done
-	generatereleasefiles
+	generatereleasefiles "$@"
 }
 
 # can be overridden by testcases for their pleasure
@@ -683,9 +702,11 @@ setupaptarchive() {
 		setupflataptarchive
 	fi
 	signreleasefiles
-	msgninfo "\tSync APT's cache with the archive… "
-	aptget update -qq
-	msgdone "info"
+	if [ "$1" != '--no-update' ]; then
+		msgninfo "\tSync APT's cache with the archive… "
+		aptget update -qq
+		msgdone "info"
+	fi
 }
 
 signreleasefiles() {
@@ -701,18 +722,33 @@ signreleasefiles() {
 	done
 	for RELEASE in $(find aptarchive/ -name Release); do
 		gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" -abs -o ${RELEASE}.gpg ${RELEASE}
-		gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" --clearsign -o "$(echo "${RELEASE}" | sed 's#/Release$#/InRelease#')" $RELEASE
+		local INRELEASE="$(echo "${RELEASE}" | sed 's#/Release$#/InRelease#')"
+		gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" --clearsign -o $INRELEASE $RELEASE
+		# we might have set a specific date for the Release file, so copy it
+		touch -d "$(stat --format "%y" ${RELEASE})" ${RELEASE}.gpg ${INRELEASE}
 	done
 	msgdone "info"
 }
 
 changetowebserver() {
-	if which weborf > /dev/null; then
-		weborf -xb aptarchive/ 2>&1 > /dev/null &
+	local LOG='/dev/null'
+	if test -x ${BUILDDIRECTORY}/aptwebserver; then
+		cd aptarchive
+		LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/aptwebserver -o aptwebserver::fork=1 "$@" >$LOG 2>&1
+		local PID="$(cat aptwebserver.pid)"
+		if [ -z "$PID" ]; then
+			msgdie 'Could not fork aptwebserver successfully'
+		fi
+		addtrap "kill $PID;"
+		cd - > /dev/null
+	elif [ $# -gt 0 ]; then
+		msgdie 'Need the aptwebserver when passing arguments for the webserver'
+	elif which weborf > /dev/null; then
+		weborf -xb aptarchive/ >$LOG 2>&1 &
 		addtrap "kill $!;"
 	elif which gatling > /dev/null; then
 		cd aptarchive
-		gatling -p 8080 -F -S 2>&1 > /dev/null &
+		gatling -p 8080 -F -S >$LOG 2>&1 &
 		addtrap "kill $!;"
 		cd - > /dev/null
 	elif which lighttpd > /dev/null; then
@@ -720,11 +756,10 @@ changetowebserver() {
 server.port = 8080
 server.stat-cache-engine = \"disable\"" > lighttpd.conf
 		lighttpd -t -f lighttpd.conf >/dev/null || msgdie 'Can not change to webserver: our lighttpd config is invalid'
-		lighttpd -D -f lighttpd.conf 2>/dev/null >/dev/null &
+		lighttpd -D -f lighttpd.conf >$LOG 2>&1 &
 		addtrap "kill $!;"
 	else
-		msgdie 'You have to install weborf or lighttpd first'
-		return 1
+		msgdie 'You have to build aptwerbserver or install a webserver'
 	fi
 	local APTARCHIVE="file://$(readlink -f ./aptarchive)"
 	for LIST in $(find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list'); do

+ 0 - 38
test/integration/skip-bug-602412-dequote-redirect

@@ -1,38 +0,0 @@
-#!/bin/sh
-set -e
-
-TESTDIR=$(readlink -f $(dirname $0))
-. $TESTDIR/framework
-setupenvironment
-configarchitecture 'i386'
-
-if ! which lighttpd > /dev/null; then
-	msgdie 'You need lighttpd for this testcase, sorry…'
-	exit 1
-fi
-
-buildsimplenativepackage 'unrelated' 'all' '0.5~squeeze1' 'unstable'
-
-setupaptarchive
-
-echo "server.modules = ( \"mod_redirect\" )
-server.document-root = \"$(readlink -f ./aptarchive)\"
-server.port = 8080
-server.stat-cache-engine = \"disable\"
-url.redirect = ( \"^/pool/(.*)$\" => \"/newpool/\$1\",
- \"^/dists/(.*)$\" => \"/newdists/\$1\" )" > lighttpd.conf
-
-mv aptarchive/pool aptarchive/newpool
-mv aptarchive/dists aptarchive/newdists
-
-lighttpd -t -f lighttpd.conf >/dev/null || msgdie 'Can not change to webserver: our lighttpd config is invalid'
-lighttpd -D -f lighttpd.conf 2>/dev/null >/dev/null &
-addtrap "kill $!;"
-
-APTARCHIVE="file://$(readlink -f ./aptarchive)"
-for LIST in $(find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list'); do
-	sed -i $LIST -e "s#$APTARCHIVE#http://localhost:8080/#"
-done
-
-aptget update || msgdie 'apt-get update failed'
-aptget install unrelated --download-only || msgdie 'downloading package failed'

+ 47 - 0
test/integration/skip-bug-711456-apt-cdrom-multiple-cds-multiarch

@@ -0,0 +1,47 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64' 'i386'
+
+buildsimplenativepackage 'testing' 'amd64,i386' '0.8.15' 'stable' 'Depends: libtest'
+buildsimplenativepackage 'libtest' 'amd64,i386' '0.8.15' 'stable' 'Multi-Arch: same'
+buildsimplenativepackage 'libtest' 'amd64,i386' '1' 'unstable' 'Multi-Arch: same'
+
+# needed by the ftparchive.conf
+cd aptarchive
+ln -s ../incoming pool
+createaptftparchiveconfig
+cd - >/dev/null
+# create an amd64 cdrom
+sed -i 's#Architectures .*$#Architectures "amd64 source";#' aptarchive/ftparchive.conf
+setupaptarchive --no-update
+changetocdrom 'Debian APT Testdisk amd64 0.8.15'
+mv rootdir/media/cdrom rootdir/media/cdrom-amd64
+addtrap 'prefix' "chmod -R +w $PWD/rootdir/media/cdrom-amd64/dists/;"
+chmod -R -w rootdir/media/cdrom-amd64/dists
+ln -s $PWD/rootdir/media/cdrom-amd64 $PWD/rootdir/media/cdrom
+aptcdrom add -m -o quiet=1
+rm $PWD/rootdir/media/cdrom
+# do it again to create a i386 cdrom
+sed -i 's#Architectures .*$#Architectures "i386 source";#' aptarchive/ftparchive.conf
+setupaptarchive --no-update
+changetocdrom 'Debian APT Testdisk i386 0.8.15'
+mv rootdir/media/cdrom rootdir/media/cdrom-i386
+addtrap 'prefix' "chmod -R +w $PWD/rootdir/media/cdrom-i386/dists/;"
+chmod -R -w rootdir/media/cdrom-i386/dists
+ln -s $PWD/rootdir/media/cdrom-i386 $PWD/rootdir/media/cdrom
+aptcdrom add -m -o quiet=1
+
+# play with the cdroms
+testdpkgnotinstalled testing
+aptget install testing -t stable -y #> /dev/null 2>&1
+testdpkginstalled testing
+
+testdpkgnotinstalled testing:i386
+aptget install testing:i386 -t stable -y #> /dev/null 2>&1
+testdpkginstalled testing:i386
+
+aptget dist-upgrade -y

+ 2 - 2
test/integration/test-apt-cdrom

@@ -7,7 +7,7 @@ setupenvironment
 configarchitecture 'amd64' 'i386'
 
 buildsimplenativepackage 'testing' 'amd64,i386' '0.8.15' 'stable'
-setupaptarchive
+setupaptarchive --no-update
 
 changetocdrom 'Debian APT Testdisk 0.8.15'
 
@@ -24,7 +24,7 @@ cat Translation-de | xz --format=lzma > Translation-de.lzma
 cat Translation-de | xz > Translation-de.xz
 rm Translation-en Translation-de
 cd - > /dev/null
-addtrap "chmod -R +w $PWD/rootdir/media/cdrom/dists/;"
+addtrap 'prefix' "chmod -R +w $PWD/rootdir/media/cdrom/dists/;"
 chmod -R -w rootdir/media/cdrom/dists
 
 aptcdrom add -m -o quiet=1 > apt-cdrom.log 2>&1

+ 2 - 0
test/integration/test-bug-590041-prefer-non-virtual-packages

@@ -9,6 +9,7 @@ pkglibc6="Package: libc6
 Architecture: armel
 Version: 2.11.2-2~0.3
 Description: Embedded GNU C Library: Shared libraries
+Description-md5: b8c1e0561b75e2dc6b6482a99079c3e4
 Filename: pool/main/e/eglibc/libc6_2.11.2-2_armel.deb
 Installed-Size: 9740
 MD5sum: f5b878ce5fb8aa01a7927fa1460df537
@@ -25,6 +26,7 @@ Architecture: i386
 Version: 2.1.3-13~0.3
 Replaces: libc6 (<< 2.2.5-13~0.3)
 Description: The Berkeley database routines [glibc 2.0/2.1 compatibility]
+Description-md5: de1876f7fe7f7709a110875e145e38a8
 Filename: pool/main/d/db1-compat/libdb1-compat_2.1.3-13_armel.deb
 Installed-Size: 136
 MD5sum: 4043f176ab2b40b0c01bc1211b8c103c

+ 20 - 11
test/integration/test-bug-601016-description-translation

@@ -9,8 +9,9 @@ configarchitecture 'i386' 'amd64'
 # we need a valid locale here, otherwise the language configuration
 # will be overridden by LC_ALL=C
 LOCALE="$(echo "$LANG" | cut -d'_' -f 1)"
+MD5Sum='Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c'
 
-PACKAGESTANZA="Package: apt
+PACKAGESTANZA='Package: apt
 Priority: important
 Section: admin
 Installed-Size: 5984
@@ -19,8 +20,7 @@ Architecture: i386
 Version: 0.8.7
 Filename: pool/main/a/apt/apt_0.8.7_i386.deb
 Size: 2140230
-MD5sum: 74769bfbcef9ebc4fa74f7a5271b9c08
-Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c"
+MD5sum: 74769bfbcef9ebc4fa74f7a5271b9c08'
 
 PACKAGESTANZA2='Package: apt
 Priority: important
@@ -31,22 +31,23 @@ Architecture: amd64
 Version: 0.8.7
 Filename: pool/main/a/apt/apt_0.8.7_amd64.deb
 Size: 2210342
-MD5sum: 4a869bfbdef9ebc9fa74f7a5271e8d1a
-Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c'
+MD5sum: 4a869bfbdef9ebc9fa74f7a5271e8d1a'
 
 echo "$PACKAGESTANZA
 Description: Advanced front-end for dpkg
+$MD5Sum
 
 $PACKAGESTANZA2
-Description: Advanced front-end for dpkg" > aptarchive/Packages
+Description: Advanced front-end for dpkg
+$MD5Sum" > aptarchive/Packages
 
 echo "Package: apt
-Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c
 Description-${LOCALE}: Mächtige Oberfläche für dpkg
  Das Paket bietet dem Nutzer technisch führende Methoden für den Zugriff
  auf den dpkg-Paketmanager. Es beinhaltet das apt-get-Werkzeug und die
  APT-Dselect-Methode. Beides sind einfache und sicherere Wege,
- um Pakete zu installieren und Upgrades durchzuführen." | bzip2 > aptarchive/${LOCALE}.bz2
+ um Pakete zu installieren und Upgrades durchzuführen.
+$MD5Sum" | bzip2 > aptarchive/${LOCALE}.bz2
 
 # the $LOCALE translation file will not be included as it is a flat archive it came from and therefore
 # its name can not be guessed correctly… (in non-flat archives the files are called Translation-*)
@@ -54,10 +55,12 @@ echo 'APT::Cache::Generate "false";' > rootdir/etc/apt/apt.conf.d/00nogenerate
 
 NOLONGSTANZA="$PACKAGESTANZA
 Description: Advanced front-end for dpkg
+$MD5Sum
 "
 
 ENGLISHSTANZA="$PACKAGESTANZA
 Description: Advanced front-end for dpkg
+$MD5Sum
 "
 
 LOCALESTANZA="$PACKAGESTANZA
@@ -66,6 +69,7 @@ Description-${LOCALE}: Mächtige Oberfläche für dpkg
  auf den dpkg-Paketmanager. Es beinhaltet das apt-get-Werkzeug und die
  APT-Dselect-Methode. Beides sind einfache und sicherere Wege,
  um Pakete zu installieren und Upgrades durchzuführen.
+$MD5Sum
 "
 LOCALESTANZA2="$PACKAGESTANZA2
 Description-${LOCALE}: Mächtige Oberfläche für dpkg
@@ -73,6 +77,7 @@ Description-${LOCALE}: Mächtige Oberfläche für dpkg
  auf den dpkg-Paketmanager. Es beinhaltet das apt-get-Werkzeug und die
  APT-Dselect-Methode. Beides sind einfache und sicherere Wege,
  um Pakete zu installieren und Upgrades durchzuführen.
+$MD5Sum
 "
 
 testrun() {
@@ -97,28 +102,32 @@ testrun
 
 echo "$PACKAGESTANZA
 Description: Advanced front-end for dpkg
+$MD5Sum
 
 $PACKAGESTANZA2
-Description: Advanced front-end for dpkg" > aptarchive/Packages
+Description: Advanced front-end for dpkg
+$MD5Sum" > aptarchive/Packages
 
 echo "Package: apt
-Description-md5: d41ee493aa9fcc6cbc9ce4eb7069959c
 Description-en: Advanced front-end for dpkg
  This is Debian's next generation front-end for the dpkg package manager.
  It provides the apt-get utility and APT dselect method that provides a
- simpler, safer way to install and upgrade packages." | bzip2 > aptarchive/en.bz2
+ simpler, safer way to install and upgrade packages.
+$MD5Sum" | bzip2 > aptarchive/en.bz2
 
 ENGLISHSTANZA="$PACKAGESTANZA
 Description-en: Advanced front-end for dpkg
  This is Debian's next generation front-end for the dpkg package manager.
  It provides the apt-get utility and APT dselect method that provides a
  simpler, safer way to install and upgrade packages.
+$MD5Sum
 "
 ENGLISHSTANZA2="$PACKAGESTANZA2
 Description-en: Advanced front-end for dpkg
  This is Debian's next generation front-end for the dpkg package manager.
  It provides the apt-get utility and APT dselect method that provides a
  simpler, safer way to install and upgrade packages.
+$MD5Sum
 "
 
 testrun

+ 29 - 0
test/integration/test-bug-602412-dequote-redirect

@@ -0,0 +1,29 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+buildsimplenativepackage 'unrelated' 'all' '0.5~squeeze1' 'unstable'
+
+setupaptarchive
+changetowebserver -o aptwebserver::redirect::replace::/pool/=/newpool/ \
+		  -o aptwebserver::redirect::replace::/dists/=/newdists/
+
+mv aptarchive/pool aptarchive/newpool
+mv aptarchive/dists aptarchive/newdists
+
+msgtest 'Test redirection works in' 'apt-get update'
+aptget update -qq && msgpass || msgfail
+
+# check that I-M-S header is kept in redirections
+testequal 'Hit http://localhost:8080 unstable InRelease
+Hit http://localhost:8080 unstable/main Sources
+Hit http://localhost:8080 unstable/main amd64 Packages
+Hit http://localhost:8080 unstable/main Translation-en
+Reading package lists...' aptget update #-o debug::pkgacquire=1 -o debug::pkgacquire::worker=1
+
+msgtest 'Test redirection works in' 'package download'
+aptget install unrelated --download-only -qq && msgpass || msgfail

+ 33 - 0
test/integration/test-bug-691453-apt-cache-search-multi-pattern

@@ -0,0 +1,33 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'native'
+
+insertpackage 'unstable' 'foobar' 'native' '1' '' '' 'funky tool'
+insertpackage 'unstable' 'coolstuff' 'native' '1' '' '' 'funky tool just like foo and bar'
+insertpackage 'unstable' 'foo' 'native' '1' '' '' 'tool best used with bar'
+insertpackage 'unstable' 'bar' 'native' '1' '' '' 'tool best used with foo'
+insertpackage 'unstable' 'baz' 'native' '1' 'Provides: bar' '' 'alternative tool best used with foo'
+
+setupaptarchive
+
+# in this special case the following queries should be equal
+FOOBAR='foobar - funky tool
+coolstuff - funky tool just like foo and bar
+foo - tool best used with bar
+bar - tool best used with foo
+baz - alternative tool best used with foo'
+
+testequal "$FOOBAR" aptcache search foo
+testequal "$FOOBAR" aptcache search bar
+testequal "$FOOBAR" aptcache search foo bar
+
+testequal 'foobar - funky tool
+foo - tool best used with bar' aptcache search -n foo
+testequal 'foobar - funky tool
+bar - tool best used with foo
+baz - alternative tool best used with foo' aptcache search -n bar
+testequal 'foobar - funky tool' aptcache search -n foo bar

+ 89 - 0
test/integration/test-bug-712435-missing-descriptions

@@ -0,0 +1,89 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+PACKAGESTANZA='Version: 0.9.7.8
+Installed-Size: 3270
+Maintainer: APT Development Team <deity@lists.debian.org>
+Architecture: amd64
+Filename: pool/main/a/apt/apt_0.9.7.8_amd64.deb
+MD5sum: 3a622acda41620df50aa22a9fac6f32e'
+
+DESCRIPTION='Description: commandline package manager
+ This APT has Super Cow Powers.'
+
+TRANSDESCRIPTION='Description-en: commandline package manager
+ This APT has translated Super Cow Powers.'
+
+echo "Package: apt-normal
+$PACKAGESTANZA
+$DESCRIPTION
+Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+Package: apt-both-below
+$PACKAGESTANZA
+$DESCRIPTION
+$TRANSDESCRIPTION
+Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+
+Package: apt-both-middle
+$PACKAGESTANZA
+$DESCRIPTION
+Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+$TRANSDESCRIPTION
+
+Package: apt-both-top
+$PACKAGESTANZA
+Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+$DESCRIPTION
+$TRANSDESCRIPTION
+
+Package: apt-trans
+$PACKAGESTANZA
+$TRANSDESCRIPTION
+Description-md5: cccccccccccccccccccccccccccccccc
+
+Package: apt-md5
+$PACKAGESTANZA
+Description-md5: dddddddddddddddddddddddddddddddd
+
+Package: apt-none
+$PACKAGESTANZA" > aptarchive/Packages
+
+setupaptarchive
+
+testequal "Package: apt-normal
+$PACKAGESTANZA
+$DESCRIPTION
+Description-md5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+" 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
+	testequal "Package: apt-both-$variant
+$PACKAGESTANZA
+$DESCRIPTION
+Description-md5: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+" aptcache show apt-both-$variant
+done
+
+testequal "Package: apt-trans
+$PACKAGESTANZA
+$TRANSDESCRIPTION
+Description-md5: cccccccccccccccccccccccccccccccc
+" aptcache show apt-trans
+
+testequal "Package: apt-md5
+$PACKAGESTANZA
+Description-md5: dddddddddddddddddddddddddddddddd
+" aptcache show apt-md5
+
+testequal "Package: apt-none
+$PACKAGESTANZA
+" aptcache show apt-none

+ 2 - 2
test/integration/test-cve-2013-1051-InRelease-parsing

@@ -12,7 +12,7 @@ insertpackage 'stable' 'good-pkg' 'all' '1.0'
 setupaptarchive
 
 changetowebserver
-ARCHIVE='http://localhost/'
+ARCHIVE='http://localhost:8080/'
 msgtest 'Initial apt-get update should work with' 'InRelease'
 aptget update -qq && msgpass || msgfail
 
@@ -37,7 +37,7 @@ sed -i '/^-----BEGIN PGP SIGNATURE-----/,/^-----END PGP SIGNATURE-----/ s/^$/  /
 
 # we append the (evil unsigned) Release file to the (good signed) InRelease
 cat aptarchive/dists/stable/Release >> aptarchive/dists/stable/InRelease
-
+touch -d '+1hour' aptarchive/dists/stable/InRelease
 
 # ensure the update fails
 # useful for debugging to add "-o Debug::pkgAcquire::auth=true"

+ 1 - 1
test/integration/test-pdiff-usage

@@ -35,7 +35,7 @@ SHA1-History:
 SHA1-Patches:
  7651fc0ac57cd83d41c63195a9342e2db5650257 19722 2010-08-18-0814.28
  $(sha1sum $PATCHFILE | cut -d' ' -f 1) $(stat -c%s $PATCHFILE) $(basename $PATCHFILE)" > $PATCHINDEX
-generatereleasefiles
+generatereleasefiles '+1hour'
 signreleasefiles
 find aptarchive -name 'Packages*' -type f -delete
 aptget update -qq

+ 106 - 0
test/integration/test-prefer-higher-priority-providers

@@ -0,0 +1,106 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'native'
+
+insertpackage 'unstable' 'foo' 'all' '1' 'Provides: stuff' 'important'
+insertpackage 'unstable' 'bar' 'all' '1' 'Provides: stuff' 'optional'
+insertpackage 'unstable' 'baz' 'all' '1' 'Provides: stuff' 'extra'
+insertpackage 'unstable' 'awesome' 'all' '1' 'Depends: stuff'
+
+setupaptarchive
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following extra packages will be installed:
+  foo
+The following NEW packages will be installed:
+  awesome foo
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst foo (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf foo (1 unstable [all])
+Conf awesome (1 unstable [all])' aptget install awesome -s
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+  awesome foo
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst foo (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf foo (1 unstable [all])
+Conf awesome (1 unstable [all])' aptget install awesome foo -s
+
+testequal "Reading package lists...
+Building dependency tree...
+Package 'bar' is not installed, so not removed
+Package 'baz' is not installed, so not removed
+The following extra packages will be installed:
+  foo
+The following NEW packages will be installed:
+  awesome foo
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst foo (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf foo (1 unstable [all])
+Conf awesome (1 unstable [all])" aptget install awesome bar- baz- -s
+
+testequal "Reading package lists...
+Building dependency tree...
+Package 'foo' is not installed, so not removed
+The following extra packages will be installed:
+  bar
+The following NEW packages will be installed:
+  awesome bar
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst bar (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf bar (1 unstable [all])
+Conf awesome (1 unstable [all])" aptget install awesome foo- -s
+
+testequal "Reading package lists...
+Building dependency tree...
+Package 'foo' is not installed, so not removed
+Package 'baz' is not installed, so not removed
+The following extra packages will be installed:
+  bar
+The following NEW packages will be installed:
+  awesome bar
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst bar (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf bar (1 unstable [all])
+Conf awesome (1 unstable [all])" aptget install awesome foo- baz- -s
+
+testequal "Reading package lists...
+Building dependency tree...
+Package 'foo' is not installed, so not removed
+Package 'bar' is not installed, so not removed
+The following extra packages will be installed:
+  baz
+The following NEW packages will be installed:
+  awesome baz
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+Inst baz (1 unstable [all])
+Inst awesome (1 unstable [all])
+Conf baz (1 unstable [all])
+Conf awesome (1 unstable [all])" aptget install awesome foo- bar- -s
+
+testequal "Reading package lists...
+Building dependency tree...
+Package 'foo' is not installed, so not removed
+Package 'bar' is not installed, so not removed
+Package 'baz' is not installed, so not removed
+Some packages could not be installed. This may mean that you have
+requested an impossible situation or if you are using the unstable
+distribution that some required packages have not yet been created
+or been moved out of Incoming.
+The following information may help to resolve the situation:
+
+The following packages have unmet dependencies:
+ awesome : Depends: stuff
+E: Unable to correct problems, you have held broken packages." aptget install awesome foo- bar- baz- -s

+ 2 - 2
test/integration/test-releasefile-verification

@@ -184,5 +184,5 @@ runtest2
 
 DELETEFILE="InRelease"
 runtest
-#DELETEFILE="Release.gpg"
-#runtest
+DELETEFILE="Release.gpg"
+runtest

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

@@ -0,0 +1,64 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture 'native'
+
+insertpackage 'unstable' 'unrelated' 'all' '1.0' 'stable'
+insertsource 'unstable' 'unrelated' 'all' '1.0' 'stable'
+
+echo 'ni ni ni' > aptarchive/knights
+
+setupaptarchive
+changetowebserver -o 'aptwebserver::overwrite::.*::filename=/knights'
+
+msgtest 'Acquire test file from the webserver to check' 'overwrite'
+echo '601 Configuration
+Config-Item: Acquire::http::DependOnSTDIN=0
+
+600 Acquire URI
+URI: http://localhost:8080/holygrail
+Filename: knights-talking
+' | http >/dev/null 2>&1 && msgpass || msgfail
+testfileequal knights-talking 'ni ni ni'
+
+ensure_n_canary_strings_in_dir() {
+	local DIR="$1"
+	local CANARY_STRING="$2"
+	local EXPECTED_N="$3"
+
+	msgtest "Testing in $DIR for $EXPECTED_N canary" "$CANARY_STRING"
+	local N=$(grep "$CANARY_STRING" $DIR/* 2>/dev/null |wc -l )
+	test "$N" = "$EXPECTED_N" && msgpass || msgfail "Expected $EXPECTED_N canaries, got $N"
+}
+
+LISTS='rootdir/var/lib/apt/lists'
+rm -rf rootdir/var/lib/apt/lists
+msgtest 'Got expected NODATA failure in' 'apt-get update'
+aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+
+ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
+testequal 'partial' ls $LISTS
+
+# and again with pre-existing files with "valid data" which should remain
+for f in Release Release.gpg main_binary-amd64_Packages main_source_Sources; do
+    echo 'peng neee-wom' > $LISTS/localhost:8080_dists_stable_${f}
+done
+
+msgtest 'Got expected NODATA failure in' 'apt-get update'
+aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+
+ensure_n_canary_strings_in_dir $LISTS 'peng neee-wom' 4
+ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
+
+# and now with a pre-existing InRelease file
+echo 'peng neee-wom' > $LISTS/localhost:8080_dists_stable_InRelease
+rm -f $LISTS/localhost:8080_dists_stable_Release $LISTS/localhost:8080_dists_stable_Release.gpg
+msgtest 'excpected failure of' 'apt-get update'
+aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+
+ensure_n_canary_strings_in_dir $LISTS 'peng neee-wom' 3
+ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0

+ 46 - 0
test/integration/test-very-tight-loop-configure-with-unpacking-new-packages

@@ -0,0 +1,46 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+# the difference between version 3 and 4 is the new package 'ure' which
+# we have to unpack before we start configuring parts of the loop
+insertinstalledpackage 'libreoffice' 'amd64' '3' 'Depends: libreoffice-core (= 3)'
+insertinstalledpackage 'libreoffice-core' 'amd64' '3' 'Depends: libreoffice-common (>= 3)'
+insertinstalledpackage 'libreoffice-common' 'all' '3' 'Depends: libreoffice-style
+Breaks: libreoffice-core (>= 3+), libreoffice-core (<= 3~), libreoffice-style-galaxy (>= 3+), libreoffice-style-galaxy (<= 3~)'
+insertinstalledpackage 'libreoffice-style-galaxy' 'amd64' '3' 'Depends: libreoffice-core
+Provides: libreoffice-style'
+
+buildsimplenativepackage 'libreoffice' 'amd64' '4' 'sid' 'Depends: libreoffice-core (= 4)'
+buildsimplenativepackage 'libreoffice-core' 'amd64' '4' 'sid' 'Depends: libreoffice-common (>= 4)
+Breaks: libreoffice-common (<< 4), libreoffice-style-galaxy (<< 4)'
+buildsimplenativepackage 'libreoffice-common' 'all' '4' 'sid' 'Depends: libreoffice-style, ure
+Breaks: libreoffice-core (>= 4+), libreoffice-core (<= 4~), libreoffice-style-galaxy (>= 4+), libreoffice-style-galaxy (<= 4~)'
+buildsimplenativepackage 'libreoffice-style-galaxy' 'amd64' '4' 'sid' 'Depends: libreoffice-core
+Provides: libreoffice-style'
+
+buildsimplenativepackage 'ure' 'amd64' '4' 'sid'
+
+setupaptarchive
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+  ure
+The following packages will be upgraded:
+  libreoffice libreoffice-common libreoffice-core libreoffice-style-galaxy
+4 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
+Inst libreoffice [3] (4 sid [amd64]) []
+Inst libreoffice-style-galaxy [3] (4 sid [amd64]) [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-common:amd64 ]
+Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-common:amd64 ]
+Inst libreoffice-common [3] (4 sid [all]) []
+Inst ure (4 sid [amd64])
+Conf ure (4 sid [amd64])
+Conf libreoffice-style-galaxy (4 sid [amd64])
+Conf libreoffice-common (4 sid [all])
+Conf libreoffice-core (4 sid [amd64])
+Conf libreoffice (4 sid [amd64])' aptget dist-upgrade -s

+ 543 - 0
test/interactive-helper/aptwebserver.cc

@@ -0,0 +1,543 @@
+#include <config.h>
+
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/init.h>
+
+#include <vector>
+#include <string>
+#include <list>
+#include <sstream>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <signal.h>
+
+char const * const httpcodeToStr(int const httpcode)			/*{{{*/
+{
+   switch (httpcode)
+   {
+      // Informational 1xx
+      case 100: return "100 Continue";
+      case 101: return "101 Switching Protocols";
+      // Successful 2xx
+      case 200: return "200 OK";
+      case 201: return "201 Created";
+      case 202: return "202 Accepted";
+      case 203: return "203 Non-Authoritative Information";
+      case 204: return "204 No Content";
+      case 205: return "205 Reset Content";
+      case 206: return "206 Partial Content";
+      // Redirections 3xx
+      case 300: return "300 Multiple Choices";
+      case 301: return "301 Moved Permanently";
+      case 302: return "302 Found";
+      case 303: return "303 See Other";
+      case 304: return "304 Not Modified";
+      case 305: return "304 Use Proxy";
+      case 307: return "307 Temporary Redirect";
+      // Client errors 4xx
+      case 400: return "400 Bad Request";
+      case 401: return "401 Unauthorized";
+      case 402: return "402 Payment Required";
+      case 403: return "403 Forbidden";
+      case 404: return "404 Not Found";
+      case 405: return "405 Method Not Allowed";
+      case 406: return "406 Not Acceptable";
+      case 407: return "407 Proxy Authentication Required";
+      case 408: return "408 Request Time-out";
+      case 409: return "409 Conflict";
+      case 410: return "410 Gone";
+      case 411: return "411 Length Required";
+      case 412: return "412 Precondition Failed";
+      case 413: return "413 Request Entity Too Large";
+      case 414: return "414 Request-URI Too Large";
+      case 415: return "415 Unsupported Media Type";
+      case 416: return "416 Requested range not satisfiable";
+      case 417: return "417 Expectation Failed";
+      case 418: return "418 I'm a teapot";
+      // Server error 5xx
+      case 500: return "500 Internal Server Error";
+      case 501: return "501 Not Implemented";
+      case 502: return "502 Bad Gateway";
+      case 503: return "503 Service Unavailable";
+      case 504: return "504 Gateway Time-out";
+      case 505: return "505 HTTP Version not supported";
+   }
+   return NULL;
+}
+									/*}}}*/
+void addFileHeaders(std::list<std::string> &headers, FileFd &data)	/*{{{*/
+{
+   std::ostringstream contentlength;
+   contentlength << "Content-Length: " << data.FileSize();
+   headers.push_back(contentlength.str());
+
+   std::string lastmodified("Last-Modified: ");
+   lastmodified.append(TimeRFC1123(data.ModificationTime()));
+   headers.push_back(lastmodified);
+}
+									/*}}}*/
+void addDataHeaders(std::list<std::string> &headers, std::string &data)	/*{{{*/
+{
+   std::ostringstream contentlength;
+   contentlength << "Content-Length: " << data.size();
+   headers.push_back(contentlength.str());
+}
+									/*}}}*/
+bool sendHead(int const client, int const httpcode, std::list<std::string> &headers)/*{{{*/
+{
+   std::string response("HTTP/1.1 ");
+   response.append(httpcodeToStr(httpcode));
+   headers.push_front(response);
+
+   headers.push_back("Server: APT webserver");
+
+   std::string date("Date: ");
+   date.append(TimeRFC1123(time(NULL)));
+   headers.push_back(date);
+
+   std::clog << ">>> RESPONSE >>>" << std::endl;
+   bool Success = true;
+   for (std::list<std::string>::const_iterator h = headers.begin();
+	Success == true && h != headers.end(); ++h)
+   {
+      Success &= FileFd::Write(client, h->c_str(), h->size());
+      if (Success == true)
+	 Success &= FileFd::Write(client, "\r\n", 2);
+      std::clog << *h << std::endl;
+   }
+   if (Success == true)
+      Success &= FileFd::Write(client, "\r\n", 2);
+   std::clog << "<<<<<<<<<<<<<<<<" << std::endl;
+   return Success;
+}
+									/*}}}*/
+bool sendFile(int const client, FileFd &data)				/*{{{*/
+{
+   bool Success = true;
+   char buffer[500];
+   unsigned long long actual = 0;
+   while ((Success &= data.Read(buffer, sizeof(buffer), &actual)) == true)
+   {
+      if (actual == 0)
+	 break;
+      if (Success == true)
+	 Success &= FileFd::Write(client, buffer, actual);
+   }
+   if (Success == true)
+      Success &= FileFd::Write(client, "\r\n", 2);
+   return Success;
+}
+									/*}}}*/
+bool sendData(int const client, std::string const &data)		/*{{{*/
+{
+   bool Success = true;
+   Success &= FileFd::Write(client, data.c_str(), data.size());
+   if (Success == true)
+      Success &= FileFd::Write(client, "\r\n", 2);
+   return Success;
+}
+									/*}}}*/
+void sendError(int const client, int const httpcode, std::string const &request,/*{{{*/
+	       bool content, std::string const &error = "")
+{
+   std::list<std::string> headers;
+   std::string response("<html><head><title>");
+   response.append(httpcodeToStr(httpcode)).append("</title></head>");
+   response.append("<body><h1>").append(httpcodeToStr(httpcode)).append("</h1>");
+   if (error.empty() == false)
+      response.append("<p><em>Error</em>: ").append(error).append("</p>");
+   response.append("This error is a result of the request: <pre>");
+   response.append(request).append("</pre></body></html>");
+   addDataHeaders(headers, response);
+   sendHead(client, httpcode, headers);
+   if (content == true)
+      sendData(client, response);
+}
+									/*}}}*/
+void sendRedirect(int const client, int const httpcode, std::string const &uri,/*{{{*/
+		  std::string const &request, bool content)
+{
+   std::list<std::string> headers;
+   std::string response("<html><head><title>");
+   response.append(httpcodeToStr(httpcode)).append("</title></head>");
+   response.append("<body><h1>").append(httpcodeToStr(httpcode)).append("</h1");
+   response.append("<p>You should be redirected to <em>").append(uri).append("</em></p>");
+   response.append("This page is a result of the request: <pre>");
+   response.append(request).append("</pre></body></html>");
+   addDataHeaders(headers, response);
+   std::string location("Location: ");
+   if (strncmp(uri.c_str(), "http://", 7) != 0)
+      location.append("http://").append(LookupTag(request, "Host")).append("/").append(uri);
+   else
+      location.append(uri);
+   headers.push_back(location);
+   sendHead(client, httpcode, headers);
+   if (content == true)
+      sendData(client, response);
+}
+									/*}}}*/
+int filter_hidden_files(const struct dirent *a)				/*{{{*/
+{
+   if (a->d_name[0] == '.')
+      return 0;
+#ifdef _DIRENT_HAVE_D_TYPE
+   // if we have the d_type check that only files and dirs will be included
+   if (a->d_type != DT_UNKNOWN &&
+       a->d_type != DT_REG &&
+       a->d_type != DT_LNK && // this includes links to regular files
+       a->d_type != DT_DIR)
+      return 0;
+#endif
+   return 1;
+}
+int grouped_alpha_case_sort(const struct dirent **a, const struct dirent **b) {
+#ifdef _DIRENT_HAVE_D_TYPE
+   if ((*a)->d_type == DT_DIR && (*b)->d_type == DT_DIR);
+   else if ((*a)->d_type == DT_DIR && (*b)->d_type == DT_REG)
+      return -1;
+   else if ((*b)->d_type == DT_DIR && (*a)->d_type == DT_REG)
+      return 1;
+   else
+#endif
+   {
+      struct stat f_prop; //File's property
+      stat((*a)->d_name, &f_prop);
+      int const amode = f_prop.st_mode;
+      stat((*b)->d_name, &f_prop);
+      int const bmode = f_prop.st_mode;
+      if (S_ISDIR(amode) && S_ISDIR(bmode));
+      else if (S_ISDIR(amode))
+	 return -1;
+      else if (S_ISDIR(bmode))
+	 return 1;
+   }
+   return strcasecmp((*a)->d_name, (*b)->d_name);
+}
+									/*}}}*/
+void sendDirectoryListing(int const client, std::string const &dir,	/*{{{*/
+			  std::string const &request, bool content)
+{
+   std::list<std::string> headers;
+   std::ostringstream listing;
+
+   struct dirent **namelist;
+   int const counter = scandir(dir.c_str(), &namelist, filter_hidden_files, grouped_alpha_case_sort);
+   if (counter == -1)
+   {
+      sendError(client, 500, request, content);
+      return;
+   }
+
+   listing << "<html><head><title>Index of " << dir << "</title>"
+	   << "<style type=\"text/css\"><!-- td {padding: 0.02em 0.5em 0.02em 0.5em;}"
+	   << "tr:nth-child(even){background-color:#dfdfdf;}"
+	   << "h1, td:nth-child(3){text-align:center;}"
+	   << "table {margin-left:auto;margin-right:auto;} --></style>"
+	   << "</head>" << std::endl
+	   << "<body><h1>Index of " << dir << "</h1>" << std::endl
+	   << "<table><tr><th>#</th><th>Name</th><th>Size</th><th>Last-Modified</th></tr>" << std::endl;
+   if (dir != ".")
+      listing << "<tr><td>d</td><td><a href=\"..\">Parent Directory</a></td><td>-</td><td>-</td></tr>";
+   for (int i = 0; i < counter; ++i) {
+      struct stat fs;
+      std::string filename(dir);
+      filename.append("/").append(namelist[i]->d_name);
+      stat(filename.c_str(), &fs);
+      if (S_ISDIR(fs.st_mode))
+      {
+	 listing << "<tr><td>d</td>"
+		 << "<td><a href=\"" << namelist[i]->d_name << "/\">" << namelist[i]->d_name << "</a></td>"
+		 << "<td>-</td>";
+      }
+      else
+      {
+	 listing << "<tr><td>f</td>"
+		 << "<td><a href=\"" << namelist[i]->d_name << "\">" << namelist[i]->d_name << "</a></td>"
+		 << "<td>" << SizeToStr(fs.st_size) << "B</td>";
+      }
+      listing << "<td>" << TimeRFC1123(fs.st_mtime) << "</td></tr>" << std::endl;
+   }
+   listing << "</table></body></html>" << std::endl;
+
+   std::string response(listing.str());
+   addDataHeaders(headers, response);
+   sendHead(client, 200, headers);
+   if (content == true)
+      sendData(client, response);
+}
+									/*}}}*/
+bool parseFirstLine(int const client, std::string const &request,	/*{{{*/
+		    std::string &filename, bool &sendContent,
+		    bool &closeConnection)
+{
+   if (strncmp(request.c_str(), "HEAD ", 5) == 0)
+      sendContent = false;
+   if (strncmp(request.c_str(), "GET ", 4) != 0)
+   {
+      sendError(client, 501, request, true);
+      return false;
+   }
+
+   size_t const lineend = request.find('\n');
+   size_t filestart = request.find(' ');
+   for (; request[filestart] == ' '; ++filestart);
+   size_t fileend = request.rfind(' ', lineend);
+   if (lineend == std::string::npos || filestart == std::string::npos ||
+	 fileend == std::string::npos || filestart == fileend)
+   {
+      sendError(client, 500, request, sendContent, "Filename can't be extracted");
+      return false;
+   }
+
+   size_t httpstart = fileend;
+   for (; request[httpstart] == ' '; ++httpstart);
+   if (strncmp(request.c_str() + httpstart, "HTTP/1.1\r", 9) == 0)
+      closeConnection = strcasecmp(LookupTag(request, "Connection", "Keep-Alive").c_str(), "Keep-Alive") != 0;
+   else if (strncmp(request.c_str() + httpstart, "HTTP/1.0\r", 9) == 0)
+      closeConnection = strcasecmp(LookupTag(request, "Connection", "Keep-Alive").c_str(), "close") == 0;
+   else
+   {
+      sendError(client, 500, request, sendContent, "Not a HTTP/1.{0,1} request");
+      return false;
+   }
+
+   filename = request.substr(filestart, fileend - filestart);
+   if (filename.find(' ') != std::string::npos)
+   {
+      sendError(client, 500, request, sendContent, "Filename contains an unencoded space");
+      return false;
+   }
+   filename = DeQuoteString(filename);
+
+   // this is not a secure server, but at least prevent the obvious …
+   if (filename.empty() == true || filename[0] != '/' ||
+       strncmp(filename.c_str(), "//", 2) == 0 ||
+       filename.find_first_of("\r\n\t\f\v") != std::string::npos ||
+       filename.find("/../") != std::string::npos)
+   {
+      sendError(client, 400, request, sendContent, "Filename contains illegal character (sequence)");
+      return false;
+   }
+
+   // nuke the first character which is a / as we assured above
+   filename.erase(0, 1);
+   if (filename.empty() == true)
+      filename = ".";
+   return true;
+}
+									/*}}}*/
+int main(int const argc, const char * argv[])
+{
+   CommandLine::Args Args[] = {
+      {0, "port", "aptwebserver::port", CommandLine::HasArg},
+      {'c',"config-file",0,CommandLine::ConfigFile},
+      {'o',"option",0,CommandLine::ArbItem},
+      {0,0,0,0}
+   };
+
+   CommandLine CmdL(Args, _config);
+   if(CmdL.Parse(argc,argv) == false)
+   {
+      _error->DumpErrors();
+      exit(1);
+   }
+
+   // create socket, bind and listen to it {{{
+   // ignore SIGPIPE, this can happen on write() if the socket closes connection
+   signal(SIGPIPE, SIG_IGN);
+   int sock = socket(AF_INET6, SOCK_STREAM, 0);
+   if(sock < 0)
+   {
+      _error->Errno("aptwerbserver", "Couldn't create socket");
+      _error->DumpErrors(std::cerr);
+      return 1;
+   }
+
+   int const port = _config->FindI("aptwebserver::port", 8080);
+
+   // ensure that we accept all connections: v4 or v6
+   int const iponly = 0;
+   setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &iponly, sizeof(iponly));
+   // to not linger on an address
+   int const enable = 1;
+   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
+
+   struct sockaddr_in6 locAddr;
+   memset(&locAddr, 0, sizeof(locAddr));
+   locAddr.sin6_family = AF_INET6;
+   locAddr.sin6_port = htons(port);
+   locAddr.sin6_addr = in6addr_any;
+
+   if (bind(sock, (struct sockaddr*) &locAddr, sizeof(locAddr)) < 0)
+   {
+      _error->Errno("aptwerbserver", "Couldn't bind");
+      _error->DumpErrors(std::cerr);
+      return 2;
+   }
+
+   FileFd pidfile;
+   if (_config->FindB("aptwebserver::fork", false) == true)
+   {
+      std::string const pidfilename = _config->Find("aptwebserver::pidfile", "aptwebserver.pid");
+      int const pidfilefd = GetLock(pidfilename);
+      if (pidfilefd < 0 || pidfile.OpenDescriptor(pidfilefd, FileFd::WriteOnly) == false)
+      {
+	 _error->Errno("aptwebserver", "Couldn't acquire lock on pidfile '%s'", pidfilename.c_str());
+	 _error->DumpErrors(std::cerr);
+	 return 3;
+      }
+
+      pid_t child = fork();
+      if (child < 0)
+      {
+	 _error->Errno("aptwebserver", "Forking failed");
+	 _error->DumpErrors(std::cerr);
+	 return 4;
+      }
+      else if (child != 0)
+      {
+	 // successfully forked: ready to serve!
+	 std::string pidcontent;
+	 strprintf(pidcontent, "%d", child);
+	 pidfile.Write(pidcontent.c_str(), pidcontent.size());
+	 if (_error->PendingError() == true)
+	 {
+	    _error->DumpErrors(std::cerr);
+	    return 5;
+	 }
+	 std::cout << "Successfully forked as " << child << std::endl;
+	 return 0;
+      }
+   }
+
+   std::clog << "Serving ANY file on port: " << port << std::endl;
+
+   listen(sock, 1);
+   /*}}}*/
+
+   std::vector<std::string> messages;
+   int client;
+   while ((client = accept(sock, NULL, NULL)) != -1)
+   {
+      std::clog << "ACCEPT client " << client
+		<< " on socket " << sock << std::endl;
+
+      while (ReadMessages(client, messages))
+      {
+	 bool closeConnection = false;
+	 for (std::vector<std::string>::const_iterator m = messages.begin();
+	      m != messages.end() && closeConnection == false; ++m) {
+	    std::clog << ">>> REQUEST >>>>" << std::endl << *m
+		      << std::endl << "<<<<<<<<<<<<<<<<" << std::endl;
+	    std::list<std::string> headers;
+	    std::string filename;
+	    bool sendContent = true;
+	    if (parseFirstLine(client, *m, filename, sendContent, closeConnection) == false)
+	       continue;
+
+	    std::string host = LookupTag(*m, "Host", "");
+	    if (host.empty() == true)
+	    {
+	       // RFC 2616 §14.23 requires Host
+	       sendError(client, 400, *m, sendContent, "Host header is required");
+	       continue;
+	    }
+
+	    // string replacements in the requested filename
+	    ::Configuration::Item const *Replaces = _config->Tree("aptwebserver::redirect::replace");
+	    if (Replaces != NULL)
+	    {
+	       std::string redirect = "/" + filename;
+	       for (::Configuration::Item *I = Replaces->Child; I != NULL; I = I->Next)
+		  redirect = SubstVar(redirect, I->Tag, I->Value);
+	       redirect.erase(0,1);
+	       if (redirect != filename)
+	       {
+		  sendRedirect(client, 301, redirect, *m, sendContent);
+		  continue;
+	       }
+	    }
+
+	    ::Configuration::Item const *Overwrite = _config->Tree("aptwebserver::overwrite");
+	    if (Overwrite != NULL)
+	    {
+	       for (::Configuration::Item *I = Overwrite->Child; I != NULL; I = I->Next)
+	       {
+		  regex_t *pattern = new regex_t;
+		  int const res = regcomp(pattern, I->Tag.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB);
+		  if (res != 0)
+		  {
+		     char error[300];
+		     regerror(res, pattern, error, sizeof(error));
+		     sendError(client, 500, *m, sendContent, error);
+		     continue;
+		  }
+		  if (regexec(pattern, filename.c_str(), 0, 0, 0) == 0)
+		  {
+		      filename = _config->Find("aptwebserver::overwrite::" + I->Tag + "::filename", filename);
+		      if (filename[0] == '/')
+			 filename.erase(0,1);
+		      regfree(pattern);
+		      break;
+		  }
+		  regfree(pattern);
+	       }
+	    }
+
+	    // deal with the request
+	    if (RealFileExists(filename) == true)
+	    {
+	       FileFd data(filename, FileFd::ReadOnly);
+	       std::string condition = LookupTag(*m, "If-Modified-Since", "");
+	       if (condition.empty() == false)
+	       {
+		  time_t cache;
+		  if (RFC1123StrToTime(condition.c_str(), cache) == true &&
+			cache >= data.ModificationTime())
+		  {
+		     sendHead(client, 304, headers);
+		     continue;
+		  }
+	       }
+
+	       addFileHeaders(headers, data);
+	       sendHead(client, 200, headers);
+	       if (sendContent == true)
+		  sendFile(client, data);
+	    }
+	    else if (DirectoryExists(filename) == true)
+	    {
+	       if (filename == "." || filename[filename.length()-1] == '/')
+		  sendDirectoryListing(client, filename, *m, sendContent);
+	       else
+		  sendRedirect(client, 301, filename.append("/"), *m, sendContent);
+	    }
+	    else
+	       sendError(client, 404, *m, sendContent);
+	 }
+	 _error->DumpErrors(std::cerr);
+	 messages.clear();
+	 if (closeConnection == true)
+	    break;
+      }
+
+      std::clog << "CLOSE client " << client
+		<< " on socket " << sock << std::endl;
+      close(client);
+   }
+   pidfile.Close();
+
+   return 0;
+}

+ 8 - 0
test/interactive-helper/makefile

@@ -1,6 +1,7 @@
 # -*- make -*-
 BASE=../..
 SUBDIR=test/interactive-helper
+APT_DOMAIN=none
 
 # Bring in the default rules
 include ../../buildlib/defaults.mak
@@ -37,3 +38,10 @@ include $(PROGRAM_H)
 #SLIBS = -lapt-pkg -lrpm
 #SOURCE = rpmver.cc
 #include $(PROGRAM_H)
+
+# Program for testing udevcdrom
+PROGRAM=aptwebserver
+SLIBS = -lapt-pkg
+LIB_MAKES = apt-pkg/makefile
+SOURCE = aptwebserver.cc
+include $(PROGRAM_H)

+ 1 - 0
test/libapt/makefile

@@ -2,6 +2,7 @@
 BASE=../..
 SUBDIR=test/libapt
 BASENAME=_libapt_test
+APT_DOMAIN=none
 
 # Bring in the default rules
 include ../../buildlib/defaults.mak