]> git.saurik.com Git - apt.git/blobdiff - ftparchive/writer.cc
check for length of Data to avoid the validness of
[apt.git] / ftparchive / writer.cc
index 7aea89317fd00b7591554c2f6e54dfd3b7bf5640..58e32dade692810a8b0777ad415820d8fc3c1bc2 100644 (file)
@@ -1,6 +1,6 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: writer.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $
+// $Id: writer.cc,v 1.14 2004/03/24 01:40:43 mdz Exp $
 /* ######################################################################
 
    Writer 
 /* ######################################################################
 
    Writer 
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "writer.h"
-#endif
-
 #include "writer.h"
     
 #include "writer.h"
     
+#include <apti18n.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/configuration.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/configuration.h>
 #include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/sha256.h>
 #include <apt-pkg/deblistparser.h>
 
 #include <sys/types.h>
 #include <unistd.h>
 #include <apt-pkg/deblistparser.h>
 
 #include <sys/types.h>
 #include <unistd.h>
+#include <ctime>
 #include <ftw.h>
 #include <ftw.h>
+#include <fnmatch.h>
+#include <iostream>
+#include <sstream>
+#include <memory>
     
 #include "cachedb.h"
 #include "apt-ftparchive.h"
 #include "multicompress.h"
                                                                        /*}}}*/
     
 #include "cachedb.h"
 #include "apt-ftparchive.h"
 #include "multicompress.h"
                                                                        /*}}}*/
-
+using namespace std;
 FTWScanner *FTWScanner::Owner;
 
 FTWScanner *FTWScanner::Owner;
 
+// SetTFRewriteData - Helper for setting rewrite lists                 /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+inline void SetTFRewriteData(struct TFRewriteData &tfrd,
+                            const char *tag,
+                            const char *rewrite,
+                            const char *newtag = 0)
+{
+    tfrd.Tag = tag;
+    tfrd.Rewrite = rewrite;
+    tfrd.NewTag = newtag;
+}
+                                                                       /*}}}*/
+
 // FTWScanner::FTWScanner - Constructor                                        /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 // FTWScanner::FTWScanner - Constructor                                        /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-FTWScanner::FTWScanner()
+FTWScanner::FTWScanner(string const &Arch): Arch(Arch)
 {
    ErrorPrinted = false;
    NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
 {
    ErrorPrinted = false;
    NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
-   TmpExt = 0;
-   Ext[0] = 0;
-   RealPath = 0;
-   long PMax = pathconf(".",_PC_PATH_MAX);
-   if (PMax > 0)
-      RealPath = new char[PMax];
 }
                                                                        /*}}}*/
 // FTWScanner::Scanner - FTW Scanner                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This is the FTW scanner, it processes each directory element in the 
    directory tree. */
 }
                                                                        /*}}}*/
 // FTWScanner::Scanner - FTW Scanner                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This is the FTW scanner, it processes each directory element in the 
    directory tree. */
-int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
+int FTWScanner::ScannerFTW(const char *File,const struct stat *sb,int Flag)
 {
    if (Flag == FTW_DNR)
    {
       Owner->NewLine(1);
 {
    if (Flag == FTW_DNR)
    {
       Owner->NewLine(1);
-      c1out << "W: Unable to read directory " << File << endl;
+      ioprintf(c1out, _("W: Unable to read directory %s\n"), File);
    }   
    if (Flag == FTW_NS)
    {
       Owner->NewLine(1);
    }   
    if (Flag == FTW_NS)
    {
       Owner->NewLine(1);
-      c1out << "W: Unable to stat " << File << endl;
+      ioprintf(c1out, _("W: Unable to stat %s\n"), File);
    }   
    if (Flag != FTW_F)
       return 0;
 
    }   
    if (Flag != FTW_F)
       return 0;
 
-   // See if it is a .deb
-   if (strlen(File) < 4)
-      return 0;
-   
-   unsigned CurExt = 0;
-   for (; Owner->Ext[CurExt] != 0; CurExt++)
-      if (strcmp(File+strlen(File)-strlen(Owner->Ext[CurExt]),
-                Owner->Ext[CurExt]) == 0)
-        break;
-   if (Owner->Ext[CurExt] == 0)
+   return ScannerFile(File, true);
+}
+                                                                       /*}}}*/
+// FTWScanner::ScannerFile - File Scanner                              /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
+{
+   const char *LastComponent = strrchr(File, '/');
+   char *RealPath = NULL;
+
+   if (LastComponent == NULL)
+      LastComponent = File;
+   else
+      LastComponent++;
+
+   vector<string>::const_iterator I;
+   for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
+   {
+      if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
+         break;
+   }
+   if (I == Owner->Patterns.end())
       return 0;
 
    /* Process it. If the file is a link then resolve it into an absolute
       return 0;
 
    /* Process it. If the file is a link then resolve it into an absolute
@@ -85,9 +110,13 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
       given are not links themselves. */
    char Jnk[2];
    Owner->OriginalPath = File;
       given are not links themselves. */
    char Jnk[2];
    Owner->OriginalPath = File;
-   if (Owner->RealPath != 0 && readlink(File,Jnk,sizeof(Jnk)) != -1 &&
-       realpath(File,Owner->RealPath) != 0)
-      Owner->DoPackage(Owner->RealPath);
+   if (ReadLink &&
+       readlink(File,Jnk,sizeof(Jnk)) != -1 &&
+       (RealPath = realpath(File,NULL)) != 0)
+   {
+      Owner->DoPackage(RealPath);
+      free(RealPath);
+   }
    else
       Owner->DoPackage(File);
    
    else
       Owner->DoPackage(File);
    
@@ -100,18 +129,18 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
       {
         Owner->NewLine(1);
         
       {
         Owner->NewLine(1);
         
-        bool Type = _error->PopMessage(Err);
+        bool const Type = _error->PopMessage(Err);
         if (Type == true)
         if (Type == true)
-           c1out << "E: " << Err << endl;
+           cerr << _("E: ") << Err << endl;
         else
         else
-           c1out << "W: " << Err << endl;
+           cerr << _("W: ") << Err << endl;
         
         if (Err.find(File) != string::npos)
            SeenPath = true;
       }      
       
       if (SeenPath == false)
         
         if (Err.find(File) != string::npos)
            SeenPath = true;
       }      
       
       if (SeenPath == false)
-        cerr << "E: Errors apply to file '" << File << "'" << endl;
+        cerr << _("E: Errors apply to file ") << "'" << File << "'" << endl;
       return 0;
    }
    
       return 0;
    }
    
@@ -121,26 +150,28 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
 // FTWScanner::RecursiveScan - Just scan a directory tree              /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 // FTWScanner::RecursiveScan - Just scan a directory tree              /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool FTWScanner::RecursiveScan(string Dir)
+bool FTWScanner::RecursiveScan(string const &Dir)
 {
 {
+   char *RealPath = NULL;
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
-      if (realpath(Dir.c_str(),RealPath) == 0)
-        return _error->Errno("realpath","Failed to resolve %s",Dir.c_str());
-      InternalPrefix = RealPath;      
+      if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
+        return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
+      InternalPrefix = RealPath;
+      free(RealPath);
    }
    
    // Do recursive directory searching
    Owner = this;
    }
    
    // Do recursive directory searching
    Owner = this;
-   int Res = ftw(Dir.c_str(),Scanner,30);
+   int const Res = ftw(Dir.c_str(),ScannerFTW,30);
    
    // Error treewalking?
    if (Res != 0)
    {
       if (_error->PendingError() == false)
    
    // Error treewalking?
    if (Res != 0)
    {
       if (_error->PendingError() == false)
-        _error->Errno("ftw","Tree walking failed");
+        _error->Errno("ftw",_("Tree walking failed"));
       return false;
    }
    
       return false;
    }
    
@@ -151,21 +182,23 @@ bool FTWScanner::RecursiveScan(string Dir)
 // ---------------------------------------------------------------------
 /* This is an alternative to using FTW to locate files, it reads the list
    of files from another file. */
 // ---------------------------------------------------------------------
 /* This is an alternative to using FTW to locate files, it reads the list
    of files from another file. */
-bool FTWScanner::LoadFileList(string Dir,string File)
+bool FTWScanner::LoadFileList(string const &Dir, string const &File)
 {
 {
+   char *RealPath = NULL;
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
-      if (realpath(Dir.c_str(),RealPath) == 0)
-        return _error->Errno("realpath","Failed to resolve %s",Dir.c_str());
+      if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
+        return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
       InternalPrefix = RealPath;      
       InternalPrefix = RealPath;      
+      free(RealPath);
    }
    
    Owner = this;
    FILE *List = fopen(File.c_str(),"r");
    if (List == 0)
    }
    
    Owner = this;
    FILE *List = fopen(File.c_str(),"r");
    if (List == 0)
-      return _error->Errno("fopen","Failed to open %s",File.c_str());
+      return _error->Errno("fopen",_("Failed to open %s"),File.c_str());
    
    /* We are a tad tricky here.. We prefix the buffer with the directory
       name, that way if we need a full path with just use line.. Sneaky and
    
    /* We are a tad tricky here.. We prefix the buffer with the directory
       name, that way if we need a full path with just use line.. Sneaky and
@@ -189,12 +222,14 @@ bool FTWScanner::LoadFileList(string Dir,string File)
         FileName = Line;
       }
       
         FileName = Line;
       }
       
+#if 0
       struct stat St;
       int Flag = FTW_F;
       if (stat(FileName,&St) != 0)
         Flag = FTW_NS;
       struct stat St;
       int Flag = FTW_F;
       if (stat(FileName,&St) != 0)
         Flag = FTW_NS;
+#endif
 
 
-      if (Scanner(FileName,&St,Flag) != 0)
+      if (ScannerFile(FileName, false) != 0)
         break;
    }
   
         break;
    }
   
@@ -207,7 +242,7 @@ bool FTWScanner::LoadFileList(string Dir,string File)
 /* */
 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
                        unsigned long &DeLinkBytes,
 /* */
 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
                        unsigned long &DeLinkBytes,
-                       struct stat &St)
+                       off_t const &FileSize)
 {
    // See if this isn't an internaly prefix'd file name.
    if (InternalPrefix.empty() == false &&
 {
    // See if this isn't an internaly prefix'd file name.
    if (InternalPrefix.empty() == false &&
@@ -222,25 +257,26 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
            cout << endl;
         
         NewLine(1);
            cout << endl;
         
         NewLine(1);
-        c1out << " DeLink " << (OriginalPath + InternalPrefix.length())
-           << " [" << SizeToStr(St.st_size) << "B]" << endl << flush;
+        ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
+                   SizeToStr(FileSize).c_str());
+        c1out << flush;
         
         if (NoLinkAct == false)
         {
            char OldLink[400];
            if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
         
         if (NoLinkAct == false)
         {
            char OldLink[400];
            if (readlink(OriginalPath,OldLink,sizeof(OldLink)) == -1)
-              _error->Errno("readlink","Failed to readlink %s",OriginalPath);
+              _error->Errno("readlink",_("Failed to readlink %s"),OriginalPath);
            else
            {
               if (unlink(OriginalPath) != 0)
            else
            {
               if (unlink(OriginalPath) != 0)
-                 _error->Errno("unlink","Failed to unlink %s",OriginalPath);
+                 _error->Errno("unlink",_("Failed to unlink %s"),OriginalPath);
               else
               {
                  if (link(FileName.c_str(),OriginalPath) != 0)
                  {
                     // Panic! Restore the symlink
                     symlink(OldLink,OriginalPath);
               else
               {
                  if (link(FileName.c_str(),OriginalPath) != 0)
                  {
                     // Panic! Restore the symlink
                     symlink(OldLink,OriginalPath);
-                    return _error->Errno("link","*** Failed to link %s to %s",
+                    return _error->Errno("link",_("*** Failed to link %s to %s"),
                                          FileName.c_str(),
                                          OriginalPath);
                  }            
                                          FileName.c_str(),
                                          OriginalPath);
                  }            
@@ -248,9 +284,9 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
            }       
         }
         
            }       
         }
         
-        DeLinkBytes += St.st_size;
+        DeLinkBytes += FileSize;
         if (DeLinkBytes/1024 >= DeLinkLimit)
         if (DeLinkBytes/1024 >= DeLinkLimit)
-           c1out << " DeLink limit of " << SizeToStr(DeLinkBytes) << "B hit." << endl;      
+           ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());      
       }
       
       FileName = OriginalPath;
       }
       
       FileName = OriginalPath;
@@ -259,98 +295,119 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
    return true;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
-// FTWScanner::SetExts - Set extensions to support                     /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool FTWScanner::SetExts(string Vals)
-{
-   delete [] TmpExt;
-   TmpExt = new char[Vals.length()+1];
-   strcpy(TmpExt,Vals.c_str());
-   return TokSplitString(' ',TmpExt,(char **)Ext,sizeof(Ext)/sizeof(Ext[0]));
-}
-                                                                       /*}}}*/
 
 // PackagesWriter::PackagesWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 
 // PackagesWriter::PackagesWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-PackagesWriter::PackagesWriter(string DB,string Overrides) :
-                   Db(DB),Stats(Db.Stats)
+PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
+                              string const &Arch) :
+   FTWScanner(Arch), Db(DB), Stats(Db.Stats), TransWriter(NULL)
 {
    Output = stdout;
 {
    Output = stdout;
-   Ext[0] = ".deb";
-   Ext[1] = 0;
+   SetExts(".deb .udeb");
    DeLinkLimit = 0;
    
    // Process the command line options
    DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
    DeLinkLimit = 0;
    
    // Process the command line options
    DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
+   DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
+   DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
+   DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
    DoContents = _config->FindB("APT::FTPArchive::Contents",true);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
    DoContents = _config->FindB("APT::FTPArchive::Contents",true);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
+   LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
 
    if (Db.Loaded() == false)
       DoContents = false;
 
    if (Db.Loaded() == false)
       DoContents = false;
-       
+
    // Read the override file
    if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
       return;
    else
       NoOverride = true;
    // Read the override file
    if (Overrides.empty() == false && Over.ReadOverride(Overrides) == false)
       return;
    else
       NoOverride = true;
+
+   if (ExtOverrides.empty() == false)
+      Over.ReadExtraOverride(ExtOverrides);
+
    _error->DumpErrors();
 }
    _error->DumpErrors();
 }
+                                                                        /*}}}*/
+// FTWScanner::SetExts - Set extensions to support                      /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FTWScanner::SetExts(string const &Vals)
+{
+   ClearPatterns();
+   string::size_type Start = 0;
+   while (Start <= Vals.length()-1)
+   {
+      string::size_type const Space = Vals.find(' ',Start);
+      string::size_type const Length = ((Space == string::npos) ? Vals.length() : Space) - Start;
+      if ( Arch.empty() == false )
+      {
+        AddPattern(string("*_") + Arch + Vals.substr(Start, Length));
+        AddPattern(string("*_all") + Vals.substr(Start, Length));
+      }
+      else
+        AddPattern(string("*") + Vals.substr(Start, Length));
+
+      Start += Length + 1;
+   }
+
+   return true;
+}
+
                                                                        /*}}}*/
 // PackagesWriter::DoPackage - Process a single package                        /*{{{*/
 // ---------------------------------------------------------------------
 /* This method takes a package and gets its control information and 
                                                                        /*}}}*/
 // PackagesWriter::DoPackage - Process a single package                        /*{{{*/
 // ---------------------------------------------------------------------
 /* This method takes a package and gets its control information and 
-   MD5 then writes out a control record with the proper fields rewritten
-   and the path/size/hash appended. */
+   MD5, SHA1 and SHA256 then writes out a control record with the proper fields 
+   rewritten and the path/size/hash appended. */
 bool PackagesWriter::DoPackage(string FileName)
 {      
 bool PackagesWriter::DoPackage(string FileName)
 {      
-   // Open the archive
-   FileFd F(FileName,FileFd::ReadOnly);
-   if (_error->PendingError() == true)
-      return false;
-   
-   // Stat the file for later
-   struct stat St;
-   if (fstat(F.Fd(),&St) != 0)
-      return _error->Errno("fstat","Failed to stat %s",FileName.c_str());
-
    // Pull all the data we need form the DB
    // Pull all the data we need form the DB
-   string MD5Res;
-   if (Db.SetFile(FileName,St,&F) == false ||
-       Db.LoadControl() == false ||
-       (DoContents == true && Db.LoadContents(true) == false) ||
-       (DoMD5 == true && Db.GetMD5(MD5Res,false) == false))
+   if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256, DoAlwaysStat)
+                 == false)
+   {
       return false;
       return false;
+   }
 
 
-   if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,St) == false)
+   off_t FileSize = Db.GetFileSize();
+   if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
       return false;
    
    // Lookup the overide information
    pkgTagSection &Tags = Db.Control.Section;
    string Package = Tags.FindS("Package");
       return false;
    
    // Lookup the overide information
    pkgTagSection &Tags = Db.Control.Section;
    string Package = Tags.FindS("Package");
-   Override::Item Tmp;
-   Override::Item *OverItem = Over.GetItem(Package);
+   string Architecture;
+   // if we generate a Packages file for a given arch, we use it to
+   // look for overrides. if we run in "simple" mode without the 
+   // "Architecures" variable in the config we use the architecure value
+   // from the deb file
+   if(Arch != "")
+      Architecture = Arch;
+   else
+      Architecture = Tags.FindS("Architecture");
+   auto_ptr<Override::Item> OverItem(Over.GetItem(Package,Architecture));
    
    if (Package.empty() == true)
    
    if (Package.empty() == true)
-      return _error->Error("Archive had no package field");
-   
+      return _error->Error(_("Archive had no package field"));
+
    // If we need to do any rewriting of the header do it now..
    // If we need to do any rewriting of the header do it now..
-   if (OverItem == 0)
+   if (OverItem.get() == 0)
    {
       if (NoOverride == false)
       {
         NewLine(1);
    {
       if (NoOverride == false)
       {
         NewLine(1);
-        c1out << "  " << Package << " has no override entry" << endl;
+        ioprintf(c1out, _("  %s has no override entry\n"), Package.c_str());
       }
       
       }
       
-      OverItem = &Tmp;
-      Tmp.Section = Tags.FindS("Section");
-      Tmp.Priority = Tags.FindS("Priority");
+      OverItem = auto_ptr<Override::Item>(new Override::Item);
+      OverItem->FieldOverride["Section"] = Tags.FindS("Section");
+      OverItem->Priority = Tags.FindS("Priority");
    }
 
    char Size[40];
    }
 
    char Size[40];
-   sprintf(Size,"%lu",St.st_size);
+   sprintf(Size,"%lu", (unsigned long) FileSize);
    
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
    
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
@@ -363,21 +420,39 @@ bool PackagesWriter::DoPackage(string FileName)
       NewFileName = FileName;
    if (PathPrefix.empty() == false)
       NewFileName = flCombine(PathPrefix,NewFileName);
       NewFileName = FileName;
    if (PathPrefix.empty() == false)
       NewFileName = flCombine(PathPrefix,NewFileName);
-          
+
+   /* Configuration says we don't want to include the long Description
+      in the package file - instead we want to ship a separated file */
+   string desc;
+   if (LongDescription == false) {
+      desc = Tags.FindS("Description").append("\n");
+      OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
+   }
+
    // This lists all the changes to the fields we are going to make.
    // This lists all the changes to the fields we are going to make.
-   TFRewriteData Changes[] = {{"Size",Size},
-                              {"MD5sum",MD5Res.c_str()},
-                              {"Filename",NewFileName.c_str()},
-                              {"Section",OverItem->Section.c_str()},
-                              {"Priority",OverItem->Priority.c_str()},
-                              {"Status",0},
-                              {"Optional",0},
-                              {},  // For maintainer
-                              {},  // For Suggests
-                             {}};
+   // (7 hardcoded + maintainer + suggests + end marker)
+   TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
+
    unsigned int End = 0;
    unsigned int End = 0;
-   for (End = 0; Changes[End].Tag != 0; End++);
-   
+   SetTFRewriteData(Changes[End++], "Size", Size);
+   SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
+   SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
+   SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
+   SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
+   SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
+   SetTFRewriteData(Changes[End++], "Status", 0);
+   SetTFRewriteData(Changes[End++], "Optional", 0);
+
+   string DescriptionMd5;
+   if (LongDescription == false) {
+      MD5Summation descmd5;
+      descmd5.Add(desc.c_str());
+      DescriptionMd5 = descmd5.Result().Value();
+      SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
+      if (TransWriter != NULL)
+        TransWriter->DoPackage(Package, desc, DescriptionMd5);
+   }
+
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
    string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
    string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
@@ -386,20 +461,16 @@ bool PackagesWriter::DoPackage(string FileName)
       if (NoOverride == false)
       {
         NewLine(1);
       if (NoOverride == false)
       {
         NewLine(1);
-        c1out << "  " << Package << " maintainer is " <<
-              Tags.FindS("Maintainer") << " not " <<
-              OverItem->OldMaint << endl;
+        ioprintf(c1out, _("  %s maintainer is %s not %s\n"),
+              Package.c_str(), Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
       }      
    }
    
    if (NewMaint.empty() == false)
       }      
    }
    
    if (NewMaint.empty() == false)
-   {
-      Changes[End].Rewrite = NewMaint.c_str();
-      Changes[End++].Tag = "Maintainer";
-   }
+      SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
    
    /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
    
    /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
-      dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
+      dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
       but dpkg does this append bit. So we do the append bit, at least that way the
       status file and package file will remain similar. There are other transforms
       but optional is the only legacy one still in use for some lazy reason. */
       but dpkg does this append bit. So we do the append bit, at least that way the
       status file and package file will remain similar. There are other transforms
       but optional is the only legacy one still in use for some lazy reason. */
@@ -408,10 +479,15 @@ bool PackagesWriter::DoPackage(string FileName)
    {
       if (Tags.FindS("Suggests").empty() == false)
         OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
    {
       if (Tags.FindS("Suggests").empty() == false)
         OptionalStr = Tags.FindS("Suggests") + ", " + OptionalStr;
-      Changes[End].Rewrite = OptionalStr.c_str();
-      Changes[End++].Tag = "Suggests";
+      SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
    }
    }
-   
+
+   for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin(); 
+        I != OverItem->FieldOverride.end(); I++) 
+      SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
+
+   SetTFRewriteData(Changes[End++], 0, 0);
+
    // Rewrite and store the fields.
    if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
       return false;
    // Rewrite and store the fields.
    if (TFRewrite(Output,Tags,TFRewritePackageOrder,Changes) == false)
       return false;
@@ -421,14 +497,63 @@ bool PackagesWriter::DoPackage(string FileName)
 }
                                                                        /*}}}*/
 
 }
                                                                        /*}}}*/
 
+// TranslationWriter::TranslationWriter - Constructor                  /*{{{*/
+// ---------------------------------------------------------------------
+/* Create a Translation-Master file for this Packages file */
+TranslationWriter::TranslationWriter(string const &File, string const &TransCompress,
+                                       mode_t const &Permissions) : Output(NULL),
+                                                       RefCounter(0)
+{
+   if (File.empty() == true)
+      return;
+
+   Comp = new MultiCompress(File, TransCompress, Permissions);
+   Output = Comp->Input;
+}
+                                                                       /*}}}*/
+// TranslationWriter::DoPackage - Process a single package             /*{{{*/
+// ---------------------------------------------------------------------
+/* Create a Translation-Master file for this Packages file */
+bool TranslationWriter::DoPackage(string const &Pkg, string const &Desc,
+                                 string const &MD5)
+{
+   if (Output == NULL)
+      return true;
+
+   // Different archs can include different versions and therefore
+   // different descriptions - so we need to check for both name and md5.
+   string const Record = Pkg + ":" + MD5;
+
+   if (Included.find(Record) != Included.end())
+      return true;
+
+   fprintf(Output, "Package: %s\nDescription-md5: %s\nDescription-en: %s\n",
+          Pkg.c_str(), MD5.c_str(), Desc.c_str());
+
+   Included.insert(Record);
+   return true;
+}
+                                                                       /*}}}*/
+// TranslationWriter::~TranslationWriter - Destructor                  /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+TranslationWriter::~TranslationWriter()
+{
+   if (Comp == NULL)
+      return;
+
+   delete Comp;
+}
+                                                                       /*}}}*/
+
 // SourcesWriter::SourcesWriter - Constructor                          /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 // SourcesWriter::SourcesWriter - Constructor                          /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-SourcesWriter::SourcesWriter(string BOverrides,string SOverrides)
+SourcesWriter::SourcesWriter(string const &BOverrides,string const &SOverrides,
+                            string const &ExtOverrides)
 {
    Output = stdout;
 {
    Output = stdout;
-   Ext[0] = ".dsc";
-   Ext[1] = 0;
+   AddPattern("*.dsc");
    DeLinkLimit = 0;
    Buffer = 0;
    BufSize = 0;
    DeLinkLimit = 0;
    Buffer = 0;
    BufSize = 0;
@@ -441,11 +566,16 @@ SourcesWriter::SourcesWriter(string BOverrides,string SOverrides)
       return;
    else
       NoOverride = true;
       return;
    else
       NoOverride = true;
+
+   // WTF?? The logic above: if we can't read binary overrides, don't even try
+   // reading source overrides. if we can read binary overrides, then say there
+   // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
+
+   if (ExtOverrides.empty() == false)
+      SOver.ReadExtraOverride(ExtOverrides);
    
    
-   if (SOverrides.empty() == false && FileExists(SOverrides) == true &&
-       SOver.ReadOverride(SOverrides,true) == false)
-      return;
-//   _error->DumpErrors();
+   if (SOverrides.empty() == false && FileExists(SOverrides) == true)
+      SOver.ReadOverride(SOverrides,true);
 }
                                                                        /*}}}*/
 // SourcesWriter::DoPackage - Process a single package                 /*{{{*/
 }
                                                                        /*}}}*/
 // SourcesWriter::DoPackage - Process a single package                 /*{{{*/
@@ -480,7 +610,12 @@ bool SourcesWriter::DoPackage(string FileName)
    char *BlkEnd = Buffer + St.st_size;
    MD5Summation MD5;
    MD5.Add((unsigned char *)Start,BlkEnd - Start);
    char *BlkEnd = Buffer + St.st_size;
    MD5Summation MD5;
    MD5.Add((unsigned char *)Start,BlkEnd - Start);
-      
+
+   SHA1Summation SHA1;
+   SHA256Summation SHA256;
+   SHA1.Add((unsigned char *)Start,BlkEnd - Start);
+   SHA256.Add((unsigned char *)Start,BlkEnd - Start);
+
    // Add an extra \n to the end, just in case
    *BlkEnd++ = '\n';
    
    // Add an extra \n to the end, just in case
    *BlkEnd++ = '\n';
    
@@ -513,10 +648,10 @@ bool SourcesWriter::DoPackage(string FileName)
       
    // Lookup the overide information, finding first the best priority.
    string BestPrio;
       
    // Lookup the overide information, finding first the best priority.
    string BestPrio;
-   char Buffer[1000];
    string Bins = Tags.FindS("Binary");
    string Bins = Tags.FindS("Binary");
-   Override::Item *OverItem = 0;
-   if (Bins.empty() == false && Bins.length() < sizeof(Buffer))
+   char Buffer[Bins.length() + 1];
+   auto_ptr<Override::Item> OverItem(0);
+   if (Bins.empty() == false)
    {
       strcpy(Buffer,Bins.c_str());
       
    {
       strcpy(Buffer,Bins.c_str());
       
@@ -528,11 +663,9 @@ bool SourcesWriter::DoPackage(string FileName)
       unsigned char BestPrioV = pkgCache::State::Extra;
       for (unsigned I = 0; BinList[I] != 0; I++)
       {
       unsigned char BestPrioV = pkgCache::State::Extra;
       for (unsigned I = 0; BinList[I] != 0; I++)
       {
-        Override::Item *Itm = BOver.GetItem(BinList[I]);
-        if (Itm == 0)
+        auto_ptr<Override::Item> Itm(BOver.GetItem(BinList[I]));
+        if (Itm.get() == 0)
            continue;
            continue;
-        if (OverItem == 0)
-           OverItem = Itm;
 
         unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
         if (NewPrioV < BestPrioV || BestPrio.empty() == true)
 
         unsigned char NewPrioV = debListParser::GetPrio(Itm->Priority);
         if (NewPrioV < BestPrioV || BestPrio.empty() == true)
@@ -540,55 +673,73 @@ bool SourcesWriter::DoPackage(string FileName)
            BestPrioV = NewPrioV;
            BestPrio = Itm->Priority;
         }       
            BestPrioV = NewPrioV;
            BestPrio = Itm->Priority;
         }       
+
+        if (OverItem.get() == 0)
+           OverItem = Itm;
       }
    }
    
    // If we need to do any rewriting of the header do it now..
       }
    }
    
    // If we need to do any rewriting of the header do it now..
-   Override::Item Tmp;   
-   if (OverItem == 0)
+   if (OverItem.get() == 0)
    {
       if (NoOverride == false)
       {
         NewLine(1);     
    {
       if (NoOverride == false)
       {
         NewLine(1);     
-        c1out << "  " << Tags.FindS("Source") << " has no override entry" << endl;
+        ioprintf(c1out, _("  %s has no override entry\n"), Tags.FindS("Source").c_str());
       }
       
       }
       
-      OverItem = &Tmp;
+      OverItem = auto_ptr<Override::Item>(new Override::Item);
    }
    
    }
    
-   Override::Item *SOverItem = SOver.GetItem(Tags.FindS("Source"));
-   if (SOverItem == 0)
+   auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
+   // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
+   if (SOverItem.get() == 0)
    {
    {
-      SOverItem = BOver.GetItem(Tags.FindS("Source"));
-      if (SOverItem == 0)
-        SOverItem = OverItem;
+      ioprintf(c1out, _("  %s has no source override entry\n"), Tags.FindS("Source").c_str());
+      SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
+      if (SOverItem.get() == 0)
+      {
+        ioprintf(c1out, _("  %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
+        SOverItem = auto_ptr<Override::Item>(new Override::Item);
+        *SOverItem = *OverItem;
+      }
    }
    
    // Add the dsc to the files hash list
    }
    
    // Add the dsc to the files hash list
-   char Files[1000];
-   snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
-           string(MD5.Result()).c_str(),St.st_size,
-           flNotDir(FileName).c_str(),
-           Tags.FindS("Files").c_str());
-   
+   string const strippedName = flNotDir(FileName);
+   std::ostringstream ostreamFiles;
+   ostreamFiles << "\n " << string(MD5.Result()) << " " << St.st_size << " "
+               << strippedName << "\n " << Tags.FindS("Files");
+   string const Files = ostreamFiles.str();
+
+   std::ostringstream ostreamSha1;
+   ostreamSha1 << "\n " << string(SHA1.Result()) << " " << St.st_size << " "
+               << strippedName << "\n " << Tags.FindS("Checksums-Sha1");
+   string const ChecksumsSha1 = ostreamSha1.str();
+
+   std::ostringstream ostreamSha256;
+   ostreamSha256 << "\n " << string(SHA256.Result()) << " " << St.st_size << " "
+               << strippedName << "\n " << Tags.FindS("Checksums-Sha256");
+   string const ChecksumsSha256 = ostreamSha256.str();
+
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
    if (DirStrip.empty() == false &&
        FileName.length() > DirStrip.length() &&
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
    if (DirStrip.empty() == false &&
        FileName.length() > DirStrip.length() &&
-       stringcmp(OriginalPath,OriginalPath + DirStrip.length(),
-                DirStrip.begin(),DirStrip.end()) == 0)
+       stringcmp(DirStrip,OriginalPath,OriginalPath + DirStrip.length()) == 0)
       NewFileName = string(OriginalPath + DirStrip.length());
    else 
       NewFileName = OriginalPath;
    if (PathPrefix.empty() == false)
       NewFileName = flCombine(PathPrefix,NewFileName);
       NewFileName = string(OriginalPath + DirStrip.length());
    else 
       NewFileName = OriginalPath;
    if (PathPrefix.empty() == false)
       NewFileName = flCombine(PathPrefix,NewFileName);
-    
+
    string Directory = flNotFile(OriginalPath);
    string Package = Tags.FindS("Source");
    string Directory = flNotFile(OriginalPath);
    string Package = Tags.FindS("Source");
-   
+
    // Perform the delinking operation over all of the files
    string ParseJnk;
    // Perform the delinking operation over all of the files
    string ParseJnk;
-   const char *C = Files;
+   const char *C = Files.c_str();
+   char *RealPath = NULL;
    for (;isspace(*C); C++);
    while (*C != 0)
    {   
    for (;isspace(*C); C++);
    while (*C != 0)
    {   
@@ -600,11 +751,12 @@ bool SourcesWriter::DoPackage(string FileName)
       
       char Jnk[2];
       string OriginalPath = Directory + ParseJnk;
       
       char Jnk[2];
       string OriginalPath = Directory + ParseJnk;
-      if (RealPath != 0 && readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
-         realpath(OriginalPath.c_str(),RealPath) != 0)
+      if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
+         (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
       {
         string RP = RealPath;
       {
         string RP = RealPath;
-        if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St) == false)
+        free(RealPath);
+        if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
            return false;
       }
    }
            return false;
       }
    }
@@ -612,18 +764,20 @@ bool SourcesWriter::DoPackage(string FileName)
    Directory = flNotFile(NewFileName);
    if (Directory.length() > 2)
       Directory.erase(Directory.end()-1);
    Directory = flNotFile(NewFileName);
    if (Directory.length() > 2)
       Directory.erase(Directory.end()-1);
-      
+
    // This lists all the changes to the fields we are going to make.
    // This lists all the changes to the fields we are going to make.
-   TFRewriteData Changes[] = {{"Source",Package.c_str(),"Package"},
-                              {"Files",Files},
-                              {"Directory",Directory.c_str()},
-                              {"Section",SOverItem->Section.c_str()},
-                              {"Priority",BestPrio.c_str()},
-                              {"Status",0},
-                              {},  // For maintainer
-                             {}};
+   // (5 hardcoded + checksums + maintainer + end marker)
+   TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
+
    unsigned int End = 0;
    unsigned int End = 0;
-   for (End = 0; Changes[End].Tag != 0; End++);
+   SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
+   SetTFRewriteData(Changes[End++],"Files",Files.c_str());
+   SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1.c_str());
+   SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256.c_str());
+   if (Directory != "./")
+      SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
+   SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
+   SetTFRewriteData(Changes[End++],"Status",0);
 
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
 
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
@@ -633,16 +787,18 @@ bool SourcesWriter::DoPackage(string FileName)
       if (NoOverride == false)
       {
         NewLine(1);     
       if (NoOverride == false)
       {
         NewLine(1);     
-        c1out << "  " << Package << " maintainer is " <<
-              Tags.FindS("Maintainer") << " not " <<
-              OverItem->OldMaint << endl;
+        ioprintf(c1out, _("  %s maintainer is %s not %s\n"), Package.c_str(),
+              Tags.FindS("Maintainer").c_str(), OverItem->OldMaint.c_str());
       }      
    }
    if (NewMaint.empty() == false)
       }      
    }
    if (NewMaint.empty() == false)
-   {
-      Changes[End].Rewrite = NewMaint.c_str();
-      Changes[End++].Tag = "Maintainer";
-   }
+      SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
+   
+   for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin(); 
+        I != SOverItem->FieldOverride.end(); I++) 
+      SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
+
+   SetTFRewriteData(Changes[End++], 0, 0);
       
    // Rewrite and store the fields.
    if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
       
    // Rewrite and store the fields.
    if (TFRewrite(Output,Tags,TFRewriteSourceOrder,Changes) == false)
@@ -658,12 +814,11 @@ bool SourcesWriter::DoPackage(string FileName)
 // ContentsWriter::ContentsWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 // ContentsWriter::ContentsWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-ContentsWriter::ContentsWriter(string DB) : 
-                   Db(DB), Stats(Db.Stats)
+ContentsWriter::ContentsWriter(string const &DB, string const &Arch) :
+                   FTWScanner(Arch), Db(DB), Stats(Db.Stats)
 
 {
 
 {
-   Ext[0] = ".deb";
-   Ext[1] = 0;
+   SetExts(".deb");
    Output = stdout;
 }
                                                                        /*}}}*/
    Output = stdout;
 }
                                                                        /*}}}*/
@@ -671,28 +826,16 @@ ContentsWriter::ContentsWriter(string DB) :
 // ---------------------------------------------------------------------
 /* If Package is the empty string the control record will be parsed to
    determine what the package name is. */
 // ---------------------------------------------------------------------
 /* If Package is the empty string the control record will be parsed to
    determine what the package name is. */
-bool ContentsWriter::DoPackage(string FileName,string Package)
+bool ContentsWriter::DoPackage(string FileName, string Package)
 {
 {
-   // Open the archive
-   FileFd F(FileName,FileFd::ReadOnly);
-   if (_error->PendingError() == true)
-      return false;
-   
-   // Stat the file for later
-   struct stat St;
-   if (fstat(F.Fd(),&St) != 0)
-      return _error->Errno("fstat","Failed too stat %s",FileName.c_str());
-
-   // Ready the DB
-   if (Db.SetFile(FileName,St,&F) == false || 
-       Db.LoadContents(false) == false)
+   if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false, false))
+   {
       return false;
       return false;
+   }
 
    // Parse the package name
    if (Package.empty() == true)
    {
 
    // Parse the package name
    if (Package.empty() == true)
    {
-      if (Db.LoadControl() == false)
-        return false;
       Package = Db.Control.Section.FindS("Package");
    }
 
       Package = Db.Control.Section.FindS("Package");
    }
 
@@ -704,7 +847,7 @@ bool ContentsWriter::DoPackage(string FileName,string Package)
 // ContentsWriter::ReadFromPkgs - Read from a packages file            /*{{{*/
 // ---------------------------------------------------------------------
 /* */
 // ContentsWriter::ReadFromPkgs - Read from a packages file            /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
+bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
 {
    MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
    if (_error->PendingError() == true)
 {
    MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
    if (_error->PendingError() == true)
@@ -712,7 +855,7 @@ bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
    
    // Open the package file
    int CompFd = -1;
    
    // Open the package file
    int CompFd = -1;
-   int Proc = -1;
+   pid_t Proc = -1;
    if (Pkgs.OpenOld(CompFd,Proc) == false)
       return false;
    
    if (Pkgs.OpenOld(CompFd,Proc) == false)
       return false;
    
@@ -753,4 +896,153 @@ bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
    
    return true;
 }
    
    return true;
 }
+
+                                                                       /*}}}*/
+
+// ReleaseWriter::ReleaseWriter - Constructor                          /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ReleaseWriter::ReleaseWriter(string const &DB)
+{
+   AddPattern("Packages");
+   AddPattern("Packages.gz");
+   AddPattern("Packages.bz2");
+   AddPattern("Packages.lzma");
+   AddPattern("Sources");
+   AddPattern("Sources.gz");
+   AddPattern("Sources.bz2");
+   AddPattern("Sources.lzma");
+   AddPattern("Release");
+   AddPattern("md5sum.txt");
+
+   Output = stdout;
+   time_t const now = time(NULL);
+   char datestr[128];
+   if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
+                gmtime(&now)) == 0)
+   {
+      datestr[0] = '\0';
+   }
+
+   time_t const validuntil = now + _config->FindI("APT::FTPArchive::Release::ValidTime", 0);
+   char validstr[128] = "";
+   if (now == validuntil ||
+       strftime(validstr, sizeof(validstr), "%a, %d %b %Y %H:%M:%S UTC",
+                gmtime(&validuntil)) == 0)
+   {
+      datestr[0] = '\0';
+   }
+
+   map<string,string> Fields;
+   Fields["Origin"] = "";
+   Fields["Label"] = "";
+   Fields["Suite"] = "";
+   Fields["Version"] = "";
+   Fields["Codename"] = "";
+   Fields["Date"] = datestr;
+   Fields["Valid-Until"] = validstr;
+   Fields["Architectures"] = "";
+   Fields["Components"] = "";
+   Fields["Description"] = "";
+
+   for(map<string,string>::const_iterator I = Fields.begin();
+       I != Fields.end();
+       ++I)
+   {
+      string Config = string("APT::FTPArchive::Release::") + (*I).first;
+      string Value = _config->Find(Config, (*I).second.c_str());
+      if (Value == "")
+         continue;
+
+      fprintf(Output, "%s: %s\n", (*I).first.c_str(), Value.c_str());
+   }
+}
+                                                                       /*}}}*/
+// ReleaseWriter::DoPackage - Process a single package                 /*{{{*/
+// ---------------------------------------------------------------------
+bool ReleaseWriter::DoPackage(string FileName)
+{
+   // Strip the DirStrip prefix from the FileName and add the PathPrefix
+   string NewFileName;
+   if (DirStrip.empty() == false &&
+       FileName.length() > DirStrip.length() &&
+       stringcmp(FileName.begin(),FileName.begin() + DirStrip.length(),
+                DirStrip.begin(),DirStrip.end()) == 0)
+   {
+      NewFileName = string(FileName.begin() + DirStrip.length(),FileName.end());
+      while (NewFileName[0] == '/')
+         NewFileName = string(NewFileName.begin() + 1,NewFileName.end());
+   }
+   else 
+      NewFileName = FileName;
+
+   if (PathPrefix.empty() == false)
+      NewFileName = flCombine(PathPrefix,NewFileName);
+
+   FileFd fd(FileName, FileFd::ReadOnly);
+
+   if (!fd.IsOpen())
+   {
+      return false;
+   }
+
+   CheckSums[NewFileName].size = fd.Size();
+
+   MD5Summation MD5;
+   MD5.AddFD(fd.Fd(), fd.Size());
+   CheckSums[NewFileName].MD5 = MD5.Result();
+
+   fd.Seek(0);
+   SHA1Summation SHA1;
+   SHA1.AddFD(fd.Fd(), fd.Size());
+   CheckSums[NewFileName].SHA1 = SHA1.Result();
+
+   fd.Seek(0);
+   SHA256Summation SHA256;
+   SHA256.AddFD(fd.Fd(), fd.Size());
+   CheckSums[NewFileName].SHA256 = SHA256.Result();
+
+   fd.Close();
+   
+   return true;
+}
+
                                                                        /*}}}*/
                                                                        /*}}}*/
+// ReleaseWriter::Finish - Output the checksums                                /*{{{*/
+// ---------------------------------------------------------------------
+void ReleaseWriter::Finish()
+{
+   fprintf(Output, "MD5Sum:\n");
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
+       I != CheckSums.end();
+       ++I)
+   {
+      fprintf(Output, " %s %16ld %s\n",
+              (*I).second.MD5.c_str(),
+              (*I).second.size,
+              (*I).first.c_str());
+   }
+
+   fprintf(Output, "SHA1:\n");
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
+       I != CheckSums.end();
+       ++I)
+   {
+      fprintf(Output, " %s %16ld %s\n",
+              (*I).second.SHA1.c_str(),
+              (*I).second.size,
+              (*I).first.c_str());
+   }
+
+   fprintf(Output, "SHA256:\n");
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
+       I != CheckSums.end();
+       ++I)
+   {
+      fprintf(Output, " %s %16ld %s\n",
+              (*I).second.SHA256.c_str(),
+              (*I).second.size,
+              (*I).first.c_str());
+   }
+}
+