// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
/* ######################################################################

   Debian Specific sources.list types and the three sorts of Debian
   index files.
   
   ##################################################################### */
									/*}}}*/
// Include Files							/*{{{*/
#include <config.h>

#include <apt-pkg/configuration.h>
#include <apt-pkg/debindexfile.h>
#include <apt-pkg/deblistparser.h>
#include <apt-pkg/debrecords.h>
#include <apt-pkg/debsrcrecords.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/indexfile.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/srcrecords.h>

#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <stdio.h>

#include <sys/stat.h>
#include <unistd.h>
									/*}}}*/

// Sources Index							/*{{{*/
debSourcesIndex::debSourcesIndex(IndexTarget const &Target,bool const Trusted) :
     pkgDebianIndexTargetFile(Target, Trusted), d(NULL)
{
}
std::string debSourcesIndex::SourceInfo(pkgSrcRecords::Parser const &Record,
				   pkgSrcRecords::File const &File) const
{
   // The result looks like: http://foo/debian/ stable/main src 1.1.1 (dsc)
   std::string Res = Target.Description;
   Res.erase(Target.Description.rfind(' '));

   Res += " ";
   Res += Record.Package();
   Res += " ";
   Res += Record.Version();
   if (File.Type.empty() == false)
      Res += " (" + File.Type + ")";
   return Res;
}
pkgSrcRecords::Parser *debSourcesIndex::CreateSrcParser() const
{
   std::string const SourcesURI = IndexFileName();
   if (FileExists(SourcesURI))
      return new debSrcRecordParser(SourcesURI, this);
   return NULL;
}
bool debSourcesIndex::OpenListFile(FileFd &, std::string const &)
{
   return true;
}
pkgCacheListParser * debSourcesIndex::CreateListParser(FileFd &)
{
   return nullptr;
}
uint8_t debSourcesIndex::GetIndexFlags() const
{
   return 0;
}
									/*}}}*/
// Packages Index							/*{{{*/
debPackagesIndex::debPackagesIndex(IndexTarget const &Target, bool const Trusted) :
                  pkgDebianIndexTargetFile(Target, Trusted), d(NULL)
{
}
std::string debPackagesIndex::ArchiveInfo(pkgCache::VerIterator const &Ver) const
{
   std::string Res = Target.Description;
   {
      auto const space = Target.Description.rfind(' ');
      if (space != std::string::npos)
	 Res.erase(space);
   }

   Res += " ";
   Res += Ver.ParentPkg().Name();
   Res += " ";
   std::string const Dist = Target.Option(IndexTarget::RELEASE);
   if (Dist.empty() == false && Dist[Dist.size() - 1] != '/')
      Res.append(Ver.Arch()).append(" ");
   Res += Ver.VerStr();
   return Res;
}
uint8_t debPackagesIndex::GetIndexFlags() const
{
   return 0;
}
									/*}}}*/
// Translation-* Index							/*{{{*/
debTranslationsIndex::debTranslationsIndex(IndexTarget const &Target) :
			pkgDebianIndexTargetFile(Target, true), d(NULL)
{}
bool debTranslationsIndex::HasPackages() const
{
   return Exists();
}
bool debTranslationsIndex::OpenListFile(FileFd &Pkg, std::string const &FileName)
{
   if (FileExists(FileName))
      return pkgDebianIndexTargetFile::OpenListFile(Pkg, FileName);
   return true;
}
uint8_t debTranslationsIndex::GetIndexFlags() const
{
   return pkgCache::Flag::NotSource | pkgCache::Flag::NoPackages;
}
std::string debTranslationsIndex::GetArchitecture() const
{
   return std::string();
}
pkgCacheListParser * debTranslationsIndex::CreateListParser(FileFd &Pkg)
{
   if (Pkg.IsOpen() == false)
      return nullptr;
   _error->PushToStack();
   std::unique_ptr<pkgCacheListParser> Parser(new debTranslationsParser(&Pkg));
   bool const newError = _error->PendingError();
   _error->MergeWithStack();
   return newError ? nullptr : Parser.release();
}
									/*}}}*/
// dpkg/status Index							/*{{{*/
debStatusIndex::debStatusIndex(std::string const &File) : pkgDebianIndexRealFile(File, true), d(NULL)
{
}
std::string debStatusIndex::GetArchitecture() const
{
   return std::string();
}
std::string debStatusIndex::GetComponent() const
{
   return "now";
}
uint8_t debStatusIndex::GetIndexFlags() const
{
   return pkgCache::Flag::NotSource;
}

pkgCacheListParser * debStatusIndex::CreateListParser(FileFd &Pkg)
{
   if (Pkg.IsOpen() == false)
      return nullptr;
   _error->PushToStack();
   std::unique_ptr<pkgCacheListParser> Parser(new debStatusListParser(&Pkg));
   bool const newError = _error->PendingError();
   _error->MergeWithStack();
   return newError ? nullptr : Parser.release();
}
									/*}}}*/
// DebPkgFile Index - a single .deb file as an index			/*{{{*/
debDebPkgFileIndex::debDebPkgFileIndex(std::string const &DebFile)
   : pkgDebianIndexRealFile(DebFile, true), d(NULL), DebFile(DebFile)
{
}
bool debDebPkgFileIndex::GetContent(std::ostream &content, std::string const &debfile)
{
   struct stat Buf;
   if (stat(debfile.c_str(), &Buf) != 0)
      return false;

   // get the control data out of the deb file via dpkg-deb -I
   std::string dpkg = _config->Find("Dir::Bin::dpkg","dpkg-deb");
   std::vector<const char *> Args;
   Args.push_back(dpkg.c_str());
   Args.push_back("-I");
   Args.push_back(debfile.c_str());
   Args.push_back("control");
   Args.push_back(NULL);
   FileFd PipeFd;
   pid_t Child;
   if(Popen((const char**)&Args[0], PipeFd, Child, FileFd::ReadOnly) == false)
      return _error->Error("Popen failed");

   std::string line;
   bool first_line_seen = false;
   while (PipeFd.ReadLine(line))
   {
      if (first_line_seen == false)
      {
	 if (line.empty())
	    continue;
	 first_line_seen = true;
      }
      else if (line.empty())
	 break;
      content << line << "\n";
   }
   content << "Filename: " << debfile << "\n";
   content << "Size: " << std::to_string(Buf.st_size) << "\n";
   ExecWait(Child, "Popen");

   return true;
}
bool debDebPkgFileIndex::OpenListFile(FileFd &Pkg, std::string const &FileName)
{
   // write the control data to a tempfile
   if (GetTempFile("deb-file-" + flNotDir(FileName), true, &Pkg) == NULL)
      return false;
   std::ostringstream content;
   if (GetContent(content, FileName) == false)
      return false;
   std::string const contentstr = content.str();
   if (contentstr.empty())
      return true;
   if (Pkg.Write(contentstr.c_str(), contentstr.length()) == false || Pkg.Seek(0) == false)
      return false;
   return true;
}
pkgCacheListParser * debDebPkgFileIndex::CreateListParser(FileFd &Pkg)
{
   if (Pkg.IsOpen() == false)
      return nullptr;
   _error->PushToStack();
   std::unique_ptr<pkgCacheListParser> Parser(new debDebFileParser(&Pkg, DebFile));
   bool const newError = _error->PendingError();
   _error->MergeWithStack();
   return newError ? nullptr : Parser.release();
}
uint8_t debDebPkgFileIndex::GetIndexFlags() const
{
   return pkgCache::Flag::LocalSource;
}
std::string debDebPkgFileIndex::GetArchitecture() const
{
   return std::string();
}
std::string debDebPkgFileIndex::GetComponent() const
{
   return "local-deb";
}
pkgCache::PkgFileIterator debDebPkgFileIndex::FindInCache(pkgCache &Cache) const
{
   std::string const FileName = IndexFileName();
   pkgCache::PkgFileIterator File = Cache.FileBegin();
   for (; File.end() == false; ++File)
   {
       if (File.FileName() == NULL || FileName != File.FileName())
	 continue;
      // we can't do size checks here as file size != content size
      return File;
   }

   return File;
}
std::string debDebPkgFileIndex::ArchiveInfo_impl(pkgCache::VerIterator const &Ver) const
{
   std::string Res = IndexFileName() + " ";
   Res.append(Ver.ParentPkg().Name()).append(" ");
   Res.append(Ver.Arch()).append(" ");
   Res.append(Ver.VerStr());
   return Res;
}
									/*}}}*/
// DscFile Index - a single .dsc file as an index			/*{{{*/
debDscFileIndex::debDscFileIndex(std::string const &DscFile)
   : pkgDebianIndexRealFile(DscFile, true), d(NULL)
{
}
pkgSrcRecords::Parser *debDscFileIndex::CreateSrcParser() const
{
   if (Exists() == false)
      return NULL;
   return new debDscRecordParser(File, this);
}
std::string debDscFileIndex::GetComponent() const
{
   return "local-dsc";
}
std::string debDscFileIndex::GetArchitecture() const
{
   return "source";
}
uint8_t debDscFileIndex::GetIndexFlags() const
{
   return pkgCache::Flag::LocalSource;
}
									/*}}}*/
// ControlFile Index - a directory with a debian/control file		/*{{{*/
std::string debDebianSourceDirIndex::GetComponent() const
{
   return "local-control";
}
									/*}}}*/
// String Package Index - a string of Packages file content		/*{{{*/
std::string debStringPackageIndex::GetArchitecture() const
{
   return std::string();
}
std::string debStringPackageIndex::GetComponent() const
{
   return "apt-tmp-index";
}
uint8_t debStringPackageIndex::GetIndexFlags() const
{
   return pkgCache::Flag::NotSource;
}
const pkgIndexFile::Type *debStringPackageIndex::GetType() const
{
   return pkgIndexFile::Type::GetType("Debian Package Index");
}
debStringPackageIndex::debStringPackageIndex(std::string const &content) :
   pkgDebianIndexRealFile("", false), d(NULL)
{
   char fn[1024];
   std::string const tempdir = GetTempDir();
   snprintf(fn, sizeof(fn), "%s/%s.XXXXXX", tempdir.c_str(), "apt-tmp-index");
   int const fd = mkstemp(fn);
   File = fn;
   FileFd::Write(fd, content.data(), content.length());
   close(fd);
}
debStringPackageIndex::~debStringPackageIndex()
{
   RemoveFile("~debStringPackageIndex", File);
}
									/*}}}*/

// Index File types for Debian						/*{{{*/
class APT_HIDDEN debIFTypeSrc : public pkgIndexFile::Type
{
   public:
   debIFTypeSrc() {Label = "Debian Source Index";};
};
class APT_HIDDEN debIFTypePkg : public pkgIndexFile::Type
{
   public:
   virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator const &File) const APT_OVERRIDE
   {
      return new debRecordParser(File.FileName(),*File.Cache());
   };
   debIFTypePkg() {Label = "Debian Package Index";};
};
class APT_HIDDEN debIFTypeTrans : public debIFTypePkg
{
   public:
   debIFTypeTrans() {Label = "Debian Translation Index";};
};
class APT_HIDDEN debIFTypeStatus : public pkgIndexFile::Type
{
   public:
   virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator const &File) const APT_OVERRIDE
   {
      return new debRecordParser(File.FileName(),*File.Cache());
   };
   debIFTypeStatus() {Label = "Debian dpkg status file";};
};
class APT_HIDDEN debIFTypeDebPkgFile : public pkgIndexFile::Type
{
   public:
   virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator const &File) const APT_OVERRIDE
   {
      return new debDebFileRecordParser(File.FileName());
   };
   debIFTypeDebPkgFile() {Label = "Debian deb file";};
};
class APT_HIDDEN debIFTypeDscFile : public pkgIndexFile::Type
{
   public:
   virtual pkgSrcRecords::Parser *CreateSrcPkgParser(std::string const &DscFile) const APT_OVERRIDE
   {
      return new debDscRecordParser(DscFile, NULL);
   };
   debIFTypeDscFile() {Label = "Debian dsc file";};
};
class APT_HIDDEN debIFTypeDebianSourceDir : public pkgIndexFile::Type
{
   public:
   virtual pkgSrcRecords::Parser *CreateSrcPkgParser(std::string const &SourceDir) const APT_OVERRIDE
   {
      return new debDscRecordParser(SourceDir + std::string("/debian/control"), NULL);
   };
   debIFTypeDebianSourceDir() {Label = "Debian control file";};
};

APT_HIDDEN debIFTypeSrc _apt_Src;
APT_HIDDEN debIFTypePkg _apt_Pkg;
APT_HIDDEN debIFTypeTrans _apt_Trans;
APT_HIDDEN debIFTypeStatus _apt_Status;
APT_HIDDEN debIFTypeDebPkgFile _apt_DebPkgFile;
// file based pseudo indexes
APT_HIDDEN debIFTypeDscFile _apt_DscFile;
APT_HIDDEN debIFTypeDebianSourceDir _apt_DebianSourceDir;

const pkgIndexFile::Type *debSourcesIndex::GetType() const
{
   return &_apt_Src;
}
const pkgIndexFile::Type *debPackagesIndex::GetType() const
{
   return &_apt_Pkg;
}
const pkgIndexFile::Type *debTranslationsIndex::GetType() const
{
   return &_apt_Trans;
}
const pkgIndexFile::Type *debStatusIndex::GetType() const
{
   return &_apt_Status;
}
const pkgIndexFile::Type *debDebPkgFileIndex::GetType() const
{
   return &_apt_DebPkgFile;
}
const pkgIndexFile::Type *debDscFileIndex::GetType() const
{
   return &_apt_DscFile;
}
const pkgIndexFile::Type *debDebianSourceDirIndex::GetType() const
{
   return &_apt_DebianSourceDir;
}
									/*}}}*/

debStatusIndex::~debStatusIndex() {}
debPackagesIndex::~debPackagesIndex() {}
debTranslationsIndex::~debTranslationsIndex() {}
debSourcesIndex::~debSourcesIndex() {}

debDebPkgFileIndex::~debDebPkgFileIndex() {}
debDscFileIndex::~debDscFileIndex() {}