summaryrefslogtreecommitdiffstats
path: root/apt-pkg/contrib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:07:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:07:13 +0000
commit636c7dc17286d93d788c741d15fd756aeda066d5 (patch)
treee7ae158cc54f591041a061b9865bcae51854f15c /apt-pkg/contrib
parentInitial commit. (diff)
downloadapt-636c7dc17286d93d788c741d15fd756aeda066d5.tar.xz
apt-636c7dc17286d93d788c741d15fd756aeda066d5.zip
Adding upstream version 1.8.2.3.upstream/1.8.2.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--apt-pkg/contrib/cdromutl.cc293
-rw-r--r--apt-pkg/contrib/cdromutl.h25
-rw-r--r--apt-pkg/contrib/cmndline.cc445
-rw-r--r--apt-pkg/contrib/cmndline.h118
-rw-r--r--apt-pkg/contrib/configuration.cc1209
-rw-r--r--apt-pkg/contrib/configuration.h155
-rw-r--r--apt-pkg/contrib/crc-16.cc77
-rw-r--r--apt-pkg/contrib/crc-16.h19
-rw-r--r--apt-pkg/contrib/error.cc244
-rw-r--r--apt-pkg/contrib/error.h365
-rw-r--r--apt-pkg/contrib/fileutl.cc3443
-rw-r--r--apt-pkg/contrib/fileutl.h305
-rw-r--r--apt-pkg/contrib/gpgv.cc580
-rw-r--r--apt-pkg/contrib/gpgv.h92
-rw-r--r--apt-pkg/contrib/hashes.cc408
-rw-r--r--apt-pkg/contrib/hashes.h250
-rw-r--r--apt-pkg/contrib/hashsum.cc52
-rw-r--r--apt-pkg/contrib/hashsum_template.h141
-rw-r--r--apt-pkg/contrib/macros.h173
-rw-r--r--apt-pkg/contrib/md5.cc279
-rw-r--r--apt-pkg/contrib/md5.h58
-rw-r--r--apt-pkg/contrib/mmap.cc502
-rw-r--r--apt-pkg/contrib/mmap.h120
-rw-r--r--apt-pkg/contrib/netrc.cc195
-rw-r--r--apt-pkg/contrib/netrc.h39
-rw-r--r--apt-pkg/contrib/progress.cc221
-rw-r--r--apt-pkg/contrib/progress.h89
-rw-r--r--apt-pkg/contrib/proxy.cc97
-rw-r--r--apt-pkg/contrib/proxy.h16
-rw-r--r--apt-pkg/contrib/sha1.cc273
-rw-r--r--apt-pkg/contrib/sha1.h47
-rw-r--r--apt-pkg/contrib/sha2.h108
-rw-r--r--apt-pkg/contrib/sha256.h8
-rw-r--r--apt-pkg/contrib/sha2_internal.cc1089
-rw-r--r--apt-pkg/contrib/sha2_internal.h188
-rw-r--r--apt-pkg/contrib/sptr.h74
-rw-r--r--apt-pkg/contrib/srvrec.cc204
-rw-r--r--apt-pkg/contrib/srvrec.h54
-rw-r--r--apt-pkg/contrib/string_view.h132
-rw-r--r--apt-pkg/contrib/strutl.cc1840
-rw-r--r--apt-pkg/contrib/strutl.h245
-rw-r--r--apt-pkg/contrib/weakptr.h64
42 files changed, 14336 insertions, 0 deletions
diff --git a/apt-pkg/contrib/cdromutl.cc b/apt-pkg/contrib/cdromutl.cc
new file mode 100644
index 0000000..9db3980
--- /dev/null
+++ b/apt-pkg/contrib/cdromutl.cc
@@ -0,0 +1,293 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ CDROM Utilities - Some functions to manipulate CDROM mounts.
+
+ These are here for the cdrom method and apt-cdrom.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cdromutl.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/md5.h>
+#include <apt-pkg/strutl.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using std::string;
+
+// IsMounted - Returns true if the mount point is mounted /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a simple algorithm that should always work, we stat the mount point
+ and the '..' file in the mount point and see if they are on the same device.
+ By definition if they are the same then it is not mounted. This should
+ account for symlinked mount points as well. */
+bool IsMounted(string &Path)
+{
+ if (Path.empty() == true)
+ return false;
+
+ // Need that trailing slash for directories
+ if (Path[Path.length() - 1] != '/')
+ Path += '/';
+
+ // if the path has a ".disk" directory we treat it as mounted
+ // this way even extracted copies of disks are recognized
+ if (DirectoryExists(Path + ".disk/") == true)
+ return true;
+
+ /* First we check if the path is actually mounted, we do this by
+ stating the path and the previous directory (careful of links!)
+ and comparing their device fields. */
+ struct stat Buf,Buf2;
+ if (stat(Path.c_str(),&Buf) != 0 ||
+ stat((Path + "../").c_str(),&Buf2) != 0)
+ return _error->Errno("stat",_("Unable to stat the mount point %s"),Path.c_str());
+
+ if (Buf.st_dev == Buf2.st_dev)
+ return false;
+ return true;
+}
+ /*}}}*/
+// UnmountCdrom - Unmount a cdrom /*{{{*/
+// ---------------------------------------------------------------------
+/* Forking umount works much better than the umount syscall which can
+ leave /etc/mtab inconsitant. We drop all messages this produces. */
+bool UnmountCdrom(string Path)
+{
+ // do not generate errors, even if the mountpoint does not exist
+ // the mountpoint might be auto-created by the mount command
+ // and a non-existing mountpoint is surely not mounted
+ _error->PushToStack();
+ bool const mounted = IsMounted(Path);
+ _error->RevertToStack();
+ if (mounted == false)
+ return true;
+
+ for (int i=0;i<3;i++)
+ {
+
+ int Child = ExecFork();
+
+ // The child
+ if (Child == 0)
+ {
+ // Make all the fds /dev/null
+ int const null_fd = open("/dev/null",O_RDWR);
+ for (int I = 0; I != 3; ++I)
+ dup2(null_fd, I);
+
+ if (_config->Exists("Acquire::cdrom::"+Path+"::UMount") == true)
+ {
+ if (system(_config->Find("Acquire::cdrom::"+Path+"::UMount").c_str()) != 0)
+ _exit(100);
+ _exit(0);
+ }
+ else
+ {
+ const char * const Args[] = {
+ "umount",
+ Path.c_str(),
+ nullptr
+ };
+ execvp(Args[0], const_cast<char **>(Args));
+ _exit(100);
+ }
+ }
+
+ // if it can not be umounted, give it a bit more time
+ // this can happen when auto-mount magic or fs/cdrom prober attack
+ if (ExecWait(Child,"umount",true) == true)
+ return true;
+ sleep(1);
+ }
+
+ return false;
+}
+ /*}}}*/
+// MountCdrom - Mount a cdrom /*{{{*/
+// ---------------------------------------------------------------------
+/* We fork mount and drop all messages */
+bool MountCdrom(string Path, string DeviceName)
+{
+ // do not generate errors, even if the mountpoint does not exist
+ // the mountpoint might be auto-created by the mount command
+ _error->PushToStack();
+ bool const mounted = IsMounted(Path);
+ _error->RevertToStack();
+ if (mounted == true)
+ return true;
+
+ int Child = ExecFork();
+
+ // The child
+ if (Child == 0)
+ {
+ // Make all the fds /dev/null
+ int const null_fd = open("/dev/null",O_RDWR);
+ for (int I = 0; I != 3; ++I)
+ dup2(null_fd, I);
+
+ if (_config->Exists("Acquire::cdrom::"+Path+"::Mount") == true)
+ {
+ if (system(_config->Find("Acquire::cdrom::"+Path+"::Mount").c_str()) != 0)
+ _exit(100);
+ _exit(0);
+ }
+ else
+ {
+ const char *Args[10];
+ Args[0] = "mount";
+ if (DeviceName == "")
+ {
+ Args[1] = Path.c_str();
+ Args[2] = 0;
+ } else {
+ Args[1] = DeviceName.c_str();
+ Args[2] = Path.c_str();
+ Args[3] = 0;
+ }
+ execvp(Args[0],(char **)Args);
+ _exit(100);
+ }
+ }
+
+ // Wait for mount
+ return ExecWait(Child,"mount",true);
+}
+ /*}}}*/
+// IdentCdrom - Generate a unique string for this CD /*{{{*/
+// ---------------------------------------------------------------------
+/* We convert everything we hash into a string, this prevents byte size/order
+ from effecting the outcome. */
+bool IdentCdrom(string CD,string &Res,unsigned int Version)
+{
+ MD5Summation Hash;
+ bool writable_media = false;
+
+ int dirfd = open(CD.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (dirfd == -1)
+ return _error->Errno("open",_("Unable to read %s"),CD.c_str());
+
+ // if we are on a writable medium (like a usb-stick) that is just
+ // used like a cdrom don't use "." as it will constantly change,
+ // use .disk instead
+ if (faccessat(dirfd, ".", W_OK, 0) == 0)
+ {
+ int diskfd = openat(dirfd, "./.disk", O_RDONLY | O_DIRECTORY | O_CLOEXEC, 0);
+ if (diskfd != -1)
+ {
+ close(dirfd);
+ dirfd = diskfd;
+ writable_media = true;
+ CD = CD.append("/.disk");
+ if (_config->FindB("Debug::aptcdrom",false) == true)
+ std::clog << "Found writable cdrom, using alternative path: " << CD
+ << std::endl;
+ }
+ }
+
+ DIR * const D = fdopendir(dirfd);
+ if (D == nullptr)
+ return _error->Errno("opendir",_("Unable to read %s"),CD.c_str());
+
+ /* Run over the directory, we assume that the reader order will never
+ change as the media is read-only. In theory if the kernel did
+ some sort of wacked caching this might not be true.. */
+ for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
+ {
+ // Skip some files..
+ if (strcmp(Dir->d_name,".") == 0 ||
+ strcmp(Dir->d_name,"..") == 0)
+ continue;
+
+ std::string S;
+ if (Version <= 1)
+ S = std::to_string(Dir->d_ino);
+ else
+ {
+ struct stat Buf;
+ if (fstatat(dirfd, Dir->d_name, &Buf, 0) != 0)
+ continue;
+ S = std::to_string(Buf.st_mtime);
+ }
+
+ Hash.Add(S.c_str());
+ Hash.Add(Dir->d_name);
+ }
+
+ // Some stats from the fsys
+ std::string S;
+ if (_config->FindB("Debug::identcdrom",false) == false)
+ {
+ struct statvfs Buf;
+ if (fstatvfs(dirfd, &Buf) != 0)
+ return _error->Errno("statfs",_("Failed to stat the cdrom"));
+
+ // We use a kilobyte block size to avoid overflow
+ S = std::to_string(Buf.f_blocks * (Buf.f_bsize / 1024));
+ if (writable_media == false)
+ S.append(" ").append(std::to_string(Buf.f_bfree * (Buf.f_bsize / 1024)));
+ Hash.Add(S.c_str(), S.length());
+ strprintf(S, "-%u", Version);
+ }
+ else
+ strprintf(S, "-%u.debug", Version);
+
+ closedir(D);
+ Res = Hash.Result().Value().append(std::move(S));
+ return true;
+}
+ /*}}}*/
+// FindMountPointForDevice - Find mountpoint for the given device /*{{{*/
+string FindMountPointForDevice(const char *devnode)
+{
+ // this is the order that mount uses as well
+ std::vector<std::string> const mounts = _config->FindVector("Dir::state::MountPoints", "/etc/mtab,/proc/mount");
+
+ for (std::vector<std::string>::const_iterator m = mounts.begin(); m != mounts.end(); ++m)
+ if (FileExists(*m) == true)
+ {
+ char * line = NULL;
+ size_t line_len = 0;
+ FILE * f = fopen(m->c_str(), "r");
+ while(getline(&line, &line_len, f) != -1)
+ {
+ char * out[] = { NULL, NULL, NULL };
+ TokSplitString(' ', line, out, 3);
+ if (out[2] != NULL || out[1] == NULL || out[0] == NULL)
+ continue;
+ if (strcmp(out[0], devnode) != 0)
+ continue;
+ fclose(f);
+ // unescape the \0XXX chars in the path
+ string mount_point = out[1];
+ free(line);
+ return DeEscapeString(mount_point);
+ }
+ fclose(f);
+ free(line);
+ }
+
+ return string();
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/cdromutl.h b/apt-pkg/contrib/cdromutl.h
new file mode 100644
index 0000000..4e07e42
--- /dev/null
+++ b/apt-pkg/contrib/cdromutl.h
@@ -0,0 +1,25 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ CDROM Utilities - Some functions to manipulate CDROM mounts.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_CDROMUTL_H
+#define PKGLIB_CDROMUTL_H
+
+#include <string>
+
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+#endif
+
+// mount cdrom, DeviceName (e.g. /dev/sr0) is optional
+bool MountCdrom(std::string Path, std::string DeviceName="");
+bool UnmountCdrom(std::string Path);
+bool IdentCdrom(std::string CD,std::string &Res,unsigned int Version = 2);
+bool IsMounted(std::string &Path);
+std::string FindMountPointForDevice(const char *device);
+
+#endif
diff --git a/apt-pkg/contrib/cmndline.cc b/apt-pkg/contrib/cmndline.cc
new file mode 100644
index 0000000..b2a96ca
--- /dev/null
+++ b/apt-pkg/contrib/cmndline.cc
@@ -0,0 +1,445 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Command Line Class - Sophisticated command line parser
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe <jgg@debian.org>.
+
+ ##################################################################### */
+ /*}}}*/
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/strutl.h>
+
+#include <string>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <apti18n.h>
+ /*}}}*/
+using namespace std;
+
+// CommandLine::CommandLine - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+CommandLine::CommandLine(Args *AList,Configuration *Conf) : ArgList(AList),
+ Conf(Conf), FileList(0)
+{
+}
+CommandLine::CommandLine() : ArgList(NULL), Conf(NULL), FileList(0)
+{
+}
+ /*}}}*/
+// CommandLine::~CommandLine - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+CommandLine::~CommandLine()
+{
+ delete [] FileList;
+}
+ /*}}}*/
+// CommandLine::GetCommand - return the first non-option word /*{{{*/
+char const * CommandLine::GetCommand(Dispatch const * const Map,
+ unsigned int const argc, char const * const * const argv)
+{
+ // if there is a -- on the line there must be the word we search for either
+ // before it (as -- marks the end of the options) or right after it (as we can't
+ // decide if the command is actually an option, given that in theory, you could
+ // have parameters named like commands)
+ for (size_t i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--") != 0)
+ continue;
+ // check if command is before --
+ for (size_t k = 1; k < i; ++k)
+ for (size_t j = 0; Map[j].Match != NULL; ++j)
+ if (strcmp(argv[k], Map[j].Match) == 0)
+ return Map[j].Match;
+ // see if the next token after -- is the command
+ ++i;
+ if (i < argc)
+ for (size_t j = 0; Map[j].Match != NULL; ++j)
+ if (strcmp(argv[i], Map[j].Match) == 0)
+ return Map[j].Match;
+ // we found a --, but not a command
+ return NULL;
+ }
+ // no --, so search for the first word matching a command
+ // FIXME: How like is it that an option parameter will be also a valid Match ?
+ for (size_t i = 1; i < argc; ++i)
+ {
+ if (*(argv[i]) == '-')
+ continue;
+ for (size_t j = 0; Map[j].Match != NULL; ++j)
+ if (strcmp(argv[i], Map[j].Match) == 0)
+ return Map[j].Match;
+ }
+ return NULL;
+}
+ /*}}}*/
+// CommandLine::Parse - Main action member /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool CommandLine::Parse(int argc,const char **argv)
+{
+ delete [] FileList;
+ FileList = new const char *[argc];
+ const char **Files = FileList;
+ int I;
+ for (I = 1; I != argc; I++)
+ {
+ const char *Opt = argv[I];
+
+ // It is not an option
+ if (*Opt != '-')
+ {
+ *Files++ = Opt;
+ continue;
+ }
+
+ Opt++;
+
+ // Double dash signifies the end of option processing
+ if (*Opt == '-' && Opt[1] == 0)
+ {
+ I++;
+ break;
+ }
+
+ // Single dash is a short option
+ if (*Opt != '-')
+ {
+ // Iterate over each letter
+ while (*Opt != 0)
+ {
+ // Search for the option
+ Args *A;
+ for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
+ if (A->end() == true)
+ return _error->Error(_("Command line option '%c' [from %s] is not understood in combination with the other options."),*Opt,argv[I]);
+
+ if (HandleOpt(I,argc,argv,Opt,A) == false)
+ return false;
+ if (*Opt != 0)
+ Opt++;
+ }
+ continue;
+ }
+
+ Opt++;
+
+ // Match up to a = against the list
+ Args *A;
+ const char *OptEnd = strchrnul(Opt, '=');
+ for (A = ArgList; A->end() == false &&
+ (A->LongOpt == 0 || stringcasecmp(Opt,OptEnd,A->LongOpt) != 0);
+ ++A);
+
+ // Failed, look for a word after the first - (no-foo)
+ bool PreceedMatch = false;
+ if (A->end() == true)
+ {
+ Opt = (const char*) memchr(Opt, '-', OptEnd - Opt);
+ if (Opt == NULL)
+ return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
+ Opt++;
+
+ for (A = ArgList; A->end() == false &&
+ (A->LongOpt == 0 || stringcasecmp(Opt,OptEnd,A->LongOpt) != 0);
+ ++A);
+
+ // Failed again..
+ if (A->end() == true && OptEnd - Opt != 1)
+ return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
+
+ // The option could be a single letter option prefixed by a no-..
+ if (A->end() == true)
+ {
+ for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
+
+ if (A->end() == true)
+ return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
+ }
+
+ // The option is not boolean
+ if (A->IsBoolean() == false)
+ return _error->Error(_("Command line option %s is not boolean"),argv[I]);
+ PreceedMatch = true;
+ }
+
+ // Deal with it.
+ OptEnd--;
+ if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
+ return false;
+ }
+
+ // Copy any remaining file names over
+ for (; I != argc; I++)
+ *Files++ = argv[I];
+ *Files = 0;
+
+ SaveInConfig(argc, argv);
+
+ return true;
+}
+ /*}}}*/
+// CommandLine::HandleOpt - Handle a single option including all flags /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a helper function for parser, it looks at a given argument
+ and looks for specific patterns in the string, it gets tokanized
+ -ruffly- like -*[yes|true|enable]-(o|longopt)[=][ ][argument] */
+bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
+ const char *&Opt,Args *A,bool PreceedMatch)
+{
+ const char *Argument = 0;
+ bool CertainArg = false;
+ int IncI = 0;
+
+ /* Determine the possible location of an option or 0 if their is
+ no option */
+ if (Opt[1] == 0)
+ {
+ if (I + 1 < argc && argv[I+1][0] != '-')
+ Argument = argv[I+1];
+
+ IncI = 1;
+ }
+ else
+ {
+ if (Opt[1] == '=')
+ {
+ CertainArg = true;
+ Argument = Opt + 2;
+ }
+ else
+ Argument = Opt + 1;
+ }
+
+ // Option is an argument set
+ if ((A->Flags & HasArg) == HasArg)
+ {
+ if (Argument == 0)
+ return _error->Error(_("Option %s requires an argument."),argv[I]);
+ Opt += strlen(Opt);
+ I += IncI;
+
+ // Parse a configuration file
+ if ((A->Flags & ConfigFile) == ConfigFile)
+ return ReadConfigFile(*Conf,Argument);
+
+ // Arbitrary item specification
+ if ((A->Flags & ArbItem) == ArbItem)
+ {
+ const char * const J = strchr(Argument, '=');
+ if (J == nullptr)
+ return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
+
+ Conf->Set(string(Argument,J-Argument), J+1);
+ return true;
+ }
+
+ const char *I = strchrnul(A->ConfName, ' ');
+ if (*I == ' ')
+ Conf->Set(string(A->ConfName,0,I-A->ConfName),string(I+1) + Argument);
+ else
+ Conf->Set(A->ConfName,string(I) + Argument);
+
+ return true;
+ }
+
+ // Option is an integer level
+ if ((A->Flags & IntLevel) == IntLevel)
+ {
+ // There might be an argument
+ if (Argument != 0)
+ {
+ char *EndPtr;
+ unsigned long Value = strtol(Argument,&EndPtr,10);
+
+ // Conversion failed and the argument was specified with an =s
+ if (EndPtr == Argument && CertainArg == true)
+ return _error->Error(_("Option %s requires an integer argument, not '%s'"),argv[I],Argument);
+
+ // Conversion was ok, set the value and return
+ if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
+ {
+ Conf->Set(A->ConfName,Value);
+ Opt += strlen(Opt);
+ I += IncI;
+ return true;
+ }
+ }
+
+ // Increase the level
+ Conf->Set(A->ConfName,Conf->FindI(A->ConfName)+1);
+ return true;
+ }
+
+ // Option is a boolean
+ int Sense = -1; // -1 is unspecified, 0 is yes 1 is no
+
+ // Look for an argument.
+ while (1)
+ {
+ // Look at preceding text
+ char Buffer[300];
+ if (Argument == 0)
+ {
+ if (PreceedMatch == false)
+ break;
+
+ if (strlen(argv[I]) >= sizeof(Buffer))
+ return _error->Error(_("Option '%s' is too long"),argv[I]);
+
+ // Skip the leading dash
+ const char *J = argv[I];
+ for (; *J != 0 && *J == '-'; J++);
+
+ const char *JEnd = strchr(J, '-');
+ if (JEnd != NULL)
+ {
+ strncpy(Buffer,J,JEnd - J);
+ Buffer[JEnd - J] = 0;
+ Argument = Buffer;
+ CertainArg = true;
+ }
+ else
+ break;
+ }
+
+ // Check for boolean
+ Sense = StringToBool(Argument);
+ if (Sense >= 0)
+ {
+ // Eat the argument
+ if (Argument != Buffer)
+ {
+ Opt += strlen(Opt);
+ I += IncI;
+ }
+ break;
+ }
+
+ if (CertainArg == true)
+ return _error->Error(_("Sense %s is not understood, try true or false."),Argument);
+
+ Argument = 0;
+ }
+
+ // Indeterminate sense depends on the flag
+ if (Sense == -1)
+ {
+ if ((A->Flags & InvBoolean) == InvBoolean)
+ Sense = 0;
+ else
+ Sense = 1;
+ }
+
+ Conf->Set(A->ConfName,Sense);
+ return true;
+}
+ /*}}}*/
+// CommandLine::FileSize - Count the number of filenames /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+unsigned int CommandLine::FileSize() const
+{
+ unsigned int Count = 0;
+ for (const char **I = FileList; I != 0 && *I != 0; I++)
+ Count++;
+ return Count;
+}
+ /*}}}*/
+// CommandLine::DispatchArg - Do something with the first arg /*{{{*/
+bool CommandLine::DispatchArg(Dispatch const * const Map,bool NoMatch)
+{
+ int I;
+ for (I = 0; Map[I].Match != 0; I++)
+ {
+ if (strcmp(FileList[0],Map[I].Match) == 0)
+ {
+ bool Res = Map[I].Handler(*this);
+ if (Res == false && _error->PendingError() == false)
+ _error->Error("Handler silently failed");
+ return Res;
+ }
+ }
+
+ // No matching name
+ if (Map[I].Match == 0)
+ {
+ if (NoMatch == true)
+ _error->Error(_("Invalid operation %s"),FileList[0]);
+ }
+
+ return false;
+}
+bool CommandLine::DispatchArg(Dispatch *Map,bool NoMatch)
+{
+ Dispatch const * const Map2 = Map;
+ return DispatchArg(Map2, NoMatch);
+}
+ /*}}}*/
+// CommandLine::SaveInConfig - for output later in a logfile or so /*{{{*/
+// ---------------------------------------------------------------------
+/* We save the commandline here to have it around later for e.g. logging.
+ It feels a bit like a hack here and isn't bulletproof, but it is better
+ than nothing after all. */
+void CommandLine::SaveInConfig(unsigned int const &argc, char const * const * const argv)
+{
+ char cmdline[100 + argc * 50];
+ memset(cmdline, 0, sizeof(cmdline));
+ unsigned int length = 0;
+ bool lastWasOption = false;
+ bool closeQuote = false;
+ for (unsigned int i = 0; i < argc && length < sizeof(cmdline); ++i, ++length)
+ {
+ for (unsigned int j = 0; argv[i][j] != '\0' && length < sizeof(cmdline)-2; ++j)
+ {
+ // we can't really sensibly deal with quoting so skip it
+ if (strchr("\"\'\r\n", argv[i][j]) != nullptr)
+ continue;
+ cmdline[length++] = argv[i][j];
+ if (lastWasOption == true && argv[i][j] == '=')
+ {
+ // That is possibly an option: Quote it if it includes spaces,
+ // the benefit is that this will eliminate also most false positives
+ const char* c = strchr(&argv[i][j+1], ' ');
+ if (c == NULL) continue;
+ cmdline[length++] = '\'';
+ closeQuote = true;
+ }
+ }
+ if (closeQuote == true)
+ {
+ cmdline[length++] = '\'';
+ closeQuote = false;
+ }
+ // Problem: detects also --hello
+ if (cmdline[length-1] == 'o')
+ lastWasOption = true;
+ cmdline[length] = ' ';
+ }
+ cmdline[--length] = '\0';
+ _config->Set("CommandLine::AsString", cmdline);
+}
+ /*}}}*/
+CommandLine::Args CommandLine::MakeArgs(char ShortOpt, char const *LongOpt, char const *ConfName, unsigned long Flags)/*{{{*/
+{
+ /* In theory, this should be a constructor for CommandLine::Args instead,
+ but this breaks compatibility as gcc thinks this is a c++11 initializer_list */
+ CommandLine::Args arg;
+ arg.ShortOpt = ShortOpt;
+ arg.LongOpt = LongOpt;
+ arg.ConfName = ConfName;
+ arg.Flags = Flags;
+ return arg;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/cmndline.h b/apt-pkg/contrib/cmndline.h
new file mode 100644
index 0000000..bdd4f6e
--- /dev/null
+++ b/apt-pkg/contrib/cmndline.h
@@ -0,0 +1,118 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Command Line Class - Sophisticated command line parser
+
+ This class provides a unified command line parser/option handliner/
+ configuration mechanism. It allows the caller to specify the option
+ set and map the option set into the configuration class or other
+ special functioning.
+
+ Filenames are stripped from the option stream and put into their
+ own array.
+
+ The argument descriptor array can be initialized as:
+
+ CommandLine::Args Args[] =
+ {{'q',"quiet","apt::get::quiet",CommandLine::IntLevel},
+ {0,0,0,0}};
+
+ The flags mean,
+ HasArg - Means the argument has a value
+ IntLevel - Means the argument is an integer level indication, the
+ following -qqqq (+3) -q5 (=5) -q=5 (=5) are valid
+ Boolean - Means it is true/false or yes/no.
+ -d (true) --no-d (false) --yes-d (true)
+ --long (true) --no-long (false) --yes-long (true)
+ -d=yes (true) -d=no (false) Words like enable, disable,
+ true false, yes no and on off are recognized in logical
+ places.
+ InvBoolean - Same as boolean but the case with no specified sense
+ (first case) is set to false.
+ ConfigFile - Means this flag should be interprited as the name of
+ a config file to read in at this point in option processing.
+ Implies HasArg.
+ ArbItem - Means the item is an arbitrary configuration string of
+ the form item=value, where item is passed directly
+ to the configuration class.
+ The default, if the flags are 0 is to use Boolean
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_CMNDLINE_H
+#define PKGLIB_CMNDLINE_H
+
+#include <apt-pkg/macros.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/configuration.h>
+#endif
+
+class Configuration;
+
+class CommandLine
+{
+ public:
+ struct Args;
+ struct Dispatch;
+ struct DispatchWithHelp;
+
+ protected:
+
+ Args *ArgList;
+ Configuration *Conf;
+ bool HandleOpt(int &I,int argc,const char *argv[],
+ const char *&Opt,Args *A,bool PreceedeMatch = false);
+ void static SaveInConfig(unsigned int const &argc, char const * const * const argv);
+
+ public:
+
+ enum AFlags
+ {
+ HasArg = (1 << 0),
+ IntLevel = (1 << 1),
+ Boolean = (1 << 2),
+ InvBoolean = (1 << 3),
+ ConfigFile = (1 << 4) | HasArg,
+ ArbItem = (1 << 5) | HasArg
+ };
+
+ const char **FileList;
+
+ bool Parse(int argc,const char **argv);
+ void ShowHelp();
+ unsigned int FileSize() const APT_PURE;
+ // FIXME: merge on next ABI break
+ bool DispatchArg(Dispatch *List,bool NoMatch = true);
+ bool DispatchArg(Dispatch const * const List,bool NoMatch = true);
+
+ static char const * GetCommand(Dispatch const * const Map,
+ unsigned int const argc, char const * const * const argv) APT_PURE;
+
+ static CommandLine::Args MakeArgs(char ShortOpt, char const *LongOpt,
+ char const *ConfName, unsigned long Flags) APT_PURE;
+
+ CommandLine();
+ CommandLine(Args *AList,Configuration *Conf);
+ ~CommandLine();
+};
+
+struct CommandLine::Args
+{
+ char ShortOpt;
+ const char *LongOpt;
+ const char *ConfName;
+ unsigned long Flags;
+
+ inline bool end() {return ShortOpt == 0 && LongOpt == 0;};
+ inline bool IsBoolean() {return Flags == 0 || (Flags & (Boolean|InvBoolean)) != 0;};
+};
+
+struct CommandLine::Dispatch
+{
+ const char *Match;
+ bool (*Handler)(CommandLine &);
+};
+
+#endif
diff --git a/apt-pkg/contrib/configuration.cc b/apt-pkg/contrib/configuration.cc
new file mode 100644
index 0000000..997ef74
--- /dev/null
+++ b/apt-pkg/contrib/configuration.cc
@@ -0,0 +1,1209 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Configuration Class
+
+ This class provides a configuration file and command line parser
+ for a tree-oriented configuration environment. All runtime configuration
+ is stored in here.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe <jgg@debian.org>.
+
+ ##################################################################### */
+ /*}}}*/
+// Include files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/strutl.h>
+
+#include <ctype.h>
+#include <regex.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <sstream>
+#include <stack>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <apti18n.h>
+
+using namespace std;
+ /*}}}*/
+
+Configuration *_config = new Configuration;
+
+/* TODO: This config verification shouldn't be using a static variable
+ but a Cnf-member – but that would need ABI breaks and stuff and for now
+ that really is an apt-dev-only tool, so it isn't that bad that it is
+ unusable and allaround a bit strange */
+enum class APT_HIDDEN ConfigType { UNDEFINED, INT, BOOL, STRING, STRING_OR_BOOL, STRING_OR_LIST, FILE, DIR, LIST, PROGRAM_PATH = FILE };
+APT_HIDDEN std::unordered_map<std::string, ConfigType> apt_known_config {};
+static std::string getConfigTypeString(ConfigType const type) /*{{{*/
+{
+ switch (type)
+ {
+ case ConfigType::UNDEFINED: return "UNDEFINED";
+ case ConfigType::INT: return "INT";
+ case ConfigType::BOOL: return "BOOL";
+ case ConfigType::STRING: return "STRING";
+ case ConfigType::STRING_OR_BOOL: return "STRING_OR_BOOL";
+ case ConfigType::FILE: return "FILE";
+ case ConfigType::DIR: return "DIR";
+ case ConfigType::LIST: return "LIST";
+ case ConfigType::STRING_OR_LIST: return "STRING_OR_LIST";
+ }
+ return "UNKNOWN";
+}
+ /*}}}*/
+static ConfigType getConfigType(std::string const &type) /*{{{*/
+{
+ if (type == "<INT>")
+ return ConfigType::INT;
+ else if (type == "<BOOL>")
+ return ConfigType::BOOL;
+ else if (type == "<STRING>")
+ return ConfigType::STRING;
+ else if (type == "<STRING_OR_BOOL>")
+ return ConfigType::STRING_OR_BOOL;
+ else if (type == "<FILE>")
+ return ConfigType::FILE;
+ else if (type == "<DIR>")
+ return ConfigType::DIR;
+ else if (type == "<LIST>")
+ return ConfigType::LIST;
+ else if (type == "<STRING_OR_LIST>")
+ return ConfigType::STRING_OR_LIST;
+ else if (type == "<PROGRAM_PATH>")
+ return ConfigType::PROGRAM_PATH;
+ return ConfigType::UNDEFINED;
+}
+ /*}}}*/
+// checkFindConfigOptionType - workhorse of option checking /*{{{*/
+static void checkFindConfigOptionTypeInternal(std::string name, ConfigType const type)
+{
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ auto known = apt_known_config.find(name);
+ if (known == apt_known_config.cend())
+ {
+ auto const rcolon = name.rfind(':');
+ if (rcolon != std::string::npos)
+ {
+ known = apt_known_config.find(name.substr(0, rcolon) + ":*");
+ if (known == apt_known_config.cend())
+ {
+ auto const parts = StringSplit(name, "::");
+ size_t psize = parts.size();
+ if (psize > 1)
+ {
+ for (size_t max = psize; max != 1; --max)
+ {
+ std::ostringstream os;
+ std::copy(parts.begin(), parts.begin() + max, std::ostream_iterator<std::string>(os, "::"));
+ os << "**";
+ known = apt_known_config.find(os.str());
+ if (known != apt_known_config.cend() && known->second == ConfigType::UNDEFINED)
+ return;
+ }
+ for (size_t max = psize - 1; max != 1; --max)
+ {
+ std::ostringstream os;
+ std::copy(parts.begin(), parts.begin() + max - 1, std::ostream_iterator<std::string>(os, "::"));
+ os << "*::";
+ std::copy(parts.begin() + max + 1, parts.end() - 1, std::ostream_iterator<std::string>(os, "::"));
+ os << *(parts.end() - 1);
+ known = apt_known_config.find(os.str());
+ if (known != apt_known_config.cend())
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (known == apt_known_config.cend())
+ _error->Warning("Using unknown config option »%s« of type %s",
+ name.c_str(), getConfigTypeString(type).c_str());
+ else if (known->second != type)
+ {
+ if (known->second == ConfigType::DIR && type == ConfigType::FILE)
+ ; // implementation detail
+ else if (type == ConfigType::STRING && (known->second == ConfigType::FILE || known->second == ConfigType::DIR))
+ ; // TODO: that might be an error or not, we will figure this out later
+ else if (known->second == ConfigType::STRING_OR_BOOL && (type == ConfigType::BOOL || type == ConfigType::STRING))
+ ;
+ else if (known->second == ConfigType::STRING_OR_LIST && (type == ConfigType::LIST || type == ConfigType::STRING))
+ ;
+ else
+ _error->Warning("Using config option »%s« of type %s as a type %s",
+ name.c_str(), getConfigTypeString(known->second).c_str(), getConfigTypeString(type).c_str());
+ }
+}
+static void checkFindConfigOptionType(char const * const name, ConfigType const type)
+{
+ if (apt_known_config.empty())
+ return;
+ checkFindConfigOptionTypeInternal(name, type);
+}
+ /*}}}*/
+static bool LoadConfigurationIndex(std::string const &filename) /*{{{*/
+{
+ apt_known_config.clear();
+ if (filename.empty())
+ return true;
+ Configuration Idx;
+ if (ReadConfigFile(Idx, filename) == false)
+ return false;
+
+ Configuration::Item const * Top = Idx.Tree(nullptr);
+ if (unlikely(Top == nullptr))
+ return false;
+
+ do {
+ if (Top->Value.empty() == false)
+ {
+ std::string fulltag = Top->FullTag();
+ std::transform(fulltag.begin(), fulltag.end(), fulltag.begin(), ::tolower);
+ apt_known_config.emplace(std::move(fulltag), getConfigType(Top->Value));
+ }
+
+ if (Top->Child != nullptr)
+ {
+ Top = Top->Child;
+ continue;
+ }
+
+ while (Top != nullptr && Top->Next == nullptr)
+ Top = Top->Parent;
+ if (Top != nullptr)
+ Top = Top->Next;
+ } while (Top != nullptr);
+
+ return true;
+}
+ /*}}}*/
+
+// Configuration::Configuration - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+Configuration::Configuration() : ToFree(true)
+{
+ Root = new Item;
+}
+Configuration::Configuration(const Item *Root) : Root((Item *)Root), ToFree(false)
+{
+}
+ /*}}}*/
+// Configuration::~Configuration - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+Configuration::~Configuration()
+{
+ if (ToFree == false)
+ return;
+
+ Item *Top = Root;
+ for (; Top != 0;)
+ {
+ if (Top->Child != 0)
+ {
+ Top = Top->Child;
+ continue;
+ }
+
+ while (Top != 0 && Top->Next == 0)
+ {
+ Item *Parent = Top->Parent;
+ delete Top;
+ Top = Parent;
+ }
+ if (Top != 0)
+ {
+ Item *Next = Top->Next;
+ delete Top;
+ Top = Next;
+ }
+ }
+}
+ /*}}}*/
+// Configuration::Lookup - Lookup a single item /*{{{*/
+// ---------------------------------------------------------------------
+/* This will lookup a single item by name below another item. It is a
+ helper function for the main lookup function */
+Configuration::Item *Configuration::Lookup(Item *Head,const char *S,
+ unsigned long const &Len,bool const &Create)
+{
+ int Res = 1;
+ Item *I = Head->Child;
+ Item **Last = &Head->Child;
+
+ // Empty strings match nothing. They are used for lists.
+ if (Len != 0)
+ {
+ for (; I != 0; Last = &I->Next, I = I->Next)
+ if (Len == I->Tag.length() && (Res = stringcasecmp(I->Tag,S,S + Len)) == 0)
+ break;
+ }
+ else
+ for (; I != 0; Last = &I->Next, I = I->Next);
+
+ if (Res == 0)
+ return I;
+ if (Create == false)
+ return 0;
+
+ I = new Item;
+ I->Tag.assign(S,Len);
+ I->Next = *Last;
+ I->Parent = Head;
+ *Last = I;
+ return I;
+}
+ /*}}}*/
+// Configuration::Lookup - Lookup a fully scoped item /*{{{*/
+// ---------------------------------------------------------------------
+/* This performs a fully scoped lookup of a given name, possibly creating
+ new items */
+Configuration::Item *Configuration::Lookup(const char *Name,bool const &Create)
+{
+ if (Name == 0)
+ return Root->Child;
+
+ const char *Start = Name;
+ const char *End = Start + strlen(Name);
+ const char *TagEnd = Name;
+ Item *Itm = Root;
+ for (; End - TagEnd >= 2; TagEnd++)
+ {
+ if (TagEnd[0] == ':' && TagEnd[1] == ':')
+ {
+ Itm = Lookup(Itm,Start,TagEnd - Start,Create);
+ if (Itm == 0)
+ return 0;
+ TagEnd = Start = TagEnd + 2;
+ }
+ }
+
+ // This must be a trailing ::, we create unique items in a list
+ if (End - Start == 0)
+ {
+ if (Create == false)
+ return 0;
+ }
+
+ Itm = Lookup(Itm,Start,End - Start,Create);
+ return Itm;
+}
+ /*}}}*/
+// Configuration::Find - Find a value /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string Configuration::Find(const char *Name,const char *Default) const
+{
+ checkFindConfigOptionType(Name, ConfigType::STRING);
+ const Item *Itm = Lookup(Name);
+ if (Itm == 0 || Itm->Value.empty() == true)
+ {
+ if (Default == 0)
+ return "";
+ else
+ return Default;
+ }
+
+ return Itm->Value;
+}
+ /*}}}*/
+// Configuration::FindFile - Find a Filename /*{{{*/
+// ---------------------------------------------------------------------
+/* Directories are stored as the base dir in the Parent node and the
+ sub directory in sub nodes with the final node being the end filename
+ */
+string Configuration::FindFile(const char *Name,const char *Default) const
+{
+ checkFindConfigOptionType(Name, ConfigType::FILE);
+ const Item *RootItem = Lookup("RootDir");
+ std::string result = (RootItem == 0) ? "" : RootItem->Value;
+ if(result.empty() == false && result[result.size() - 1] != '/')
+ result.push_back('/');
+
+ const Item *Itm = Lookup(Name);
+ if (Itm == 0 || Itm->Value.empty() == true)
+ {
+ if (Default != 0)
+ result.append(Default);
+ }
+ else
+ {
+ string val = Itm->Value;
+ while (Itm->Parent != 0)
+ {
+ if (Itm->Parent->Value.empty() == true)
+ {
+ Itm = Itm->Parent;
+ continue;
+ }
+
+ // Absolute
+ if (val.length() >= 1 && val[0] == '/')
+ {
+ if (val.compare(0, 9, "/dev/null") == 0)
+ val.erase(9);
+ break;
+ }
+
+ // ~/foo or ./foo
+ if (val.length() >= 2 && (val[0] == '~' || val[0] == '.') && val[1] == '/')
+ break;
+
+ // ../foo
+ if (val.length() >= 3 && val[0] == '.' && val[1] == '.' && val[2] == '/')
+ break;
+
+ if (Itm->Parent->Value.end()[-1] != '/')
+ val.insert(0, "/");
+
+ val.insert(0, Itm->Parent->Value);
+ Itm = Itm->Parent;
+ }
+ result.append(val);
+ }
+ return flNormalize(result);
+}
+ /*}}}*/
+// Configuration::FindDir - Find a directory name /*{{{*/
+// ---------------------------------------------------------------------
+/* This is like findfile execept the result is terminated in a / */
+string Configuration::FindDir(const char *Name,const char *Default) const
+{
+ checkFindConfigOptionType(Name, ConfigType::DIR);
+ string Res = FindFile(Name,Default);
+ if (Res.end()[-1] != '/')
+ {
+ size_t const found = Res.rfind("/dev/null");
+ if (found != string::npos && found == Res.size() - 9)
+ return Res; // /dev/null returning
+ return Res + '/';
+ }
+ return Res;
+}
+ /*}}}*/
+// Configuration::FindVector - Find a vector of values /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns a vector of config values under the given item */
+vector<string> Configuration::FindVector(const char *Name, std::string const &Default, bool const Keys) const
+{
+ checkFindConfigOptionType(Name, ConfigType::LIST);
+ vector<string> Vec;
+ const Item *Top = Lookup(Name);
+ if (Top == NULL)
+ return VectorizeString(Default, ',');
+
+ if (Top->Value.empty() == false)
+ return VectorizeString(Top->Value, ',');
+
+ Item *I = Top->Child;
+ while(I != NULL)
+ {
+ Vec.push_back(Keys ? I->Tag : I->Value);
+ I = I->Next;
+ }
+ if (Vec.empty() == true)
+ return VectorizeString(Default, ',');
+
+ return Vec;
+}
+ /*}}}*/
+// Configuration::FindI - Find an integer value /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int Configuration::FindI(const char *Name,int const &Default) const
+{
+ checkFindConfigOptionType(Name, ConfigType::INT);
+ const Item *Itm = Lookup(Name);
+ if (Itm == 0 || Itm->Value.empty() == true)
+ return Default;
+
+ char *End;
+ int Res = strtol(Itm->Value.c_str(),&End,0);
+ if (End == Itm->Value.c_str())
+ return Default;
+
+ return Res;
+}
+ /*}}}*/
+// Configuration::FindB - Find a boolean type /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool Configuration::FindB(const char *Name,bool const &Default) const
+{
+ checkFindConfigOptionType(Name, ConfigType::BOOL);
+ const Item *Itm = Lookup(Name);
+ if (Itm == 0 || Itm->Value.empty() == true)
+ return Default;
+
+ return StringToBool(Itm->Value,Default);
+}
+ /*}}}*/
+// Configuration::FindAny - Find an arbitrary type /*{{{*/
+// ---------------------------------------------------------------------
+/* a key suffix of /f, /d, /b or /i calls Find{File,Dir,B,I} */
+string Configuration::FindAny(const char *Name,const char *Default) const
+{
+ string key = Name;
+ char type = 0;
+
+ if (key.size() > 2 && key.end()[-2] == '/')
+ {
+ type = key.end()[-1];
+ key.resize(key.size() - 2);
+ }
+
+ switch (type)
+ {
+ // file
+ case 'f':
+ return FindFile(key.c_str(), Default);
+
+ // directory
+ case 'd':
+ return FindDir(key.c_str(), Default);
+
+ // bool
+ case 'b':
+ return FindB(key, Default) ? "true" : "false";
+
+ // int
+ case 'i':
+ {
+ char buf[16];
+ snprintf(buf, sizeof(buf)-1, "%d", FindI(key, Default ? atoi(Default) : 0 ));
+ return buf;
+ }
+ }
+
+ // fallback
+ return Find(Name, Default);
+}
+ /*}}}*/
+// Configuration::CndSet - Conditinal Set a value /*{{{*/
+// ---------------------------------------------------------------------
+/* This will not overwrite */
+void Configuration::CndSet(const char *Name,const string &Value)
+{
+ Item *Itm = Lookup(Name,true);
+ if (Itm == 0)
+ return;
+ if (Itm->Value.empty() == true)
+ Itm->Value = Value;
+}
+ /*}}}*/
+// Configuration::Set - Set an integer value /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::CndSet(const char *Name,int const Value)
+{
+ Item *Itm = Lookup(Name,true);
+ if (Itm == 0 || Itm->Value.empty() == false)
+ return;
+ char S[300];
+ snprintf(S,sizeof(S),"%i",Value);
+ Itm->Value = S;
+}
+ /*}}}*/
+// Configuration::Set - Set a value /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::Set(const char *Name,const string &Value)
+{
+ Item *Itm = Lookup(Name,true);
+ if (Itm == 0)
+ return;
+ Itm->Value = Value;
+}
+ /*}}}*/
+// Configuration::Set - Set an integer value /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::Set(const char *Name,int const &Value)
+{
+ Item *Itm = Lookup(Name,true);
+ if (Itm == 0)
+ return;
+ char S[300];
+ snprintf(S,sizeof(S),"%i",Value);
+ Itm->Value = S;
+}
+ /*}}}*/
+// Configuration::Clear - Clear an single value from a list /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::Clear(string const &Name, int const &Value)
+{
+ char S[300];
+ snprintf(S,sizeof(S),"%i",Value);
+ Clear(Name, S);
+}
+ /*}}}*/
+// Configuration::Clear - Clear an single value from a list /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::Clear(string const &Name, string const &Value)
+{
+ Item *Top = Lookup(Name.c_str(),false);
+ if (Top == 0 || Top->Child == 0)
+ return;
+
+ Item *Tmp, *Prev, *I;
+ Prev = I = Top->Child;
+
+ while(I != NULL)
+ {
+ if(I->Value == Value)
+ {
+ Tmp = I;
+ // was first element, point parent to new first element
+ if(Top->Child == Tmp)
+ Top->Child = I->Next;
+ I = I->Next;
+ Prev->Next = I;
+ delete Tmp;
+ } else {
+ Prev = I;
+ I = I->Next;
+ }
+ }
+
+}
+ /*}}}*/
+// Configuration::Clear - Clear everything /*{{{*/
+// ---------------------------------------------------------------------
+void Configuration::Clear()
+{
+ const Configuration::Item *Top = Tree(0);
+ while( Top != 0 )
+ {
+ Clear(Top->FullTag());
+ Top = Top->Next;
+ }
+}
+ /*}}}*/
+// Configuration::Clear - Clear an entire tree /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void Configuration::Clear(string const &Name)
+{
+ Item *Top = Lookup(Name.c_str(),false);
+ if (Top == 0)
+ return;
+
+ Top->Value.clear();
+ Item *Stop = Top;
+ Top = Top->Child;
+ Stop->Child = 0;
+ for (; Top != 0;)
+ {
+ if (Top->Child != 0)
+ {
+ Top = Top->Child;
+ continue;
+ }
+
+ while (Top != 0 && Top->Next == 0)
+ {
+ Item *Tmp = Top;
+ Top = Top->Parent;
+ delete Tmp;
+
+ if (Top == Stop)
+ return;
+ }
+
+ Item *Tmp = Top;
+ if (Top != 0)
+ Top = Top->Next;
+ delete Tmp;
+ }
+}
+ /*}}}*/
+void Configuration::MoveSubTree(char const * const OldRootName, char const * const NewRootName)/*{{{*/
+{
+ // prevent NewRoot being a subtree of OldRoot
+ if (OldRootName == nullptr)
+ return;
+ if (NewRootName != nullptr)
+ {
+ if (strcmp(OldRootName, NewRootName) == 0)
+ return;
+ std::string const oldroot = std::string(OldRootName) + "::";
+ if (strcasestr(NewRootName, oldroot.c_str()) != NULL)
+ return;
+ }
+
+ Item * Top;
+ Item const * const OldRoot = Top = Lookup(OldRootName, false);
+ if (Top == nullptr)
+ return;
+ std::string NewRoot;
+ if (NewRootName != nullptr)
+ NewRoot.append(NewRootName).append("::");
+
+ Top->Value.clear();
+ Item * const Stop = Top;
+ Top = Top->Child;
+ Stop->Child = 0;
+ for (; Top != 0;)
+ {
+ if (Top->Child != 0)
+ {
+ Top = Top->Child;
+ continue;
+ }
+
+ while (Top != 0 && Top->Next == 0)
+ {
+ Set(NewRoot + Top->FullTag(OldRoot), Top->Value);
+ Item const * const Tmp = Top;
+ Top = Top->Parent;
+ delete Tmp;
+
+ if (Top == Stop)
+ return;
+ }
+
+ Set(NewRoot + Top->FullTag(OldRoot), Top->Value);
+ Item const * const Tmp = Top;
+ if (Top != 0)
+ Top = Top->Next;
+ delete Tmp;
+ }
+}
+ /*}}}*/
+// Configuration::Exists - Returns true if the Name exists /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool Configuration::Exists(const char *Name) const
+{
+ const Item *Itm = Lookup(Name);
+ if (Itm == 0)
+ return false;
+ return true;
+}
+ /*}}}*/
+// Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/
+// ---------------------------------------------------------------------
+/* qualified by /[fdbi] exists */
+bool Configuration::ExistsAny(const char *Name) const
+{
+ string key = Name;
+
+ if (key.size() > 2 && key.end()[-2] == '/')
+ {
+ if (key.find_first_of("fdbi",key.size()-1) < key.size())
+ {
+ key.resize(key.size() - 2);
+ if (Exists(key.c_str()))
+ return true;
+ }
+ else
+ {
+ _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]);
+ }
+ }
+ return Exists(Name);
+}
+ /*}}}*/
+// Configuration::Dump - Dump the config /*{{{*/
+// ---------------------------------------------------------------------
+/* Dump the entire configuration space */
+void Configuration::Dump(ostream& str)
+{
+ Dump(str, NULL, "%F \"%v\";\n", true);
+}
+void Configuration::Dump(ostream& str, char const * const root,
+ char const * const formatstr, bool const emptyValue)
+{
+ const Configuration::Item* Top = Tree(root);
+ if (Top == 0)
+ return;
+ const Configuration::Item* const Root = (root == NULL) ? NULL : Top;
+ std::vector<std::string> const format = VectorizeString(formatstr, '%');
+
+ /* Write out all of the configuration directives by walking the
+ configuration tree */
+ do {
+ if (emptyValue == true || Top->Value.empty() == emptyValue)
+ {
+ std::vector<std::string>::const_iterator f = format.begin();
+ str << *f;
+ for (++f; f != format.end(); ++f)
+ {
+ if (f->empty() == true)
+ {
+ ++f;
+ str << '%' << *f;
+ continue;
+ }
+ char const type = (*f)[0];
+ if (type == 'f')
+ str << Top->FullTag();
+ else if (type == 't')
+ str << Top->Tag;
+ else if (type == 'v')
+ str << Top->Value;
+ else if (type == 'F')
+ str << QuoteString(Top->FullTag(), "=\"\n");
+ else if (type == 'T')
+ str << QuoteString(Top->Tag, "=\"\n");
+ else if (type == 'V')
+ str << QuoteString(Top->Value, "=\"\n");
+ else if (type == 'n')
+ str << "\n";
+ else if (type == 'N')
+ str << "\t";
+ else
+ str << '%' << type;
+ str << f->c_str() + 1;
+ }
+ }
+
+ if (Top->Child != 0)
+ {
+ Top = Top->Child;
+ continue;
+ }
+
+ while (Top != 0 && Top->Next == 0)
+ Top = Top->Parent;
+ if (Top != 0)
+ Top = Top->Next;
+
+ if (Root != NULL)
+ {
+ const Configuration::Item* I = Top;
+ while(I != 0)
+ {
+ if (I == Root)
+ break;
+ else
+ I = I->Parent;
+ }
+ if (I == 0)
+ break;
+ }
+ } while (Top != 0);
+}
+ /*}}}*/
+
+// Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
+// ---------------------------------------------------------------------
+/* Stop sets an optional max recursion depth if this item is being viewed as
+ part of a sub tree. */
+string Configuration::Item::FullTag(const Item *Stop) const
+{
+ if (Parent == 0 || Parent->Parent == 0 || Parent == Stop)
+ return Tag;
+ return Parent->FullTag(Stop) + "::" + Tag;
+}
+ /*}}}*/
+
+// ReadConfigFile - Read a configuration file /*{{{*/
+// ---------------------------------------------------------------------
+/* The configuration format is very much like the named.conf format
+ used in bind8, in fact this routine can parse most named.conf files.
+ Sectional config files are like bind's named.conf where there are
+ sections like 'zone "foo.org" { .. };' This causes each section to be
+ added in with a tag like "zone::foo.org" instead of being split
+ tag/value. AsSectional enables Sectional parsing.*/
+static void leaveCurrentScope(std::stack<std::string> &Stack, std::string &ParentTag)
+{
+ if (Stack.empty())
+ ParentTag.clear();
+ else
+ {
+ ParentTag = Stack.top();
+ Stack.pop();
+ }
+}
+bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectional,
+ unsigned const &Depth)
+{
+ // Open the stream for reading
+ FileFd F;
+ if (OpenConfigurationFileFd(FName, F) == false)
+ return false;
+
+ string LineBuffer;
+ std::stack<std::string> Stack;
+
+ // Parser state
+ string ParentTag;
+
+ int CurLine = 0;
+ bool InComment = false;
+ while (F.Eof() == false)
+ {
+ // The raw input line.
+ std::string Input;
+ if (F.ReadLine(Input) == false)
+ Input.clear();
+ // The input line with comments stripped.
+ std::string Fragment;
+
+ // Expand tabs in the input line and remove leading and trailing
+ // whitespace.
+ {
+ const int BufferSize = Input.size() * 8 + 1;
+ char *Buffer = new char[BufferSize];
+ try
+ {
+ memcpy(Buffer, Input.c_str(), Input.size() + 1);
+
+ _strtabexpand(Buffer, BufferSize);
+ _strstrip(Buffer);
+ Input = Buffer;
+ }
+ catch(...)
+ {
+ delete[] Buffer;
+ throw;
+ }
+ delete[] Buffer;
+ }
+ CurLine++;
+
+ // Now strip comments; if the whole line is contained in a
+ // comment, skip this line.
+
+ // The first meaningful character in the current fragment; will
+ // be adjusted below as we remove bytes from the front.
+ std::string::const_iterator Start = Input.begin();
+ // The last meaningful character in the current fragment.
+ std::string::const_iterator End = Input.end();
+
+ // Multi line comment
+ if (InComment == true)
+ {
+ for (std::string::const_iterator I = Start;
+ I != End; ++I)
+ {
+ if (*I == '*' && I + 1 != End && I[1] == '/')
+ {
+ Start = I + 2;
+ InComment = false;
+ break;
+ }
+ }
+ if (InComment == true)
+ continue;
+ }
+
+ // Discard single line comments
+ bool InQuote = false;
+ for (std::string::const_iterator I = Start;
+ I != End; ++I)
+ {
+ if (*I == '"')
+ InQuote = !InQuote;
+ if (InQuote == true)
+ continue;
+
+ if ((*I == '/' && I + 1 != End && I[1] == '/') ||
+ (*I == '#' && strcmp(string(I,I+6).c_str(),"#clear") != 0 &&
+ strcmp(string(I,I+8).c_str(),"#include") != 0 &&
+ strcmp(string(I,I+strlen("#x-apt-configure-index")).c_str(), "#x-apt-configure-index") != 0))
+ {
+ End = I;
+ break;
+ }
+ }
+
+ // Look for multi line comments and build up the
+ // fragment.
+ Fragment.reserve(End - Start);
+ InQuote = false;
+ for (std::string::const_iterator I = Start;
+ I != End; ++I)
+ {
+ if (*I == '"')
+ InQuote = !InQuote;
+ if (InQuote == true)
+ Fragment.push_back(*I);
+ else if (*I == '/' && I + 1 != End && I[1] == '*')
+ {
+ InComment = true;
+ for (std::string::const_iterator J = I;
+ J != End; ++J)
+ {
+ if (*J == '*' && J + 1 != End && J[1] == '/')
+ {
+ // Pretend we just finished walking over the
+ // comment, and don't add anything to the output
+ // fragment.
+ I = J + 1;
+ InComment = false;
+ break;
+ }
+ }
+
+ if (InComment == true)
+ break;
+ }
+ else
+ Fragment.push_back(*I);
+ }
+
+ // Skip blank lines.
+ if (Fragment.empty())
+ continue;
+
+ // The line has actual content; interpret what it means.
+ InQuote = false;
+ Start = Fragment.begin();
+ End = Fragment.end();
+ for (std::string::const_iterator I = Start;
+ I != End; ++I)
+ {
+ if (*I == '"')
+ InQuote = !InQuote;
+
+ if (InQuote == false && (*I == '{' || *I == ';' || *I == '}'))
+ {
+ // Put the last fragment into the buffer
+ std::string::const_iterator NonWhitespaceStart = Start;
+ std::string::const_iterator NonWhitespaceStop = I;
+ for (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; ++NonWhitespaceStart)
+ ;
+ for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; --NonWhitespaceStop)
+ ;
+ if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0)
+ LineBuffer += ' ';
+ LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop);
+
+ // Drop this from the input string, saving the character
+ // that terminated the construct we just closed. (i.e., a
+ // brace or a semicolon)
+ char TermChar = *I;
+ Start = I + 1;
+
+ // Syntax Error
+ if (TermChar == '{' && LineBuffer.empty() == true)
+ return _error->Error(_("Syntax error %s:%u: Block starts with no name."),FName.c_str(),CurLine);
+
+ // No string on this line
+ if (LineBuffer.empty() == true)
+ {
+ if (TermChar == '}')
+ leaveCurrentScope(Stack, ParentTag);
+ continue;
+ }
+
+ // Parse off the tag
+ string Tag;
+ const char *Pos = LineBuffer.c_str();
+ if (ParseQuoteWord(Pos,Tag) == false)
+ return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine);
+
+ // Parse off the word
+ string Word;
+ bool NoWord = false;
+ if (ParseCWord(Pos,Word) == false &&
+ ParseQuoteWord(Pos,Word) == false)
+ {
+ if (TermChar != '{')
+ {
+ Word = Tag;
+ Tag = "";
+ }
+ else
+ NoWord = true;
+ }
+ if (strlen(Pos) != 0)
+ return _error->Error(_("Syntax error %s:%u: Extra junk after value"),FName.c_str(),CurLine);
+
+ // Go down a level
+ if (TermChar == '{')
+ {
+ Stack.push(ParentTag);
+
+ /* Make sectional tags incorporate the section into the
+ tag string */
+ if (AsSectional == true && Word.empty() == false)
+ {
+ Tag.append("::").append(Word);
+ Word.clear();
+ }
+
+ if (ParentTag.empty() == true)
+ ParentTag = Tag;
+ else
+ ParentTag.append("::").append(Tag);
+ Tag.clear();
+ }
+
+ // Generate the item name
+ string Item;
+ if (ParentTag.empty() == true)
+ Item = Tag;
+ else
+ {
+ if (TermChar != '{' || Tag.empty() == false)
+ Item = ParentTag + "::" + Tag;
+ else
+ Item = ParentTag;
+ }
+
+ // Specials
+ if (Tag.length() >= 1 && Tag[0] == '#')
+ {
+ if (ParentTag.empty() == false)
+ return _error->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName.c_str(),CurLine);
+ Tag.erase(Tag.begin());
+ if (Tag == "clear")
+ Conf.Clear(Word);
+ else if (Tag == "include")
+ {
+ if (Depth > 10)
+ return _error->Error(_("Syntax error %s:%u: Too many nested includes"),FName.c_str(),CurLine);
+ if (Word.length() > 2 && Word.end()[-1] == '/')
+ {
+ if (ReadConfigDir(Conf,Word,AsSectional,Depth+1) == false)
+ return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
+ }
+ else
+ {
+ if (ReadConfigFile(Conf,Word,AsSectional,Depth+1) == false)
+ return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
+ }
+ }
+ else if (Tag == "x-apt-configure-index")
+ {
+ if (LoadConfigurationIndex(Word) == false)
+ return _error->Warning("Loading the configure index %s in file %s:%u failed!", Word.c_str(), FName.c_str(), CurLine);
+ }
+ else
+ return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str());
+ }
+ else if (Tag.empty() == true && NoWord == false && Word == "#clear")
+ return _error->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName.c_str(),CurLine);
+ else
+ {
+ // Set the item in the configuration class
+ if (NoWord == false)
+ Conf.Set(Item,Word);
+ }
+
+ // Empty the buffer
+ LineBuffer.clear();
+
+ // Move up a tag, but only if there is no bit to parse
+ if (TermChar == '}')
+ leaveCurrentScope(Stack, ParentTag);
+ }
+ }
+
+ // Store the remaining text, if any, in the current line buffer.
+
+ // NB: could change this to use string-based operations; I'm
+ // using strstrip now to ensure backwards compatibility.
+ // -- dburrows 2008-04-01
+ {
+ char *Buffer = new char[End - Start + 1];
+ try
+ {
+ std::copy(Start, End, Buffer);
+ Buffer[End - Start] = '\0';
+
+ const char *Stripd = _strstrip(Buffer);
+ if (*Stripd != 0 && LineBuffer.empty() == false)
+ LineBuffer += " ";
+ LineBuffer += Stripd;
+ }
+ catch(...)
+ {
+ delete[] Buffer;
+ throw;
+ }
+ delete[] Buffer;
+ }
+ }
+
+ if (LineBuffer.empty() == false)
+ return _error->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName.c_str(),CurLine);
+ return true;
+}
+ /*}}}*/
+// ReadConfigDir - Read a directory of config files /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool ReadConfigDir(Configuration &Conf,const string &Dir,
+ bool const &AsSectional, unsigned const &Depth)
+{
+ bool good = true;
+ for (auto const &I : GetListOfFilesInDir(Dir, "conf", true, true))
+ good = ReadConfigFile(Conf, I, AsSectional, Depth) && good;
+ return good;
+}
+ /*}}}*/
+// MatchAgainstConfig Constructor /*{{{*/
+Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config)
+{
+ std::vector<std::string> const strings = _config->FindVector(Config);
+ for (std::vector<std::string>::const_iterator s = strings.begin();
+ s != strings.end(); ++s)
+ {
+ regex_t *p = new regex_t;
+ if (regcomp(p, s->c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0)
+ patterns.push_back(p);
+ else
+ {
+ regfree(p);
+ delete p;
+ _error->Warning("Invalid regular expression '%s' in configuration "
+ "option '%s' will be ignored.",
+ s->c_str(), Config);
+ continue;
+ }
+ }
+ if (strings.empty() == true)
+ patterns.push_back(NULL);
+}
+ /*}}}*/
+// MatchAgainstConfig Destructor /*{{{*/
+Configuration::MatchAgainstConfig::~MatchAgainstConfig()
+{
+ clearPatterns();
+}
+void Configuration::MatchAgainstConfig::clearPatterns()
+{
+ for(std::vector<regex_t *>::const_iterator p = patterns.begin();
+ p != patterns.end(); ++p)
+ {
+ if (*p == NULL) continue;
+ regfree(*p);
+ delete *p;
+ }
+ patterns.clear();
+}
+ /*}}}*/
+// MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/
+bool Configuration::MatchAgainstConfig::Match(char const * str) const
+{
+ for(std::vector<regex_t *>::const_iterator p = patterns.begin();
+ p != patterns.end(); ++p)
+ if (*p != NULL && regexec(*p, str, 0, 0, 0) == 0)
+ return true;
+
+ return false;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/configuration.h b/apt-pkg/contrib/configuration.h
new file mode 100644
index 0000000..2a3ae1a
--- /dev/null
+++ b/apt-pkg/contrib/configuration.h
@@ -0,0 +1,155 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Configuration Class
+
+ This class provides a configuration file and command line parser
+ for a tree-oriented configuration environment. All runtime configuration
+ is stored in here.
+
+ Each configuration name is given as a fully scoped string such as
+ Foo::Bar
+ And has associated with it a text string. The Configuration class only
+ provides storage and lookup for this tree, other classes provide
+ configuration file formats (and parsers/emitters if needed).
+
+ Most things can get by quite happily with,
+ cout << _config->Find("Foo::Bar") << endl;
+
+ A special extension, support for ordered lists is provided by using the
+ special syntax, "block::list::" the trailing :: designates the
+ item as a list. To access the list you must use the tree function on
+ "block::list".
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_CONFIGURATION_H
+#define PKGLIB_CONFIGURATION_H
+
+#include <regex.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <apt-pkg/macros.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+#endif
+
+class Configuration
+{
+ public:
+
+ struct Item
+ {
+ std::string Value;
+ std::string Tag;
+ Item *Parent;
+ Item *Child;
+ Item *Next;
+
+ std::string FullTag(const Item *Stop = 0) const;
+
+ Item() : Parent(0), Child(0), Next(0) {};
+ };
+
+ private:
+
+ Item *Root;
+ bool ToFree;
+
+ Item *Lookup(Item *Head,const char *S,unsigned long const &Len,bool const &Create);
+ Item *Lookup(const char *Name,const bool &Create);
+ inline const Item *Lookup(const char *Name) const
+ {
+ return const_cast<Configuration *>(this)->Lookup(Name,false);
+ }
+
+ public:
+
+ std::string Find(const char *Name,const char *Default = 0) const;
+ std::string Find(std::string const &Name,const char *Default = 0) const {return Find(Name.c_str(),Default);};
+ std::string Find(std::string const &Name, std::string const &Default) const {return Find(Name.c_str(),Default.c_str());};
+ std::string FindFile(const char *Name,const char *Default = 0) const;
+ std::string FindDir(const char *Name,const char *Default = 0) const;
+ /** return a list of child options
+ *
+ * Options like Acquire::Languages are handled as lists which
+ * can be overridden and have a default. For the later two a comma
+ * separated list of values is supported.
+ *
+ * \param Name of the parent node
+ * \param Default list of values separated by commas */
+ std::vector<std::string> FindVector(const char *Name, std::string const &Default = "", bool const Keys = false) const;
+ std::vector<std::string> FindVector(std::string const &Name, std::string const &Default = "", bool const Keys = false) const { return FindVector(Name.c_str(), Default, Keys); };
+
+ int FindI(const char *Name,int const &Default = 0) const;
+ int FindI(std::string const &Name,int const &Default = 0) const {return FindI(Name.c_str(),Default);};
+ bool FindB(const char *Name,bool const &Default = false) const;
+ bool FindB(std::string const &Name,bool const &Default = false) const {return FindB(Name.c_str(),Default);};
+ std::string FindAny(const char *Name,const char *Default = 0) const;
+
+ inline void Set(const std::string &Name,const std::string &Value) {Set(Name.c_str(),Value);};
+ void CndSet(const char *Name,const std::string &Value);
+ void CndSet(const char *Name,const int Value);
+ void Set(const char *Name,const std::string &Value);
+ void Set(const char *Name,const int &Value);
+
+ inline bool Exists(const std::string &Name) const {return Exists(Name.c_str());};
+ bool Exists(const char *Name) const;
+ bool ExistsAny(const char *Name) const;
+
+ void MoveSubTree(char const * const OldRoot, char const * const NewRoot);
+
+ // clear a whole tree
+ void Clear(const std::string &Name);
+ void Clear();
+
+ // remove a certain value from a list (e.g. the list of "APT::Keep-Fds")
+ void Clear(std::string const &List, std::string const &Value);
+ void Clear(std::string const &List, int const &Value);
+
+ inline const Item *Tree(const char *Name) const {return Lookup(Name);};
+
+ inline void Dump() { Dump(std::clog); };
+ void Dump(std::ostream& str);
+ void Dump(std::ostream& str, char const * const root,
+ char const * const format, bool const emptyValue);
+
+ Configuration(const Item *Root);
+ Configuration();
+ ~Configuration();
+
+ /** \brief match a string against a configurable list of patterns */
+ class MatchAgainstConfig
+ {
+ std::vector<regex_t *> patterns;
+ APT_HIDDEN void clearPatterns();
+
+ public:
+ MatchAgainstConfig(char const * Config);
+ virtual ~MatchAgainstConfig();
+
+ /** \brief Returns \b true for a string matching one of the patterns */
+ bool Match(char const * str) const;
+ bool Match(std::string const &str) const { return Match(str.c_str()); };
+
+ /** \brief returns if the matcher setup was successful */
+ bool wasConstructedSuccessfully() const { return patterns.empty() == false; }
+ };
+};
+
+extern Configuration *_config;
+
+bool ReadConfigFile(Configuration &Conf,const std::string &FName,
+ bool const &AsSectional = false,
+ unsigned const &Depth = 0);
+
+bool ReadConfigDir(Configuration &Conf,const std::string &Dir,
+ bool const &AsSectional = false,
+ unsigned const &Depth = 0);
+
+#endif
diff --git a/apt-pkg/contrib/crc-16.cc b/apt-pkg/contrib/crc-16.cc
new file mode 100644
index 0000000..2e396ab
--- /dev/null
+++ b/apt-pkg/contrib/crc-16.cc
@@ -0,0 +1,77 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ CRC16 - Compute a 16bit crc very quickly
+
+ This was ripped out of the linux 2.2 kernel source (irda/crc.c) and
+ is credited to ppp.c by Michael Callahan <callahan@maths.ox.ac.uk> and
+ Al Longyear <longyear@netcom.com>
+
+ Modified by Jason Gunthorpe <jgg@debian.org> to fit the local coding
+ style, this code is believed to be in the Public Domain.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/crc-16.h>
+ /*}}}*/
+
+/*
+ * This mysterious table is just the CRC of each possible byte. It can be
+ * computed using the standard bit-at-a-time methods. The polynomial can
+ * be seen in entry 128, 0x8408. This corresponds to x^0 + x^5 + x^12.
+ * Add the implicit x^16, and you have the standard CRC-CCITT.
+ */
+static unsigned short const crc16_table[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* Recompute the FCS with one more character appended. */
+#define CalcFCS(fcs, c) (((fcs) >> 8) ^ crc16_table[((fcs) ^ (c)) & 0xff])
+unsigned short AddCRC16Byte(unsigned short fcs, unsigned char byte)
+{
+ return CalcFCS(fcs, byte);
+}
+unsigned short AddCRC16(unsigned short fcs, void const *Buf,
+ unsigned long long len)
+{
+ unsigned char const *buf = (unsigned char const *)Buf;
+ while (len--)
+ fcs = CalcFCS(fcs, *buf++);
+ return fcs;
+}
diff --git a/apt-pkg/contrib/crc-16.h b/apt-pkg/contrib/crc-16.h
new file mode 100644
index 0000000..3e07560
--- /dev/null
+++ b/apt-pkg/contrib/crc-16.h
@@ -0,0 +1,19 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ CRC16 - Compute a 16bit crc very quickly
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_CRC16_H
+#define APTPKG_CRC16_H
+
+#include <apt-pkg/macros.h>
+
+#define INIT_FCS 0xffff
+unsigned short AddCRC16Byte(unsigned short fcs, unsigned char byte) APT_PURE;
+unsigned short AddCRC16(unsigned short fcs, void const *buf,
+ unsigned long long len) APT_PURE;
+
+#endif
diff --git a/apt-pkg/contrib/error.cc b/apt-pkg/contrib/error.cc
new file mode 100644
index 0000000..3c397ea
--- /dev/null
+++ b/apt-pkg/contrib/error.cc
@@ -0,0 +1,244 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Global Error Class - Global error mechanism
+
+ We use a simple STL vector to store each error record. A PendingFlag
+ is kept which indicates when the vector contains a Sever error.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/error.h>
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+#include <list>
+#include <string>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+ /*}}}*/
+
+// Global Error Object /*{{{*/
+GlobalError *_GetErrorObj()
+{
+ static thread_local GlobalError Obj;
+ return &Obj;
+}
+ /*}}}*/
+// GlobalError::GlobalError - Constructor /*{{{*/
+GlobalError::GlobalError() : PendingFlag(false) {}
+ /*}}}*/
+// GlobalError::FatalE, Errno, WarningE, NoticeE and DebugE - Add to the list/*{{{*/
+#define GEMessage(NAME, TYPE) \
+bool GlobalError::NAME (const char *Function, const char *Description,...) { \
+ va_list args; \
+ size_t msgSize = 400; \
+ int const errsv = errno; \
+ bool retry; \
+ do { \
+ va_start(args,Description); \
+ retry = InsertErrno(TYPE, Function, Description, args, errsv, msgSize); \
+ va_end(args); \
+ } while (retry); \
+ return false; \
+}
+GEMessage(FatalE, FATAL)
+GEMessage(Errno, ERROR)
+GEMessage(WarningE, WARNING)
+GEMessage(NoticeE, NOTICE)
+GEMessage(DebugE, DEBUG)
+#undef GEMessage
+ /*}}}*/
+// GlobalError::InsertErrno - Get part of the errortype string from errno/*{{{*/
+bool GlobalError::InsertErrno(MsgType const &type, const char *Function,
+ const char *Description,...) {
+ va_list args;
+ size_t msgSize = 400;
+ int const errsv = errno;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = InsertErrno(type, Function, Description, args, errsv, msgSize);
+ va_end(args);
+ } while (retry);
+ return false;
+}
+ /*}}}*/
+// GlobalError::InsertErrno - formats an error message with the errno /*{{{*/
+bool GlobalError::InsertErrno(MsgType type, const char* Function,
+ const char* Description, va_list &args,
+ int const errsv, size_t &msgSize) {
+ char* S = (char*) malloc(msgSize);
+ int const n = snprintf(S, msgSize, "%s - %s (%i: %s)", Description,
+ Function, errsv, strerror(errsv));
+ if (n > -1 && ((unsigned int) n) < msgSize);
+ else {
+ if (n > -1)
+ msgSize = n + 1;
+ else
+ msgSize *= 2;
+ free(S);
+ return true;
+ }
+
+ bool const geins = Insert(type, S, args, msgSize);
+ free(S);
+ return geins;
+}
+ /*}}}*/
+// GlobalError::Fatal, Error, Warning, Notice and Debug - Add to the list/*{{{*/
+#define GEMessage(NAME, TYPE) \
+bool GlobalError::NAME (const char *Description,...) { \
+ va_list args; \
+ size_t msgSize = 400; \
+ bool retry; \
+ do { \
+ va_start(args,Description); \
+ retry = Insert(TYPE, Description, args, msgSize); \
+ va_end(args); \
+ } while (retry); \
+ return false; \
+}
+GEMessage(Fatal, FATAL)
+GEMessage(Error, ERROR)
+GEMessage(Warning, WARNING)
+GEMessage(Notice, NOTICE)
+GEMessage(Debug, DEBUG)
+#undef GEMessage
+ /*}}}*/
+// GlobalError::Insert - Add a errotype message to the list /*{{{*/
+bool GlobalError::Insert(MsgType const &type, const char *Description,...)
+{
+ va_list args;
+ size_t msgSize = 400;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = Insert(type, Description, args, msgSize);
+ va_end(args);
+ } while (retry);
+ return false;
+}
+ /*}}}*/
+// GlobalError::Insert - Insert a new item at the end /*{{{*/
+bool GlobalError::Insert(MsgType type, const char* Description,
+ va_list &args, size_t &msgSize) {
+ char* S = (char*) malloc(msgSize);
+ int const n = vsnprintf(S, msgSize, Description, args);
+ if (n > -1 && ((unsigned int) n) < msgSize);
+ else {
+ if (n > -1)
+ msgSize = n + 1;
+ else
+ msgSize *= 2;
+ free(S);
+ return true;
+ }
+
+ Item const m(S, type);
+ Messages.push_back(m);
+
+ if (type == ERROR || type == FATAL)
+ PendingFlag = true;
+
+ if (type == FATAL || type == DEBUG)
+ std::clog << m << std::endl;
+
+ free(S);
+ return false;
+}
+ /*}}}*/
+// GlobalError::PopMessage - Pulls a single message out /*{{{*/
+bool GlobalError::PopMessage(std::string &Text) {
+ if (Messages.empty() == true)
+ return false;
+
+ Item const msg = Messages.front();
+ Messages.pop_front();
+
+ bool const Ret = (msg.Type == ERROR || msg.Type == FATAL);
+ Text = msg.Text;
+ if (PendingFlag == false || Ret == false)
+ return Ret;
+
+ // check if another error message is pending
+ for (std::list<Item>::const_iterator m = Messages.begin();
+ m != Messages.end(); ++m)
+ if (m->Type == ERROR || m->Type == FATAL)
+ return Ret;
+
+ PendingFlag = false;
+ return Ret;
+}
+ /*}}}*/
+// GlobalError::DumpErrors - Dump all of the errors/warns to cerr /*{{{*/
+void GlobalError::DumpErrors(std::ostream &out, MsgType const &threshold,
+ bool const &mergeStack) {
+ if (mergeStack == true)
+ for (std::list<MsgStack>::const_reverse_iterator s = Stacks.rbegin();
+ s != Stacks.rend(); ++s)
+ std::copy(s->Messages.begin(), s->Messages.end(), std::front_inserter(Messages));
+
+ std::for_each(Messages.begin(), Messages.end(), [&threshold, &out](Item const &m) {
+ if (m.Type >= threshold)
+ out << m << std::endl;
+ });
+
+ Discard();
+}
+ /*}}}*/
+// GlobalError::Discard - Discard /*{{{*/
+void GlobalError::Discard() {
+ Messages.clear();
+ PendingFlag = false;
+}
+ /*}}}*/
+// GlobalError::empty - does our error list include anything? /*{{{*/
+bool GlobalError::empty(MsgType const &threshold) const {
+ if (PendingFlag == true)
+ return false;
+
+ if (Messages.empty() == true)
+ return true;
+
+ return std::find_if(Messages.begin(), Messages.end(), [&threshold](Item const &m) {
+ return m.Type >= threshold;
+ }) == Messages.end();
+}
+ /*}}}*/
+// GlobalError::PushToStack /*{{{*/
+void GlobalError::PushToStack() {
+ Stacks.emplace_back(Messages, PendingFlag);
+ Discard();
+}
+ /*}}}*/
+// GlobalError::RevertToStack /*{{{*/
+void GlobalError::RevertToStack() {
+ Discard();
+ MsgStack pack = Stacks.back();
+ Messages = pack.Messages;
+ PendingFlag = pack.PendingFlag;
+ Stacks.pop_back();
+}
+ /*}}}*/
+// GlobalError::MergeWithStack /*{{{*/
+void GlobalError::MergeWithStack() {
+ MsgStack pack = Stacks.back();
+ Messages.splice(Messages.begin(), pack.Messages);
+ PendingFlag = PendingFlag || pack.PendingFlag;
+ Stacks.pop_back();
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/error.h b/apt-pkg/contrib/error.h
new file mode 100644
index 0000000..d0f4507
--- /dev/null
+++ b/apt-pkg/contrib/error.h
@@ -0,0 +1,365 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Global Error Class - Global error mechanism
+
+ This class has a single global instance. When a function needs to
+ generate an error condition, such as a read error, it calls a member
+ in this class to add the error to a stack of errors.
+
+ By using a stack the problem with a scheme like errno is removed and
+ it allows a very detailed account of what went wrong to be transmitted
+ to the UI for display. (Errno has problems because each function sets
+ errno to 0 if it didn't have an error thus eraseing erno in the process
+ of cleanup)
+
+ Several predefined error generators are provided to handle common
+ things like errno. The general idea is that all methods return a bool.
+ If the bool is true then things are OK, if it is false then things
+ should start being undone and the stack should unwind under program
+ control.
+
+ A Warning should not force the return of false. Things did not fail, but
+ they might have had unexpected problems. Errors are stored in a FIFO
+ so Pop will return the first item..
+
+ I have some thoughts about extending this into a more general UI<->
+ Engine interface, ie allowing the Engine to say 'The disk is full' in
+ a dialog that says 'Panic' and 'Retry'.. The error generator functions
+ like errno, Warning and Error return false always so this is normal:
+ if (open(..))
+ return _error->Errno(..);
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_ERROR_H
+#define PKGLIB_ERROR_H
+
+#include <apt-pkg/macros.h>
+
+#include <iostream>
+#include <list>
+#include <string>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+class GlobalError /*{{{*/
+{
+public: /*{{{*/
+ /** \brief a message can have one of following severity */
+ enum MsgType {
+ /** \brief Message will be printed instantly as it is likely that
+ this error will lead to a complete crash */
+ FATAL = 40,
+ /** \brief An error does hinder the correct execution and should be corrected */
+ ERROR = 30,
+ /** \brief indicates problem that can lead to errors later on */
+ WARNING = 20,
+ /** \brief deprecation warnings, old fallback behavior, … */
+ NOTICE = 10,
+ /** \brief for developers only in areas it is hard to print something directly */
+ DEBUG = 0
+ };
+
+ /** \brief add a fatal error message with errno to the list
+ *
+ * \param Function name of the function generating the error
+ * \param Description format string for the error message
+ *
+ * \return \b false
+ */
+ bool FatalE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;
+
+ /** \brief add an Error message with errno to the list
+ *
+ * \param Function name of the function generating the error
+ * \param Description format string for the error message
+ *
+ * \return \b false
+ */
+ bool Errno(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;
+
+ /** \brief add a warning message with errno to the list
+ *
+ * A warning should be considered less severe than an error and
+ * may be ignored by the client.
+ *
+ * \param Function Name of the function generates the warning.
+ * \param Description Format string for the warning message.
+ *
+ * \return \b false
+ */
+ bool WarningE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;
+
+ /** \brief add a notice message with errno to the list
+ *
+ * \param Function name of the function generating the error
+ * \param Description format string for the error message
+ *
+ * \return \b false
+ */
+ bool NoticeE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;
+
+ /** \brief add a debug message with errno to the list
+ *
+ * \param Function name of the function generating the error
+ * \param Description format string for the error message
+ *
+ * \return \b false
+ */
+ bool DebugE(const char *Function,const char *Description,...) APT_PRINTF(3) APT_COLD;
+
+ /** \brief adds an errno message with the given type
+ *
+ * \param type of the error message
+ * \param Function which failed
+ * \param Description of the error
+ */
+ bool InsertErrno(MsgType const &type, const char* Function,
+ const char* Description,...) APT_PRINTF(4) APT_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) APT_COLD;
+
+ /** \brief add an fatal error message to the list
+ *
+ * Most of the stuff we consider as "error" is also "fatal" for
+ * the user as the application will not have the expected result,
+ * but a fatal message here means that it gets printed directly
+ * to stderr in addition to adding it to the list as the error
+ * leads sometimes to crashes and a maybe duplicated message
+ * is better than "Segfault" as the only displayed text
+ *
+ * \param Description Format string for the fatal error message.
+ *
+ * \return \b false
+ */
+ bool Fatal(const char *Description,...) APT_PRINTF(2) APT_COLD;
+
+ /** \brief add an Error message to the list
+ *
+ * \param Description Format string for the error message.
+ *
+ * \return \b false
+ */
+ bool Error(const char *Description,...) APT_PRINTF(2) APT_COLD;
+
+ /** \brief add a warning message to the list
+ *
+ * A warning should be considered less severe than an error and
+ * may be ignored by the client.
+ *
+ * \param Description Format string for the message
+ *
+ * \return \b false
+ */
+ bool Warning(const char *Description,...) APT_PRINTF(2) APT_COLD;
+
+ /** \brief add a notice message to the list
+ *
+ * A notice should be considered less severe than an error or a
+ * warning and can be ignored by the client without further problems
+ * for some times, but he should consider fixing the problem.
+ * This error type can be used for e.g. deprecation warnings of options.
+ *
+ * \param Description Format string for the message
+ *
+ * \return \b false
+ */
+ bool Notice(const char *Description,...) APT_PRINTF(2) APT_COLD;
+
+ /** \brief add a debug message to the list
+ *
+ * \param Description Format string for the message
+ *
+ * \return \b false
+ */
+ bool Debug(const char *Description,...) APT_PRINTF(2) APT_COLD;
+
+ /** \brief adds an error message with the given type
+ *
+ * \param type of the error message
+ * \param Description of the error
+ */
+ bool Insert(MsgType const &type, const char* Description,...) APT_PRINTF(3) APT_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) APT_COLD;
+
+ /** \brief is an error in the list?
+ *
+ * \return \b true if an error is included in the list, \b false otherwise
+ */
+ inline bool PendingError() const APT_PURE {return PendingFlag;};
+
+ /** \brief is the list empty?
+ *
+ * Can be used to check if the current stack level doesn't include
+ * anything equal or more severe than a given threshold, defaulting
+ * to warning level for historic reasons.
+ *
+ * \param threshold minimum level considered
+ *
+ * \return \b true if the list is empty, \b false otherwise
+ */
+ bool empty(MsgType const &threshold = WARNING) const APT_PURE;
+
+ /** \brief returns and removes the first (or last) message in the list
+ *
+ * \param[out] Text message of the first/last item
+ *
+ * \return \b true if the message was an error, \b false otherwise
+ */
+ bool PopMessage(std::string &Text);
+
+ /** \brief clears the list of messages */
+ void Discard();
+
+ /** \brief outputs the list of messages to the given stream
+ *
+ * Note that all messages are discarded, even undisplayed ones.
+ *
+ * \param[out] out output stream to write the messages in
+ * \param threshold minimum level considered
+ * \param mergeStack if true recursively dumps the entire stack
+ */
+ void DumpErrors(std::ostream &out, MsgType const &threshold = WARNING,
+ bool const &mergeStack = true);
+
+ /** \brief dumps the list of messages to std::cerr
+ *
+ * Note that all messages are discarded, also the notices
+ * displayed or not.
+ *
+ * \param threshold minimum level printed
+ */
+ void inline DumpErrors(MsgType const &threshold) {
+ DumpErrors(std::cerr, threshold);
+ }
+
+ // mvo: we do this instead of using a default parameter in the
+ // previous declaration to avoid a (subtle) API break for
+ // e.g. sigc++ and mem_fun0
+ /** \brief dumps the messages of type WARNING or higher to std::cerr
+ *
+ * Note that all messages are discarded, displayed or not.
+ *
+ */
+ void inline DumpErrors() {
+ DumpErrors(WARNING);
+ }
+
+ /** \brief put the current Messages into the stack
+ *
+ * All "old" messages will be pushed into a stack to
+ * them later back, but for now the Message query will be
+ * empty and performs as no messages were present before.
+ *
+ * The stack can be as deep as you want - all stack operations
+ * will only operate on the last element in the stack.
+ */
+ void PushToStack();
+
+ /** \brief throw away all current messages */
+ void RevertToStack();
+
+ /** \brief merge current and stack together */
+ void MergeWithStack();
+
+ /** \brief return the deep of the stack */
+ size_t StackCount() const APT_PURE {
+ return Stacks.size();
+ }
+
+ GlobalError();
+ /*}}}*/
+private: /*{{{*/
+ struct Item {
+ std::string Text;
+ MsgType Type;
+
+ Item(char const *Text, MsgType const &Type) :
+ Text(Text), Type(Type) {};
+
+ APT_HIDDEN friend std::ostream& operator<< (std::ostream &out, Item i) {
+ switch(i.Type) {
+ case FATAL:
+ case ERROR: out << 'E'; break;
+ case WARNING: out << 'W'; break;
+ case NOTICE: out << 'N'; break;
+ case DEBUG: out << 'D'; break;
+ }
+ out << ": ";
+ std::string::size_type line_start = 0;
+ std::string::size_type line_end;
+ while ((line_end = i.Text.find_first_of("\n\r", line_start)) != std::string::npos) {
+ if (line_start != 0)
+ out << std::endl << " ";
+ out << i.Text.substr(line_start, line_end - line_start);
+ line_start = i.Text.find_first_not_of("\n\r", line_end + 1);
+ if (line_start == std::string::npos)
+ break;
+ }
+ if (line_start == 0)
+ out << i.Text;
+ else if (line_start != std::string::npos)
+ out << std::endl << " " << i.Text.substr(line_start);
+ return out;
+ }
+ };
+
+ std::list<Item> Messages;
+ bool PendingFlag;
+
+ struct MsgStack {
+ std::list<Item> Messages;
+ bool const PendingFlag;
+
+ MsgStack(std::list<Item> const &Messages, bool const &Pending) :
+ Messages(Messages), PendingFlag(Pending) {};
+ };
+
+ std::list<MsgStack> Stacks;
+ /*}}}*/
+};
+ /*}}}*/
+
+// The 'extra-ansi' syntax is used to help with collisions.
+GlobalError *_GetErrorObj();
+#define _error _GetErrorObj()
+
+#endif
diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc
new file mode 100644
index 0000000..4f12349
--- /dev/null
+++ b/apt-pkg/contrib/fileutl.cc
@@ -0,0 +1,3443 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ File Utilities
+
+ CopyFile - Buffered copy of a single file
+ GetLock - dpkg compatible lock file manipulation (fcntl)
+
+ Most of this source is placed in the Public Domain, do with it what
+ you will
+ It was originally written by Jason Gunthorpe <jgg@debian.org>.
+ FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
+
+ The exception is RunScripts() it is under the GPLv2
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/sptr.h>
+#include <apt-pkg/strutl.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+#ifdef HAVE_BZ2
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA
+#include <lzma.h>
+#endif
+#ifdef HAVE_LZ4
+#include <lz4frame.h>
+#endif
+#ifdef HAVE_ZSTD
+#include <zstd.h>
+#endif
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-bus.h>
+#endif
+#include <endian.h>
+#include <stdint.h>
+
+#if __gnu_linux__
+#include <sys/prctl.h>
+#endif
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+/* Should be a multiple of the common page size (4096) */
+static constexpr unsigned long long APT_BUFFER_SIZE = 64 * 1024;
+
+// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RunScripts(const char *Cnf)
+{
+ Configuration::Item const *Opts = _config->Tree(Cnf);
+ if (Opts == 0 || Opts->Child == 0)
+ return true;
+ Opts = Opts->Child;
+
+ // Fork for running the system calls
+ pid_t Child = ExecFork();
+
+ // This is the child
+ if (Child == 0)
+ {
+ if (_system != nullptr && _system->IsLocked() == true && (stringcasecmp(Cnf, "dpkg::post-invoke") == 0 || stringcasecmp(Cnf, "dpkg::pre-invoke") == 0))
+ setenv("DPKG_FRONTEND_LOCKED", "true", 1);
+ if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
+ {
+ std::cerr << "Chrooting into "
+ << _config->FindDir("DPkg::Chroot-Directory")
+ << std::endl;
+ if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
+ _exit(100);
+ }
+
+ if (chdir("/tmp/") != 0)
+ _exit(100);
+
+ unsigned int Count = 1;
+ for (; Opts != 0; Opts = Opts->Next, Count++)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+
+ if(_config->FindB("Debug::RunScripts", false) == true)
+ std::clog << "Running external script: '"
+ << Opts->Value << "'" << std::endl;
+
+ if (system(Opts->Value.c_str()) != 0)
+ _exit(100+Count);
+ }
+ _exit(0);
+ }
+
+ // Wait for the child
+ int Status = 0;
+ while (waitpid(Child,&Status,0) != Child)
+ {
+ if (errno == EINTR)
+ continue;
+ return _error->Errno("waitpid","Couldn't wait for subprocess");
+ }
+
+ // Restore sig int/quit
+ signal(SIGQUIT,SIG_DFL);
+ signal(SIGINT,SIG_DFL);
+
+ // Check for an error code.
+ if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
+ {
+ unsigned int Count = WEXITSTATUS(Status);
+ if (Count > 100)
+ {
+ Count -= 100;
+ for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
+ _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
+ }
+
+ return _error->Error("Sub-process returned an error code");
+ }
+
+ return true;
+}
+ /*}}}*/
+
+// CopyFile - Buffered copy of a file /*{{{*/
+// ---------------------------------------------------------------------
+/* 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 ||
+ From.Failed() == true || To.Failed() == true)
+ return false;
+
+ // Buffered copy between fds
+ constexpr size_t BufSize = APT_BUFFER_SIZE;
+ std::unique_ptr<unsigned char[]> Buf(new unsigned char[BufSize]);
+ unsigned long long ToRead = 0;
+ do {
+ if (From.Read(Buf.get(),BufSize, &ToRead) == false ||
+ To.Write(Buf.get(),ToRead) == false)
+ return false;
+ } while (ToRead != 0);
+
+ return true;
+}
+ /*}}}*/
+bool RemoveFileAt(char const * const Function, int const dirfd, std::string const &FileName)/*{{{*/
+{
+ if (FileName == "/dev/null")
+ return true;
+ errno = 0;
+ if (unlinkat(dirfd, FileName.c_str(), 0) != 0)
+ {
+ if (errno == ENOENT)
+ return true;
+
+ return _error->WarningE(Function,_("Problem unlinking the file %s"), FileName.c_str());
+ }
+ return true;
+}
+ /*}}}*/
+bool RemoveFile(char const * const Function, std::string const &FileName)/*{{{*/
+{
+ if (FileName == "/dev/null")
+ return true;
+ errno = 0;
+ if (unlink(FileName.c_str()) != 0)
+ {
+ if (errno == ENOENT)
+ return true;
+
+ return _error->WarningE(Function,_("Problem unlinking the file %s"), FileName.c_str());
+ }
+ return true;
+}
+ /*}}}*/
+// GetLock - Gets a lock file /*{{{*/
+// ---------------------------------------------------------------------
+/* This will create an empty file of the given name and lock it. Once this
+ is done all other calls to GetLock in any other process will fail with
+ -1. The return result is the fd of the file, the call should call
+ close at some time. */
+int GetLock(string File,bool Errors)
+{
+ // GetLock() is used in aptitude on directories with public-write access
+ // Use O_NOFOLLOW here to prevent symlink traversal attacks
+ int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
+ if (FD < 0)
+ {
+ // Read only .. can't have locking problems there.
+ if (errno == EROFS)
+ {
+ _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
+ return dup(0); // Need something for the caller to close
+ }
+
+ if (Errors == true)
+ _error->Errno("open",_("Could not open lock file %s"),File.c_str());
+
+ // Feh.. We do this to distinguish the lock vs open case..
+ errno = EPERM;
+ return -1;
+ }
+ SetCloseExec(FD,true);
+
+ // Acquire a write lock
+ struct flock fl;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ if (fcntl(FD,F_SETLK,&fl) == -1)
+ {
+ // always close to not leak resources
+ int Tmp = errno;
+ close(FD);
+ errno = Tmp;
+
+ if (errno == ENOLCK)
+ {
+ _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
+ return dup(0); // Need something for the caller to close
+ }
+
+ if (Errors == true)
+ _error->Errno("open",_("Could not get lock %s"),File.c_str());
+
+ return -1;
+ }
+
+ return FD;
+}
+ /*}}}*/
+// FileExists - Check if a file exists /*{{{*/
+// ---------------------------------------------------------------------
+/* Beware: Directories are also files! */
+bool FileExists(string File)
+{
+ struct stat Buf;
+ if (stat(File.c_str(),&Buf) != 0)
+ return false;
+ return true;
+}
+ /*}}}*/
+// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RealFileExists(string File)
+{
+ struct stat Buf;
+ if (stat(File.c_str(),&Buf) != 0)
+ return false;
+ return ((Buf.st_mode & S_IFREG) != 0);
+}
+ /*}}}*/
+// DirectoryExists - Check if a directory exists and is really one /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool DirectoryExists(string const &Path)
+{
+ struct stat Buf;
+ if (stat(Path.c_str(),&Buf) != 0)
+ return false;
+ return ((Buf.st_mode & S_IFDIR) != 0);
+}
+ /*}}}*/
+// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
+// ---------------------------------------------------------------------
+/* This method will create all directories needed for path in good old
+ mkdir -p style but refuses to do this if Parent is not a prefix of
+ this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
+ so it will create apt/archives if /var/cache exists - on the other
+ hand if the parent is /var/lib the creation will fail as this path
+ is not a parent of the path to be generated. */
+bool CreateDirectory(string const &Parent, string const &Path)
+{
+ if (Parent.empty() == true || Path.empty() == true)
+ return false;
+
+ if (DirectoryExists(Path) == true)
+ return true;
+
+ if (DirectoryExists(Parent) == false)
+ return false;
+
+ // we are not going to create directories "into the blue"
+ if (Path.compare(0, Parent.length(), Parent) != 0)
+ return false;
+
+ vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
+ string progress = Parent;
+ for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
+ {
+ if (d->empty() == true)
+ continue;
+
+ progress.append("/").append(*d);
+ if (DirectoryExists(progress) == true)
+ continue;
+
+ if (mkdir(progress.c_str(), 0755) != 0)
+ return false;
+ }
+ return true;
+}
+ /*}}}*/
+// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
+// ---------------------------------------------------------------------
+/* a small wrapper around CreateDirectory to check if it exists and to
+ remove the trailing "/apt/" from the parent directory if needed */
+bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
+{
+ if (DirectoryExists(Path) == true)
+ return true;
+
+ size_t const len = Parent.size();
+ if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
+ {
+ if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
+ return true;
+ }
+ else if (CreateDirectory(Parent, Path) == true)
+ return true;
+
+ return false;
+}
+ /*}}}*/
+// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
+// ---------------------------------------------------------------------
+/* If an extension is given only files with this extension are included
+ in the returned vector, otherwise every "normal" file is included. */
+std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
+ bool const &SortList, bool const &AllowNoExt)
+{
+ std::vector<string> ext;
+ ext.reserve(2);
+ if (Ext.empty() == false)
+ ext.push_back(Ext);
+ if (AllowNoExt == true && ext.empty() == false)
+ ext.push_back("");
+ return GetListOfFilesInDir(Dir, ext, SortList);
+}
+std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
+ bool const &SortList)
+{
+ // Attention debuggers: need to be set with the environment config file!
+ bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
+ if (Debug == true)
+ {
+ std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
+ if (Ext.empty() == true)
+ std::clog << "\tNO extension" << std::endl;
+ else
+ for (std::vector<string>::const_iterator e = Ext.begin();
+ e != Ext.end(); ++e)
+ std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
+ }
+
+ std::vector<string> List;
+
+ if (DirectoryExists(Dir) == false)
+ {
+ _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
+ return List;
+ }
+
+ Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
+ DIR *D = opendir(Dir.c_str());
+ if (D == 0)
+ {
+ if (errno == EACCES)
+ _error->WarningE("opendir", _("Unable to read %s"), Dir.c_str());
+ else
+ _error->Errno("opendir", _("Unable to read %s"), Dir.c_str());
+ return List;
+ }
+
+ for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
+ {
+ // skip "hidden" files
+ if (Ent->d_name[0] == '.')
+ continue;
+
+ // Make sure it is a file and not something else
+ string const File = flCombine(Dir,Ent->d_name);
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (Ent->d_type != DT_REG)
+#endif
+ {
+ if (RealFileExists(File) == false)
+ {
+ // do not show ignoration warnings for directories
+ if (
+#ifdef _DIRENT_HAVE_D_TYPE
+ Ent->d_type == DT_DIR ||
+#endif
+ DirectoryExists(File) == true)
+ continue;
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
+ continue;
+ }
+ }
+
+ // check for accepted extension:
+ // no extension given -> periods are bad as hell!
+ // extensions given -> "" extension allows no extension
+ if (Ext.empty() == false)
+ {
+ string d_ext = flExtension(Ent->d_name);
+ if (d_ext == Ent->d_name) // no extension
+ {
+ if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
+ continue;
+ }
+ }
+ else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
+ if (SilentIgnore.Match(Ent->d_name) == false)
+ _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
+ continue;
+ }
+ }
+
+ // Skip bad filenames ala run-parts
+ const char *C = Ent->d_name;
+ for (; *C != 0; ++C)
+ if (isalpha(*C) == 0 && isdigit(*C) == 0
+ && *C != '_' && *C != '-' && *C != ':') {
+ // no required extension -> dot is a bad character
+ if (*C == '.' && Ext.empty() == false)
+ continue;
+ break;
+ }
+
+ // we don't reach the end of the name -> bad character included
+ if (*C != 0)
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → bad character »"
+ << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
+ continue;
+ }
+
+ // skip filenames which end with a period. These are never valid
+ if (*(C - 1) == '.')
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
+ continue;
+ }
+
+ if (Debug == true)
+ std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
+ List.push_back(File);
+ }
+ closedir(D);
+
+ if (SortList == true)
+ std::sort(List.begin(),List.end());
+ return List;
+}
+std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
+{
+ bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
+ if (Debug == true)
+ std::clog << "Accept in " << Dir << " all regular files" << std::endl;
+
+ std::vector<string> List;
+
+ if (DirectoryExists(Dir) == false)
+ {
+ _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
+ return List;
+ }
+
+ DIR *D = opendir(Dir.c_str());
+ if (D == 0)
+ {
+ _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+ return List;
+ }
+
+ for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
+ {
+ // skip "hidden" files
+ if (Ent->d_name[0] == '.')
+ continue;
+
+ // Make sure it is a file and not something else
+ string const File = flCombine(Dir,Ent->d_name);
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (Ent->d_type != DT_REG)
+#endif
+ {
+ if (RealFileExists(File) == false)
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
+ continue;
+ }
+ }
+
+ // Skip bad filenames ala run-parts
+ const char *C = Ent->d_name;
+ for (; *C != 0; ++C)
+ if (isalpha(*C) == 0 && isdigit(*C) == 0
+ && *C != '_' && *C != '-' && *C != '.')
+ break;
+
+ // we don't reach the end of the name -> bad character included
+ if (*C != 0)
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
+ continue;
+ }
+
+ // skip filenames which end with a period. These are never valid
+ if (*(C - 1) == '.')
+ {
+ if (Debug == true)
+ std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
+ continue;
+ }
+
+ if (Debug == true)
+ std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
+ List.push_back(File);
+ }
+ closedir(D);
+
+ if (SortList == true)
+ std::sort(List.begin(),List.end());
+ return List;
+}
+ /*}}}*/
+// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
+// ---------------------------------------------------------------------
+/* We return / on failure. */
+string SafeGetCWD()
+{
+ // Stash the current dir.
+ char S[300];
+ S[0] = 0;
+ if (getcwd(S,sizeof(S)-2) == 0)
+ return "/";
+ unsigned int Len = strlen(S);
+ S[Len] = '/';
+ S[Len+1] = 0;
+ return S;
+}
+ /*}}}*/
+// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
+// ---------------------------------------------------------------------
+/* We return / on failure. */
+time_t GetModificationTime(string const &Path)
+{
+ struct stat St;
+ if (stat(Path.c_str(), &St) < 0)
+ return -1;
+ return St.st_mtime;
+}
+ /*}}}*/
+// flNotDir - Strip the directory from the filename /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string flNotDir(string File)
+{
+ string::size_type Res = File.rfind('/');
+ if (Res == string::npos)
+ return File;
+ Res++;
+ return string(File,Res,Res - File.length());
+}
+ /*}}}*/
+// flNotFile - Strip the file from the directory name /*{{{*/
+// ---------------------------------------------------------------------
+/* Result ends in a / */
+string flNotFile(string File)
+{
+ string::size_type Res = File.rfind('/');
+ if (Res == string::npos)
+ return "./";
+ Res++;
+ return string(File,0,Res);
+}
+ /*}}}*/
+// flExtension - Return the extension for the file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string flExtension(string File)
+{
+ string::size_type Res = File.rfind('.');
+ if (Res == string::npos)
+ return File;
+ Res++;
+ return string(File,Res,Res - File.length());
+}
+ /*}}}*/
+// flNoLink - If file is a symlink then deref it /*{{{*/
+// ---------------------------------------------------------------------
+/* If the name is not a link then the returned path is the input. */
+string flNoLink(string File)
+{
+ struct stat St;
+ if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
+ return File;
+ if (stat(File.c_str(),&St) != 0)
+ return File;
+
+ /* Loop resolving the link. There is no need to limit the number of
+ loops because the stat call above ensures that the symlink is not
+ circular */
+ char Buffer[1024];
+ string NFile = File;
+ while (1)
+ {
+ // Read the link
+ ssize_t Res;
+ if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
+ (size_t)Res >= sizeof(Buffer))
+ return File;
+
+ // Append or replace the previous path
+ Buffer[Res] = 0;
+ if (Buffer[0] == '/')
+ NFile = Buffer;
+ else
+ NFile = flNotFile(NFile) + Buffer;
+
+ // See if we are done
+ if (lstat(NFile.c_str(),&St) != 0)
+ return File;
+ if (S_ISLNK(St.st_mode) == 0)
+ return NFile;
+ }
+}
+ /*}}}*/
+// flCombine - Combine a file and a directory /*{{{*/
+// ---------------------------------------------------------------------
+/* If the file is an absolute path then it is just returned, otherwise
+ the directory is pre-pended to it. */
+string flCombine(string Dir,string File)
+{
+ if (File.empty() == true)
+ return string();
+
+ if (File[0] == '/' || Dir.empty() == true)
+ return File;
+ if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
+ return File;
+ if (Dir[Dir.length()-1] == '/')
+ return Dir + File;
+ return Dir + '/' + File;
+}
+ /*}}}*/
+// flAbsPath - Return the absolute path of the filename /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string flAbsPath(string File)
+{
+ char *p = realpath(File.c_str(), NULL);
+ if (p == NULL)
+ {
+ _error->Errno("realpath", "flAbsPath on %s failed", File.c_str());
+ return "";
+ }
+ std::string AbsPath(p);
+ free(p);
+ return AbsPath;
+}
+ /*}}}*/
+std::string flNormalize(std::string file) /*{{{*/
+{
+ if (file.empty())
+ return file;
+ // do some normalisation by removing // and /./ from the path
+ size_t found = string::npos;
+ while ((found = file.find("/./")) != string::npos)
+ file.replace(found, 3, "/");
+ while ((found = file.find("//")) != string::npos)
+ file.replace(found, 2, "/");
+
+ if (APT::String::Startswith(file, "/dev/null"))
+ {
+ file.erase(strlen("/dev/null"));
+ return file;
+ }
+ return file;
+}
+ /*}}}*/
+// SetCloseExec - Set the close on exec flag /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void SetCloseExec(int Fd,bool Close)
+{
+ if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
+ {
+ cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
+ exit(100);
+ }
+}
+ /*}}}*/
+// SetNonBlock - Set the nonblocking flag /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void SetNonBlock(int Fd,bool Block)
+{
+ int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
+ if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
+ {
+ cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
+ exit(100);
+ }
+}
+ /*}}}*/
+// WaitFd - Wait for a FD to become readable /*{{{*/
+// ---------------------------------------------------------------------
+/* This waits for a FD to become readable using select. It is useful for
+ applications making use of non-blocking sockets. The timeout is
+ in seconds. */
+bool WaitFd(int Fd,bool write,unsigned long timeout)
+{
+ fd_set Set;
+ struct timeval tv;
+ FD_ZERO(&Set);
+ FD_SET(Fd,&Set);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (write == true)
+ {
+ int Res;
+ do
+ {
+ Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
+ }
+ while (Res < 0 && errno == EINTR);
+
+ if (Res <= 0)
+ return false;
+ }
+ else
+ {
+ int Res;
+ do
+ {
+ Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
+ }
+ while (Res < 0 && errno == EINTR);
+
+ if (Res <= 0)
+ return false;
+ }
+
+ return true;
+}
+ /*}}}*/
+// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
+ * set.
+ */
+void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
+{
+ Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
+ if (Opts != 0 && Opts->Child != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+ int fd = atoi(Opts->Value.c_str());
+ KeepFDs.insert(fd);
+ }
+ }
+}
+ /*}}}*/
+// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used if you want to cleanse the environment for the forked
+ child, it fixes up the important signals and nukes all of the fds,
+ otherwise acts like normal fork. */
+pid_t ExecFork()
+{
+ set<int> KeepFDs;
+ // we need to merge the Keep-Fds as external tools like
+ // debconf-apt-progress use it
+ MergeKeepFdsFromConfiguration(KeepFDs);
+ return ExecFork(KeepFDs);
+}
+
+pid_t ExecFork(std::set<int> KeepFDs)
+{
+ // Fork off the process
+ pid_t Process = fork();
+ if (Process < 0)
+ {
+ cerr << "FATAL -> Failed to fork." << endl;
+ exit(100);
+ }
+
+ // Spawn the subprocess
+ if (Process == 0)
+ {
+ // Setup the signals
+ signal(SIGPIPE,SIG_DFL);
+ signal(SIGQUIT,SIG_DFL);
+ signal(SIGINT,SIG_DFL);
+ signal(SIGWINCH,SIG_DFL);
+ signal(SIGCONT,SIG_DFL);
+ signal(SIGTSTP,SIG_DFL);
+
+ DIR *dir = opendir("/proc/self/fd");
+ if (dir != NULL)
+ {
+ struct dirent *ent;
+ while ((ent = readdir(dir)))
+ {
+ int fd = atoi(ent->d_name);
+ // If fd > 0, it was a fd number and not . or ..
+ if (fd >= 3 && KeepFDs.find(fd) == KeepFDs.end())
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ }
+ closedir(dir);
+ } else {
+ long ScOpenMax = sysconf(_SC_OPEN_MAX);
+ // Close all of our FDs - just in case
+ for (int K = 3; K != ScOpenMax; K++)
+ {
+ if(KeepFDs.find(K) == KeepFDs.end())
+ fcntl(K,F_SETFD,FD_CLOEXEC);
+ }
+ }
+ }
+
+ return Process;
+}
+ /*}}}*/
+// ExecWait - Fancy waitpid /*{{{*/
+// ---------------------------------------------------------------------
+/* Waits for the given sub process. If Reap is set then no errors are
+ generated. Otherwise a failed subprocess will generate a proper descriptive
+ message */
+bool ExecWait(pid_t Pid,const char *Name,bool Reap)
+{
+ if (Pid <= 1)
+ return true;
+
+ // Wait and collect the error code
+ int Status;
+ while (waitpid(Pid,&Status,0) != Pid)
+ {
+ if (errno == EINTR)
+ continue;
+
+ if (Reap == true)
+ return false;
+
+ return _error->Error(_("Waited for %s but it wasn't there"),Name);
+ }
+
+
+ // Check for an error code.
+ if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
+ {
+ if (Reap == true)
+ return false;
+ if (WIFSIGNALED(Status) != 0)
+ {
+ if( WTERMSIG(Status) == SIGSEGV)
+ return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
+ else
+ return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
+ }
+
+ if (WIFEXITED(Status) != 0)
+ return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
+
+ return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
+ }
+
+ return true;
+}
+ /*}}}*/
+// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
+bool StartsWithGPGClearTextSignature(string const &FileName)
+{
+ FILE* gpg = fopen(FileName.c_str(), "r");
+ if (gpg == nullptr)
+ return false;
+
+ char * lineptr = nullptr;
+ size_t n = 0;
+ errno = 0;
+ ssize_t const result = getline(&lineptr, &n, gpg);
+ if (errno != 0)
+ {
+ _error->Errno("getline", "Could not read from %s", FileName.c_str());
+ fclose(gpg);
+ free(lineptr);
+ return false;
+ }
+ fclose(gpg);
+
+ _strrstrip(lineptr);
+ static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----";
+ if (result == -1 || strcmp(lineptr, SIGMSG) != 0)
+ {
+ free(lineptr);
+ return false;
+ }
+ free(lineptr);
+ return true;
+}
+ /*}}}*/
+// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
+bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
+{
+ if (strcmp(file, "/dev/null") == 0)
+ return true;
+ bool Res = true;
+ if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
+ {
+ // ensure the file is owned by root and has good permissions
+ struct passwd const * const pw = getpwnam(user);
+ struct group const * const gr = getgrnam(group);
+ if (pw != NULL && gr != NULL && lchown(file, pw->pw_uid, gr->gr_gid) != 0)
+ Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
+ }
+ struct stat Buf;
+ if (lstat(file, &Buf) != 0 || S_ISLNK(Buf.st_mode))
+ return Res;
+ if (chmod(file, mode) != 0)
+ Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
+ return Res;
+}
+ /*}}}*/
+
+struct APT_HIDDEN simple_buffer { /*{{{*/
+ size_t buffersize_max = 0;
+ unsigned long long bufferstart = 0;
+ unsigned long long bufferend = 0;
+ char *buffer = nullptr;
+
+ simple_buffer() {
+ reset(4096);
+ }
+ ~simple_buffer() {
+ delete[] buffer;
+ }
+
+ const char *get() const { return buffer + bufferstart; }
+ char *get() { return buffer + bufferstart; }
+ const char *getend() const { return buffer + bufferend; }
+ char *getend() { return buffer + bufferend; }
+ bool empty() const { return bufferend <= bufferstart; }
+ bool full() const { return bufferend == buffersize_max; }
+ unsigned long long free() const { return buffersize_max - bufferend; }
+ unsigned long long size() const { return bufferend-bufferstart; }
+ void reset(size_t size)
+ {
+ if (size > buffersize_max) {
+ delete[] buffer;
+ buffersize_max = size;
+ buffer = new char[size];
+ }
+ reset();
+ }
+ void reset() { bufferend = bufferstart = 0; }
+ ssize_t read(void *to, unsigned long long requested_size) APT_MUSTCHECK
+ {
+ if (size() < requested_size)
+ requested_size = size();
+ memcpy(to, buffer + bufferstart, requested_size);
+ bufferstart += requested_size;
+ if (bufferstart == bufferend)
+ bufferstart = bufferend = 0;
+ return requested_size;
+ }
+ ssize_t write(const void *from, unsigned long long requested_size) APT_MUSTCHECK
+ {
+ if (free() < requested_size)
+ requested_size = free();
+ memcpy(getend(), from, requested_size);
+ bufferend += requested_size;
+ if (bufferstart == bufferend)
+ bufferstart = bufferend = 0;
+ return requested_size;
+ }
+};
+ /*}}}*/
+
+class APT_HIDDEN FileFdPrivate { /*{{{*/
+ friend class BufferedWriteFileFdPrivate;
+protected:
+ FileFd * const filefd;
+ simple_buffer buffer;
+ int compressed_fd;
+ pid_t compressor_pid;
+ bool is_pipe;
+ APT::Configuration::Compressor compressor;
+ unsigned int openmode;
+ unsigned long long seekpos;
+public:
+
+ explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
+ compressed_fd(-1), compressor_pid(-1), is_pipe(false),
+ openmode(0), seekpos(0) {};
+ virtual APT::Configuration::Compressor get_compressor() const
+ {
+ return compressor;
+ }
+ virtual void set_compressor(APT::Configuration::Compressor const &compressor)
+ {
+ this->compressor = compressor;
+ }
+ virtual unsigned int get_openmode() const
+ {
+ return openmode;
+ }
+ virtual void set_openmode(unsigned int openmode)
+ {
+ this->openmode = openmode;
+ }
+ virtual bool get_is_pipe() const
+ {
+ return is_pipe;
+ }
+ virtual void set_is_pipe(bool is_pipe)
+ {
+ this->is_pipe = is_pipe;
+ }
+ virtual unsigned long long get_seekpos() const
+ {
+ return seekpos;
+ }
+ virtual void set_seekpos(unsigned long long seekpos)
+ {
+ this->seekpos = seekpos;
+ }
+
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
+ ssize_t InternalRead(void * To, unsigned long long Size)
+ {
+ // Drain the buffer if needed.
+ if (buffer.empty() == false)
+ {
+ return buffer.read(To, Size);
+ }
+ return InternalUnbufferedRead(To, Size);
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) = 0;
+ virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
+ virtual char * InternalReadLine(char * To, unsigned long long Size)
+ {
+ if (unlikely(Size == 0))
+ return nullptr;
+ // Read one byte less than buffer size to have space for trailing 0.
+ --Size;
+
+ char * const InitialTo = To;
+
+ while (Size > 0) {
+ if (buffer.empty() == true)
+ {
+ buffer.reset();
+ unsigned long long actualread = 0;
+ if (filefd->Read(buffer.getend(), buffer.free(), &actualread) == false)
+ return nullptr;
+ buffer.bufferend = actualread;
+ if (buffer.size() == 0)
+ {
+ if (To == InitialTo)
+ return nullptr;
+ break;
+ }
+ filefd->Flags &= ~FileFd::HitEof;
+ }
+
+ unsigned long long const OutputSize = std::min(Size, buffer.size());
+ char const * const newline = static_cast<char const *>(memchr(buffer.get(), '\n', OutputSize));
+ // Read until end of line or up to Size bytes from the buffer.
+ unsigned long long actualread = buffer.read(To,
+ (newline != nullptr)
+ ? (newline - buffer.get()) + 1
+ : OutputSize);
+ To += actualread;
+ Size -= actualread;
+ if (newline != nullptr)
+ break;
+ }
+ *To = '\0';
+ return InitialTo;
+ }
+ virtual bool InternalFlush()
+ {
+ return true;
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
+ virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
+ virtual bool InternalSeek(unsigned long long const To)
+ {
+ // Our poor man seeking is costly, so try to avoid it
+ unsigned long long const iseekpos = filefd->Tell();
+ if (iseekpos == To)
+ return true;
+ else if (iseekpos < To)
+ return filefd->Skip(To - iseekpos);
+
+ if ((openmode & FileFd::ReadOnly) != FileFd::ReadOnly)
+ return filefd->FileFdError("Reopen is only implemented for read-only files!");
+ InternalClose(filefd->FileName);
+ if (filefd->iFd != -1)
+ close(filefd->iFd);
+ filefd->iFd = -1;
+ if (filefd->TemporaryFileName.empty() == false)
+ filefd->iFd = open(filefd->TemporaryFileName.c_str(), O_RDONLY);
+ else if (filefd->FileName.empty() == false)
+ filefd->iFd = open(filefd->FileName.c_str(), O_RDONLY);
+ else
+ {
+ if (compressed_fd > 0)
+ if (lseek(compressed_fd, 0, SEEK_SET) != 0)
+ filefd->iFd = compressed_fd;
+ if (filefd->iFd < 0)
+ return filefd->FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
+ }
+
+ if (filefd->OpenInternDescriptor(openmode, compressor) == false)
+ return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
+
+ buffer.reset();
+ set_seekpos(0);
+ if (To != 0)
+ return filefd->Skip(To);
+
+ seekpos = To;
+ return true;
+ }
+ virtual bool InternalSkip(unsigned long long Over)
+ {
+ unsigned long long constexpr buffersize = 1024;
+ char buffer[buffersize];
+ while (Over != 0)
+ {
+ unsigned long long toread = std::min(buffersize, Over);
+ if (filefd->Read(buffer, toread) == false)
+ return filefd->FileFdError("Unable to seek ahead %llu",Over);
+ Over -= toread;
+ }
+ return true;
+ }
+ virtual bool InternalTruncate(unsigned long long const)
+ {
+ return filefd->FileFdError("Truncating compressed files is not implemented (%s)", filefd->FileName.c_str());
+ }
+ virtual unsigned long long InternalTell()
+ {
+ // In theory, we could just return seekpos here always instead of
+ // seeking around, but not all users of FileFd use always Seek() and co
+ // so d->seekpos isn't always true and we can just use it as a hint if
+ // we have nothing else, but not always as an authority…
+ return seekpos - buffer.size();
+ }
+ virtual unsigned long long InternalSize()
+ {
+ unsigned long long size = 0;
+ unsigned long long const oldSeek = filefd->Tell();
+ unsigned long long constexpr ignoresize = 1024;
+ char ignore[ignoresize];
+ unsigned long long read = 0;
+ do {
+ if (filefd->Read(ignore, ignoresize, &read) == false)
+ {
+ filefd->Seek(oldSeek);
+ return 0;
+ }
+ } while(read != 0);
+ size = filefd->Tell();
+ filefd->Seek(oldSeek);
+ return size;
+ }
+ virtual bool InternalClose(std::string const &FileName) = 0;
+ virtual bool InternalStream() const { return false; }
+ virtual bool InternalAlwaysAutoClose() const { return true; }
+
+ virtual ~FileFdPrivate() {}
+};
+ /*}}}*/
+class APT_HIDDEN BufferedWriteFileFdPrivate : public FileFdPrivate { /*{{{*/
+protected:
+ FileFdPrivate *wrapped;
+ simple_buffer writebuffer;
+
+public:
+
+ explicit BufferedWriteFileFdPrivate(FileFdPrivate *Priv) :
+ FileFdPrivate(Priv->filefd), wrapped(Priv) {};
+
+ virtual APT::Configuration::Compressor get_compressor() const APT_OVERRIDE
+ {
+ return wrapped->get_compressor();
+ }
+ virtual void set_compressor(APT::Configuration::Compressor const &compressor) APT_OVERRIDE
+ {
+ return wrapped->set_compressor(compressor);
+ }
+ virtual unsigned int get_openmode() const APT_OVERRIDE
+ {
+ return wrapped->get_openmode();
+ }
+ virtual void set_openmode(unsigned int openmode) APT_OVERRIDE
+ {
+ return wrapped->set_openmode(openmode);
+ }
+ virtual bool get_is_pipe() const APT_OVERRIDE
+ {
+ return wrapped->get_is_pipe();
+ }
+ virtual void set_is_pipe(bool is_pipe) APT_OVERRIDE
+ {
+ FileFdPrivate::set_is_pipe(is_pipe);
+ wrapped->set_is_pipe(is_pipe);
+ }
+ virtual unsigned long long get_seekpos() const APT_OVERRIDE
+ {
+ return wrapped->get_seekpos();
+ }
+ virtual void set_seekpos(unsigned long long seekpos) APT_OVERRIDE
+ {
+ return wrapped->set_seekpos(seekpos);
+ }
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalOpen(iFd, Mode);
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalUnbufferedRead(To, Size);
+
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ return wrapped->InternalReadError();
+ }
+ virtual char * InternalReadLine(char * To, unsigned long long Size) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return nullptr;
+ return wrapped->InternalReadLine(To, Size);
+ }
+ virtual bool InternalFlush() APT_OVERRIDE
+ {
+ while (writebuffer.empty() == false) {
+ auto written = wrapped->InternalWrite(writebuffer.get(),
+ writebuffer.size());
+ // Ignore interrupted syscalls
+ if (written < 0 && errno == EINTR)
+ continue;
+ if (written < 0)
+ return wrapped->InternalWriteError();
+
+ writebuffer.bufferstart += written;
+ }
+ writebuffer.reset();
+ return wrapped->InternalFlush();
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ // Optimisation: If the buffer is empty and we have more to write than
+ // would fit in the buffer (or equal number of bytes), write directly.
+ if (writebuffer.empty() == true && Size >= writebuffer.free())
+ return wrapped->InternalWrite(From, Size);
+
+ // Write as much into the buffer as possible and then flush if needed
+ auto written = writebuffer.write(From, Size);
+
+ if (writebuffer.full() && InternalFlush() == false)
+ return -1;
+
+ return written;
+ }
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ return wrapped->InternalWriteError();
+ }
+ virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalSeek(To);
+ }
+ virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalSkip(Over);
+ }
+ virtual bool InternalTruncate(unsigned long long const Size) APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return false;
+ return wrapped->InternalTruncate(Size);
+ }
+ virtual unsigned long long InternalTell() APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalTell();
+ }
+ virtual unsigned long long InternalSize() APT_OVERRIDE
+ {
+ if (InternalFlush() == false)
+ return -1;
+ return wrapped->InternalSize();
+ }
+ virtual bool InternalClose(std::string const &FileName) APT_OVERRIDE
+ {
+ return wrapped->InternalClose(FileName);
+ }
+ virtual bool InternalAlwaysAutoClose() const APT_OVERRIDE
+ {
+ return wrapped->InternalAlwaysAutoClose();
+ }
+ virtual ~BufferedWriteFileFdPrivate()
+ {
+ delete wrapped;
+ }
+};
+ /*}}}*/
+class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
+#ifdef HAVE_ZLIB
+public:
+ gzFile gz;
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ gz = gzdopen(iFd, "r+");
+ else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ gz = gzdopen(iFd, "w");
+ else
+ gz = gzdopen(iFd, "r");
+ filefd->Flags |= FileFd::Compressed;
+ return gz != nullptr;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ return gzread(gz, To, Size);
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ int err;
+ char const * const errmsg = gzerror(gz, &err);
+ if (err != Z_ERRNO)
+ return filefd->FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
+ return FileFdPrivate::InternalReadError();
+ }
+ virtual char * InternalReadLine(char * To, unsigned long long Size) APT_OVERRIDE
+ {
+ return gzgets(gz, To, Size);
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ return gzwrite(gz,From,Size);
+ }
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ int err;
+ char const * const errmsg = gzerror(gz, &err);
+ if (err != Z_ERRNO)
+ return filefd->FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
+ return FileFdPrivate::InternalWriteError();
+ }
+ virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
+ {
+ off_t const res = gzseek(gz, To, SEEK_SET);
+ if (res != (off_t)To)
+ return filefd->FileFdError("Unable to seek to %llu", To);
+ seekpos = To;
+ buffer.reset();
+ return true;
+ }
+ virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
+ {
+ if (Over >= buffer.size())
+ {
+ Over -= buffer.size();
+ buffer.reset();
+ }
+ else
+ {
+ buffer.bufferstart += Over;
+ return true;
+ }
+ if (Over == 0)
+ return true;
+ off_t const res = gzseek(gz, Over, SEEK_CUR);
+ if (res < 0)
+ return filefd->FileFdError("Unable to seek ahead %llu",Over);
+ seekpos = res;
+ return true;
+ }
+ virtual unsigned long long InternalTell() APT_OVERRIDE
+ {
+ return gztell(gz) - buffer.size();
+ }
+ virtual unsigned long long InternalSize() APT_OVERRIDE
+ {
+ unsigned long long filesize = FileFdPrivate::InternalSize();
+ // only check gzsize if we are actually a gzip file, just checking for
+ // "gz" is not sufficient as uncompressed files could be opened with
+ // gzopen in "direct" mode as well
+ if (filesize == 0 || gzdirect(gz))
+ return filesize;
+
+ off_t const oldPos = lseek(filefd->iFd, 0, SEEK_CUR);
+ /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
+ * this ourselves; the original (uncompressed) file size is the last 32
+ * bits of the file */
+ // FIXME: Size for gz-files is limited by 32bit… no largefile support
+ if (lseek(filefd->iFd, -4, SEEK_END) < 0)
+ {
+ filefd->FileFdErrno("lseek","Unable to seek to end of gzipped file");
+ return 0;
+ }
+ uint32_t size = 0;
+ if (read(filefd->iFd, &size, 4) != 4)
+ {
+ filefd->FileFdErrno("read","Unable to read original size of gzipped file");
+ return 0;
+ }
+ size = le32toh(size);
+
+ if (lseek(filefd->iFd, oldPos, SEEK_SET) < 0)
+ {
+ filefd->FileFdErrno("lseek","Unable to seek in gzipped file");
+ return 0;
+ }
+ return size;
+ }
+ virtual bool InternalClose(std::string const &FileName) APT_OVERRIDE
+ {
+ if (gz == nullptr)
+ return true;
+ int const e = gzclose(gz);
+ gz = nullptr;
+ // gzdclose() on empty files always fails with "buffer error" here, ignore that
+ if (e != 0 && e != Z_BUF_ERROR)
+ return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
+ return true;
+ }
+
+ explicit GzipFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), gz(nullptr) {}
+ virtual ~GzipFileFdPrivate() { InternalClose(""); }
+#endif
+};
+ /*}}}*/
+class APT_HIDDEN Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
+#ifdef HAVE_BZ2
+ BZFILE* bz2;
+public:
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ bz2 = BZ2_bzdopen(iFd, "r+");
+ else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ bz2 = BZ2_bzdopen(iFd, "w");
+ else
+ bz2 = BZ2_bzdopen(iFd, "r");
+ filefd->Flags |= FileFd::Compressed;
+ return bz2 != nullptr;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ return BZ2_bzread(bz2, To, Size);
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ int err;
+ char const * const errmsg = BZ2_bzerror(bz2, &err);
+ if (err != BZ_IO_ERROR)
+ return filefd->FileFdError("BZ2_bzread: %s %s (%d: %s)", filefd->FileName.c_str(), _("Read error"), err, errmsg);
+ return FileFdPrivate::InternalReadError();
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ return BZ2_bzwrite(bz2, (void*)From, Size);
+ }
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ int err;
+ char const * const errmsg = BZ2_bzerror(bz2, &err);
+ if (err != BZ_IO_ERROR)
+ return filefd->FileFdError("BZ2_bzwrite: %s %s (%d: %s)", filefd->FileName.c_str(), _("Write error"), err, errmsg);
+ return FileFdPrivate::InternalWriteError();
+ }
+ virtual bool InternalStream() const APT_OVERRIDE { return true; }
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE
+ {
+ if (bz2 == nullptr)
+ return true;
+ BZ2_bzclose(bz2);
+ bz2 = nullptr;
+ return true;
+ }
+
+ explicit Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
+ virtual ~Bz2FileFdPrivate() { InternalClose(""); }
+#endif
+};
+ /*}}}*/
+class APT_HIDDEN Lz4FileFdPrivate: public FileFdPrivate { /*{{{*/
+ static constexpr unsigned long long LZ4_HEADER_SIZE = 19;
+ static constexpr unsigned long long LZ4_FOOTER_SIZE = 4;
+#ifdef HAVE_LZ4
+ LZ4F_decompressionContext_t dctx;
+ LZ4F_compressionContext_t cctx;
+ LZ4F_errorCode_t res;
+ FileFd backend;
+ simple_buffer lz4_buffer;
+ // Count of bytes that the decompressor expects to read next, or buffer size.
+ size_t next_to_load = APT_BUFFER_SIZE;
+public:
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ return _error->Error("lz4 only supports write or read mode");
+
+ if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly) {
+ res = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
+ lz4_buffer.reset(LZ4F_compressBound(APT_BUFFER_SIZE, nullptr)
+ + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE);
+ } else {
+ res = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
+ lz4_buffer.reset(APT_BUFFER_SIZE);
+ }
+
+ filefd->Flags |= FileFd::Compressed;
+
+ if (LZ4F_isError(res))
+ return false;
+
+ unsigned int flags = (Mode & (FileFd::WriteOnly|FileFd::ReadOnly));
+ if (backend.OpenDescriptor(iFd, flags, FileFd::None, true) == false)
+ return false;
+
+ // Write the file header
+ if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ {
+ res = LZ4F_compressBegin(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
+ if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+ return false;
+ }
+
+ return true;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ /* Keep reading as long as the compressor still wants to read */
+ while (next_to_load) {
+ // Fill compressed buffer;
+ if (lz4_buffer.empty()) {
+ unsigned long long read;
+ /* Reset - if LZ4 decompressor wants to read more, allocate more */
+ lz4_buffer.reset(next_to_load);
+ if (backend.Read(lz4_buffer.getend(), lz4_buffer.free(), &read) == false)
+ return -1;
+ lz4_buffer.bufferend += read;
+
+ /* Expected EOF */
+ if (read == 0) {
+ res = -1;
+ return filefd->FileFdError("LZ4F: %s %s",
+ filefd->FileName.c_str(),
+ _("Unexpected end of file")), -1;
+ }
+ }
+ // Drain compressed buffer as far as possible.
+ size_t in = lz4_buffer.size();
+ size_t out = Size;
+
+ res = LZ4F_decompress(dctx, To, &out, lz4_buffer.get(), &in, nullptr);
+ if (LZ4F_isError(res))
+ return -1;
+
+ next_to_load = res;
+ lz4_buffer.bufferstart += in;
+
+ if (out != 0)
+ return out;
+ }
+
+ return 0;
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ char const * const errmsg = LZ4F_getErrorName(res);
+
+ return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Read error"), res, errmsg);
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ unsigned long long const towrite = std::min(APT_BUFFER_SIZE, Size);
+
+ res = LZ4F_compressUpdate(cctx,
+ lz4_buffer.buffer, lz4_buffer.buffersize_max,
+ From, towrite, nullptr);
+
+ if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+ return -1;
+
+ return towrite;
+ }
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ char const * const errmsg = LZ4F_getErrorName(res);
+
+ return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Write error"), res, errmsg);
+ }
+ virtual bool InternalStream() const APT_OVERRIDE { return true; }
+
+ virtual bool InternalFlush() APT_OVERRIDE
+ {
+ return backend.Flush();
+ }
+
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE
+ {
+ /* Reset variables */
+ res = 0;
+ next_to_load = APT_BUFFER_SIZE;
+
+ if (cctx != nullptr)
+ {
+ if (filefd->Failed() == false)
+ {
+ res = LZ4F_compressEnd(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
+ if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
+ return false;
+ if (!backend.Flush())
+ return false;
+ }
+ if (!backend.Close())
+ return false;
+
+ res = LZ4F_freeCompressionContext(cctx);
+ cctx = nullptr;
+ }
+
+ if (dctx != nullptr)
+ {
+ res = LZ4F_freeDecompressionContext(dctx);
+ dctx = nullptr;
+ }
+ if (backend.IsOpen())
+ {
+ backend.Close();
+ filefd->iFd = -1;
+ }
+
+ return LZ4F_isError(res) == false;
+ }
+
+ explicit Lz4FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), dctx(nullptr), cctx(nullptr) {}
+ virtual ~Lz4FileFdPrivate() {
+ InternalClose("");
+ }
+#endif
+};
+ /*}}}*/
+
+class APT_HIDDEN ZstdFileFdPrivate : public FileFdPrivate
+{ /*{{{*/
+#ifdef HAVE_ZSTD
+ ZSTD_DStream *dctx;
+ ZSTD_CStream *cctx;
+ size_t res;
+ FileFd backend;
+ simple_buffer zstd_buffer;
+ // Count of bytes that the decompressor expects to read next, or buffer size.
+ size_t next_to_load = APT_BUFFER_SIZE;
+
+ public:
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ return _error->Error("zstd only supports write or read mode");
+
+ if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ {
+ cctx = ZSTD_createCStream();
+ res = ZSTD_initCStream(cctx, findLevel(compressor.CompressArgs));
+ zstd_buffer.reset(APT_BUFFER_SIZE);
+ }
+ else
+ {
+ dctx = ZSTD_createDStream();
+ res = ZSTD_initDStream(dctx);
+ zstd_buffer.reset(APT_BUFFER_SIZE);
+ }
+
+ filefd->Flags |= FileFd::Compressed;
+
+ if (ZSTD_isError(res))
+ return false;
+
+ unsigned int flags = (Mode & (FileFd::WriteOnly | FileFd::ReadOnly));
+ if (backend.OpenDescriptor(iFd, flags, FileFd::None, true) == false)
+ return false;
+
+ return true;
+ }
+ virtual ssize_t InternalUnbufferedRead(void *const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ /* Keep reading as long as the compressor still wants to read */
+ while (true)
+ {
+ // Fill compressed buffer;
+ if (zstd_buffer.empty())
+ {
+ unsigned long long read;
+ /* Reset - if LZ4 decompressor wants to read more, allocate more */
+ zstd_buffer.reset(next_to_load);
+ if (backend.Read(zstd_buffer.getend(), zstd_buffer.free(), &read) == false)
+ return -1;
+ zstd_buffer.bufferend += read;
+
+ if (read == 0)
+ {
+ /* Expected EOF */
+ if (next_to_load == 0)
+ return 0;
+
+ res = -1;
+ return filefd->FileFdError("ZSTD: %s %s",
+ filefd->FileName.c_str(),
+ _("Unexpected end of file")),
+ -1;
+ }
+ }
+ // Drain compressed buffer as far as possible.
+ ZSTD_inBuffer in = {
+ .src = zstd_buffer.get(),
+ .size = zstd_buffer.size(),
+ .pos = 0,
+ };
+ ZSTD_outBuffer out = {
+ .dst = To,
+ .size = Size,
+ .pos = 0,
+ };
+
+ next_to_load = res = ZSTD_decompressStream(dctx, &out, &in);
+
+ if (res == 0)
+ {
+ res = ZSTD_initDStream(dctx);
+ }
+
+ if (ZSTD_isError(res))
+ return -1;
+
+ zstd_buffer.bufferstart += in.pos;
+
+ if (out.pos != 0)
+ return out.pos;
+ }
+
+ return 0;
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ char const *const errmsg = ZSTD_getErrorName(res);
+
+ return filefd->FileFdError("ZSTD: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Read error"), res, errmsg);
+ }
+ virtual ssize_t InternalWrite(void const *const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ // Drain compressed buffer as far as possible.
+ ZSTD_outBuffer out = {
+ .dst = zstd_buffer.buffer,
+ .size = zstd_buffer.buffersize_max,
+ .pos = 0,
+ };
+ ZSTD_inBuffer in = {
+ .src = From,
+ .size = Size,
+ .pos = 0,
+ };
+
+ res = ZSTD_compressStream(cctx, &out, &in);
+
+ if (ZSTD_isError(res) || backend.Write(zstd_buffer.buffer, out.pos) == false)
+ return -1;
+
+ return in.pos;
+ }
+
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ char const *const errmsg = ZSTD_getErrorName(res);
+
+ return filefd->FileFdError("ZSTD: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Write error"), res, errmsg);
+ }
+ virtual bool InternalStream() const APT_OVERRIDE { return true; }
+
+ virtual bool InternalFlush() APT_OVERRIDE
+ {
+ return backend.Flush();
+ }
+
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE
+ {
+ /* Reset variables */
+ res = 0;
+ next_to_load = APT_BUFFER_SIZE;
+
+ if (cctx != nullptr)
+ {
+ if (filefd->Failed() == false)
+ {
+ do
+ {
+ ZSTD_outBuffer out = {
+ .dst = zstd_buffer.buffer,
+ .size = zstd_buffer.buffersize_max,
+ .pos = 0,
+ };
+ res = ZSTD_endStream(cctx, &out);
+ if (ZSTD_isError(res) || backend.Write(zstd_buffer.buffer, out.pos) == false)
+ return false;
+ } while (res > 0);
+
+ if (!backend.Flush())
+ return false;
+ }
+ if (!backend.Close())
+ return false;
+
+ res = ZSTD_freeCStream(cctx);
+ cctx = nullptr;
+ }
+
+ if (dctx != nullptr)
+ {
+ res = ZSTD_freeDStream(dctx);
+ dctx = nullptr;
+ }
+ if (backend.IsOpen())
+ {
+ backend.Close();
+ filefd->iFd = -1;
+ }
+
+ return ZSTD_isError(res) == false;
+ }
+
+ static uint32_t findLevel(std::vector<std::string> const &Args)
+ {
+ for (auto a = Args.rbegin(); a != Args.rend(); ++a)
+ {
+ if (a->size() >= 2 && (*a)[0] == '-' && (*a)[1] != '-')
+ {
+ auto const level = a->substr(1);
+ auto const notANumber = level.find_first_not_of("0123456789");
+ if (notANumber != std::string::npos)
+ continue;
+
+ return (uint32_t)stoi(level);
+ }
+ }
+ return 19;
+ }
+
+ explicit ZstdFileFdPrivate(FileFd *const filefd) : FileFdPrivate(filefd), dctx(nullptr), cctx(nullptr) {}
+ virtual ~ZstdFileFdPrivate()
+ {
+ InternalClose("");
+ }
+#endif
+};
+ /*}}}*/
+class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
+#ifdef HAVE_LZMA
+ struct LZMAFILE {
+ FILE* file;
+ FileFd * const filefd;
+ uint8_t buffer[4096];
+ lzma_stream stream;
+ lzma_ret err;
+ bool eof;
+ bool compressing;
+
+ LZMAFILE(FileFd * const fd) : file(nullptr), filefd(fd), eof(false), compressing(false) { buffer[0] = '\0'; }
+ ~LZMAFILE()
+ {
+ if (compressing == true && filefd->Failed() == false)
+ {
+ size_t constexpr buffersize = sizeof(buffer)/sizeof(buffer[0]);
+ while(true)
+ {
+ stream.avail_out = buffersize;
+ stream.next_out = buffer;
+ err = lzma_code(&stream, LZMA_FINISH);
+ if (err != LZMA_OK && err != LZMA_STREAM_END)
+ {
+ _error->Error("~LZMAFILE: Compress finalisation failed");
+ break;
+ }
+ size_t const n = buffersize - stream.avail_out;
+ if (n && fwrite(buffer, 1, n, file) != n)
+ {
+ _error->Errno("~LZMAFILE",_("Write error"));
+ break;
+ }
+ if (err == LZMA_STREAM_END)
+ break;
+ }
+ }
+ lzma_end(&stream);
+ fclose(file);
+ }
+ };
+ LZMAFILE* lzma;
+ static uint32_t findXZlevel(std::vector<std::string> const &Args)
+ {
+ for (auto a = Args.rbegin(); a != Args.rend(); ++a)
+ if (a->empty() == false && (*a)[0] == '-' && (*a)[1] != '-')
+ {
+ auto const number = a->find_last_of("0123456789");
+ if (number == std::string::npos)
+ continue;
+ auto const extreme = a->find("e", number);
+ uint32_t level = (extreme != std::string::npos) ? LZMA_PRESET_EXTREME : 0;
+ switch ((*a)[number])
+ {
+ case '0': return level | 0;
+ case '1': return level | 1;
+ case '2': return level | 2;
+ case '3': return level | 3;
+ case '4': return level | 4;
+ case '5': return level | 5;
+ case '6': return level | 6;
+ case '7': return level | 7;
+ case '8': return level | 8;
+ case '9': return level | 9;
+ }
+ }
+ return 6;
+ }
+public:
+ virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
+ {
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ return filefd->FileFdError("ReadWrite mode is not supported for lzma/xz files %s", filefd->FileName.c_str());
+
+ if (lzma == nullptr)
+ lzma = new LzmaFileFdPrivate::LZMAFILE(filefd);
+ if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ lzma->file = fdopen(iFd, "w");
+ else
+ lzma->file = fdopen(iFd, "r");
+ filefd->Flags |= FileFd::Compressed;
+ if (lzma->file == nullptr)
+ return false;
+
+ lzma_stream tmp_stream = LZMA_STREAM_INIT;
+ lzma->stream = tmp_stream;
+
+ if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
+ {
+ uint32_t const xzlevel = findXZlevel(compressor.CompressArgs);
+ if (compressor.Name == "xz")
+ {
+ if (lzma_easy_encoder(&lzma->stream, xzlevel, LZMA_CHECK_CRC64) != LZMA_OK)
+ return false;
+ }
+ else
+ {
+ lzma_options_lzma options;
+ lzma_lzma_preset(&options, xzlevel);
+ if (lzma_alone_encoder(&lzma->stream, &options) != LZMA_OK)
+ return false;
+ }
+ lzma->compressing = true;
+ }
+ else
+ {
+ uint64_t const memlimit = UINT64_MAX;
+ if (compressor.Name == "xz")
+ {
+ if (lzma_auto_decoder(&lzma->stream, memlimit, 0) != LZMA_OK)
+ return false;
+ }
+ else
+ {
+ if (lzma_alone_decoder(&lzma->stream, memlimit) != LZMA_OK)
+ return false;
+ }
+ lzma->compressing = false;
+ }
+ return true;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ ssize_t Res;
+ if (lzma->eof == true)
+ return 0;
+
+ lzma->stream.next_out = (uint8_t *) To;
+ lzma->stream.avail_out = Size;
+ if (lzma->stream.avail_in == 0)
+ {
+ lzma->stream.next_in = lzma->buffer;
+ lzma->stream.avail_in = fread(lzma->buffer, 1, sizeof(lzma->buffer)/sizeof(lzma->buffer[0]), lzma->file);
+ }
+ lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
+ if (lzma->err == LZMA_STREAM_END)
+ {
+ lzma->eof = true;
+ Res = Size - lzma->stream.avail_out;
+ }
+ else if (lzma->err != LZMA_OK)
+ {
+ Res = -1;
+ errno = 0;
+ }
+ else
+ {
+ Res = Size - lzma->stream.avail_out;
+ if (Res == 0)
+ {
+ // lzma run was okay, but produced no output…
+ Res = -1;
+ errno = EINTR;
+ }
+ }
+ return Res;
+ }
+ virtual bool InternalReadError() APT_OVERRIDE
+ {
+ return filefd->FileFdError("lzma_read: %s (%d)", _("Read error"), lzma->err);
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ ssize_t Res;
+ lzma->stream.next_in = (uint8_t *)From;
+ lzma->stream.avail_in = Size;
+ lzma->stream.next_out = lzma->buffer;
+ lzma->stream.avail_out = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]);
+ lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
+ if (lzma->err != LZMA_OK)
+ return -1;
+ size_t const n = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]) - lzma->stream.avail_out;
+ size_t const m = (n == 0) ? 0 : fwrite(lzma->buffer, 1, n, lzma->file);
+ if (m != n)
+ {
+ Res = -1;
+ errno = 0;
+ }
+ else
+ {
+ Res = Size - lzma->stream.avail_in;
+ if (Res == 0)
+ {
+ // lzma run was okay, but produced no output…
+ Res = -1;
+ errno = EINTR;
+ }
+ }
+ return Res;
+ }
+ virtual bool InternalWriteError() APT_OVERRIDE
+ {
+ return filefd->FileFdError("lzma_write: %s (%d)", _("Write error"), lzma->err);
+ }
+ virtual bool InternalStream() const APT_OVERRIDE { return true; }
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE
+ {
+ delete lzma;
+ lzma = nullptr;
+ return true;
+ }
+
+ explicit LzmaFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), lzma(nullptr) {}
+ virtual ~LzmaFileFdPrivate() { InternalClose(""); }
+#endif
+};
+ /*}}}*/
+class APT_HIDDEN PipedFileFdPrivate: public FileFdPrivate /*{{{*/
+/* if we don't have a specific class dealing with library calls, we (un)compress
+ by executing a specified binary and pipe in/out what we need */
+{
+public:
+ virtual bool InternalOpen(int const, unsigned int const Mode) APT_OVERRIDE
+ {
+ // collect zombies here in case we reopen
+ if (compressor_pid > 0)
+ ExecWait(compressor_pid, "FileFdCompressor", true);
+
+ if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
+ return filefd->FileFdError("ReadWrite mode is not supported for file %s", filefd->FileName.c_str());
+ if (compressor.Binary == "false")
+ return filefd->FileFdError("libapt has inbuilt support for the %s compression,"
+ " but was forced to ignore it in favor of an external binary – which isn't installed.", compressor.Name.c_str());
+
+ bool const Comp = (Mode & FileFd::WriteOnly) == FileFd::WriteOnly;
+ if (Comp == false && filefd->iFd != -1)
+ {
+ // Handle 'decompression' of empty files
+ struct stat Buf;
+ if (fstat(filefd->iFd, &Buf) != 0)
+ return filefd->FileFdErrno("fstat", "Could not stat fd %d for file %s", filefd->iFd, filefd->FileName.c_str());
+ if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
+ return true;
+
+ // We don't need the file open - instead let the compressor open it
+ // as he properly knows better how to efficiently read from 'his' file
+ if (filefd->FileName.empty() == false)
+ {
+ close(filefd->iFd);
+ filefd->iFd = -1;
+ }
+ }
+
+ // Create a data pipe
+ int Pipe[2] = {-1,-1};
+ if (pipe(Pipe) != 0)
+ return filefd->FileFdErrno("pipe",_("Failed to create subprocess IPC"));
+ for (int J = 0; J != 2; J++)
+ SetCloseExec(Pipe[J],true);
+
+ compressed_fd = filefd->iFd;
+ set_is_pipe(true);
+
+ if (Comp == true)
+ filefd->iFd = Pipe[1];
+ else
+ filefd->iFd = Pipe[0];
+
+ // The child..
+ compressor_pid = ExecFork();
+ if (compressor_pid == 0)
+ {
+ if (Comp == true)
+ {
+ dup2(compressed_fd,STDOUT_FILENO);
+ dup2(Pipe[0],STDIN_FILENO);
+ }
+ else
+ {
+ if (compressed_fd != -1)
+ dup2(compressed_fd,STDIN_FILENO);
+ dup2(Pipe[1],STDOUT_FILENO);
+ }
+ int const nullfd = open("/dev/null", O_WRONLY);
+ if (nullfd != -1)
+ {
+ dup2(nullfd,STDERR_FILENO);
+ close(nullfd);
+ }
+
+ SetCloseExec(STDOUT_FILENO,false);
+ SetCloseExec(STDIN_FILENO,false);
+
+ std::vector<char const*> Args;
+ Args.push_back(compressor.Binary.c_str());
+ std::vector<std::string> const * const addArgs =
+ (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
+ for (std::vector<std::string>::const_iterator a = addArgs->begin();
+ a != addArgs->end(); ++a)
+ Args.push_back(a->c_str());
+ if (Comp == false && filefd->FileName.empty() == false)
+ {
+ // commands not needing arguments, do not need to be told about using standard output
+ // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
+ if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
+ Args.push_back("--stdout");
+ if (filefd->TemporaryFileName.empty() == false)
+ Args.push_back(filefd->TemporaryFileName.c_str());
+ else
+ Args.push_back(filefd->FileName.c_str());
+ }
+ Args.push_back(NULL);
+
+ execvp(Args[0],(char **)&Args[0]);
+ cerr << _("Failed to exec compressor ") << Args[0] << endl;
+ _exit(100);
+ }
+ if (Comp == true)
+ close(Pipe[0]);
+ else
+ close(Pipe[1]);
+
+ return true;
+ }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ return read(filefd->iFd, To, Size);
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ return write(filefd->iFd, From, Size);
+ }
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE
+ {
+ bool Ret = true;
+ if (filefd->iFd != -1)
+ {
+ close(filefd->iFd);
+ filefd->iFd = -1;
+ }
+ if (compressor_pid > 0)
+ Ret &= ExecWait(compressor_pid, "FileFdCompressor", true);
+ compressor_pid = -1;
+ return Ret;
+ }
+ explicit PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
+ virtual ~PipedFileFdPrivate() { InternalClose(""); }
+};
+ /*}}}*/
+class APT_HIDDEN DirectFileFdPrivate: public FileFdPrivate /*{{{*/
+{
+public:
+ virtual bool InternalOpen(int const, unsigned int const) APT_OVERRIDE { return true; }
+ virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
+ {
+ return read(filefd->iFd, To, Size);
+ }
+ virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
+ {
+ // files opened read+write are strange and only really "supported" for direct files
+ if (buffer.size() != 0)
+ {
+ lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
+ buffer.reset();
+ }
+ return write(filefd->iFd, From, Size);
+ }
+ virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
+ {
+ off_t const res = lseek(filefd->iFd, To, SEEK_SET);
+ if (res != (off_t)To)
+ return filefd->FileFdError("Unable to seek to %llu", To);
+ seekpos = To;
+ buffer.reset();
+ return true;
+ }
+ virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
+ {
+ if (Over >= buffer.size())
+ {
+ Over -= buffer.size();
+ buffer.reset();
+ }
+ else
+ {
+ buffer.bufferstart += Over;
+ return true;
+ }
+ if (Over == 0)
+ return true;
+ off_t const res = lseek(filefd->iFd, Over, SEEK_CUR);
+ if (res < 0)
+ return filefd->FileFdError("Unable to seek ahead %llu",Over);
+ seekpos = res;
+ return true;
+ }
+ virtual bool InternalTruncate(unsigned long long const To) APT_OVERRIDE
+ {
+ if (buffer.size() != 0)
+ {
+ unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
+ if ((seekpos - buffer.size()) >= To)
+ buffer.reset();
+ else if (seekpos >= To)
+ buffer.bufferend = (To - seekpos) + buffer.bufferstart;
+ else
+ buffer.reset();
+ }
+ if (ftruncate(filefd->iFd, To) != 0)
+ return filefd->FileFdError("Unable to truncate to %llu",To);
+ return true;
+ }
+ virtual unsigned long long InternalTell() APT_OVERRIDE
+ {
+ return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
+ }
+ virtual unsigned long long InternalSize() APT_OVERRIDE
+ {
+ return filefd->FileSize();
+ }
+ virtual bool InternalClose(std::string const &) APT_OVERRIDE { return true; }
+ virtual bool InternalAlwaysAutoClose() const APT_OVERRIDE { return false; }
+
+ explicit DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
+ virtual ~DirectFileFdPrivate() { InternalClose(""); }
+};
+ /*}}}*/
+// FileFd Constructors /*{{{*/
+FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
+{
+ Open(FileName,Mode, None, AccessMode);
+}
+FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
+{
+ Open(FileName,Mode, Compress, AccessMode);
+}
+FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
+FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
+{
+ OpenDescriptor(Fd, Mode, Compress);
+}
+FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
+{
+ OpenDescriptor(Fd, ReadWrite, None, AutoClose);
+}
+ /*}}}*/
+// FileFd::Open - Open a file /*{{{*/
+// ---------------------------------------------------------------------
+/* The most commonly used open mode combinations are given with Mode */
+bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
+{
+ if (Mode == ReadOnlyGzip)
+ return Open(FileName, ReadOnly, Gzip, AccessMode);
+
+ if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
+ 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();
+ if (Compress == Auto)
+ {
+ for (; compressor != compressors.end(); ++compressor)
+ {
+ std::string file = FileName + compressor->Extension;
+ if (FileExists(file) == false)
+ continue;
+ FileName = file;
+ break;
+ }
+ }
+ else if (Compress == Extension)
+ {
+ std::string::size_type const found = FileName.find_last_of('.');
+ std::string ext;
+ if (found != std::string::npos)
+ {
+ ext = FileName.substr(found);
+ if (ext == ".new" || ext == ".bak")
+ {
+ std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
+ if (found2 != std::string::npos)
+ ext = FileName.substr(found2, found - found2);
+ else
+ ext.clear();
+ }
+ }
+ for (; compressor != compressors.end(); ++compressor)
+ if (ext == compressor->Extension)
+ break;
+ // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
+ if (compressor == compressors.end())
+ for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
+ if (compressor->Name == ".")
+ break;
+ }
+ else
+ {
+ std::string name;
+ switch (Compress)
+ {
+ case None: name = "."; break;
+ case Gzip: name = "gzip"; break;
+ case Bzip2: name = "bzip2"; break;
+ case Lzma: name = "lzma"; break;
+ case Xz: name = "xz"; break;
+ case Lz4: name = "lz4"; break;
+ case Zstd: name = "zstd"; break;
+ case Auto:
+ case Extension:
+ // Unreachable
+ 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 FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
+ }
+
+ if (compressor == compressors.end())
+ return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
+ return Open(FileName, Mode, *compressor, AccessMode);
+}
+bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
+{
+ Close();
+ Flags = AutoClose;
+
+ if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
+ return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
+ if ((Mode & ReadWrite) == 0)
+ return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
+
+ unsigned int OpenMode = Mode;
+ if (FileName == "/dev/null")
+ OpenMode = OpenMode & ~(Atomic | Exclusive | Create | Empty);
+
+ if ((OpenMode & Atomic) == Atomic)
+ {
+ Flags |= Replace;
+ }
+ else if ((OpenMode & (Exclusive | Create)) == (Exclusive | Create))
+ {
+ // for atomic, this will be done by rename in Close()
+ RemoveFile("FileFd::Open", FileName);
+ }
+ if ((OpenMode & Empty) == Empty)
+ {
+ struct stat Buf;
+ if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
+ RemoveFile("FileFd::Open", FileName);
+ }
+
+ int fileflags = 0;
+ #define if_FLAGGED_SET(FLAG, MODE) if ((OpenMode & FLAG) == FLAG) fileflags |= MODE
+ if_FLAGGED_SET(ReadWrite, O_RDWR);
+ else if_FLAGGED_SET(ReadOnly, O_RDONLY);
+ else if_FLAGGED_SET(WriteOnly, O_WRONLY);
+
+ if_FLAGGED_SET(Create, O_CREAT);
+ if_FLAGGED_SET(Empty, O_TRUNC);
+ if_FLAGGED_SET(Exclusive, O_EXCL);
+ #undef if_FLAGGED_SET
+
+ if ((OpenMode & Atomic) == Atomic)
+ {
+ char *name = strdup((FileName + ".XXXXXX").c_str());
+
+ if((iFd = mkstemp(name)) == -1)
+ {
+ free(name);
+ return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
+ }
+
+ TemporaryFileName = string(name);
+ free(name);
+
+ // umask() will always set the umask and return the previous value, so
+ // we first set the umask and then reset it to the old value
+ mode_t const CurrentUmask = umask(0);
+ umask(CurrentUmask);
+ // calculate the actual file permissions (just like open/creat)
+ mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
+
+ if(fchmod(iFd, FilePermissions) == -1)
+ return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
+ }
+ else
+ iFd = open(FileName.c_str(), fileflags, AccessMode);
+
+ this->FileName = FileName;
+ if (iFd == -1 || OpenInternDescriptor(OpenMode, compressor) == false)
+ {
+ if (iFd != -1)
+ {
+ close (iFd);
+ iFd = -1;
+ }
+ return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
+ }
+
+ SetCloseExec(iFd,true);
+ return true;
+}
+ /*}}}*/
+// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
+bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
+{
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ std::string name;
+
+ // compat with the old API
+ if (Mode == ReadOnlyGzip && Compress == None)
+ Compress = Gzip;
+
+ switch (Compress)
+ {
+ case None: name = "."; break;
+ case Gzip: name = "gzip"; break;
+ case Bzip2: name = "bzip2"; break;
+ case Lzma: name = "lzma"; break;
+ case Xz: name = "xz"; break;
+ case Lz4: name = "lz4"; break;
+ case Zstd: name = "zstd"; break;
+ case Auto:
+ case Extension:
+ 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())
+ {
+ 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)
+{
+ Close();
+ Flags = (AutoClose) ? FileFd::AutoClose : 0;
+ iFd = Fd;
+ this->FileName = "";
+ if (OpenInternDescriptor(Mode, compressor) == false)
+ {
+ if (iFd != -1 && (
+ (Flags & Compressed) == Compressed ||
+ AutoClose == true))
+ {
+ close (iFd);
+ iFd = -1;
+ }
+ return FileFdError(_("Could not open file descriptor %d"), Fd);
+ }
+ return true;
+}
+bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
+{
+ if (iFd == -1)
+ return false;
+
+ if (d != nullptr)
+ d->InternalClose(FileName);
+
+ if (d == nullptr)
+ {
+ if (false)
+ /* dummy so that the rest can be 'else if's */;
+#define APT_COMPRESS_INIT(NAME, CONSTRUCTOR) \
+ else if (compressor.Name == NAME) \
+ d = new CONSTRUCTOR(this)
+#ifdef HAVE_ZLIB
+ APT_COMPRESS_INIT("gzip", GzipFileFdPrivate);
+#endif
+#ifdef HAVE_BZ2
+ APT_COMPRESS_INIT("bzip2", Bz2FileFdPrivate);
+#endif
+#ifdef HAVE_LZMA
+ APT_COMPRESS_INIT("xz", LzmaFileFdPrivate);
+ APT_COMPRESS_INIT("lzma", LzmaFileFdPrivate);
+#endif
+#ifdef HAVE_LZ4
+ APT_COMPRESS_INIT("lz4", Lz4FileFdPrivate);
+#endif
+#ifdef HAVE_ZSTD
+ APT_COMPRESS_INIT("zstd", ZstdFileFdPrivate);
+#endif
+#undef APT_COMPRESS_INIT
+ else if (compressor.Name == "." || compressor.Binary.empty() == true)
+ d = new DirectFileFdPrivate(this);
+ else
+ d = new PipedFileFdPrivate(this);
+
+ if (Mode & BufferedWrite)
+ d = new BufferedWriteFileFdPrivate(d);
+
+ d->set_openmode(Mode);
+ d->set_compressor(compressor);
+ if ((Flags & AutoClose) != AutoClose && d->InternalAlwaysAutoClose())
+ {
+ // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
+ int const internFd = dup(iFd);
+ if (internFd == -1)
+ return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
+ iFd = internFd;
+ }
+ }
+ return d->InternalOpen(iFd, Mode);
+}
+ /*}}}*/
+// FileFd::~File - Closes the file /*{{{*/
+// ---------------------------------------------------------------------
+/* If the proper modes are selected then we close the Fd and possibly
+ unlink the file on error. */
+FileFd::~FileFd()
+{
+ Close();
+ if (d != NULL)
+ d->InternalClose(FileName);
+ delete d;
+ d = NULL;
+}
+ /*}}}*/
+// FileFd::Read - Read a bit of the file /*{{{*/
+// ---------------------------------------------------------------------
+/* We are careful to handle interruption by a signal while reading
+ gracefully. */
+bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
+{
+ if (d == nullptr || Failed())
+ return false;
+ ssize_t Res = 1;
+ errno = 0;
+ if (Actual != 0)
+ *Actual = 0;
+ *((char *)To) = '\0';
+ while (Res > 0 && Size > 0)
+ {
+ Res = d->InternalRead(To, Size);
+
+ if (Res < 0)
+ {
+ if (errno == EINTR)
+ {
+ // trick the while-loop into running again
+ Res = 1;
+ errno = 0;
+ continue;
+ }
+ return d->InternalReadError();
+ }
+
+ To = (char *)To + Res;
+ Size -= Res;
+ if (d != NULL)
+ d->set_seekpos(d->get_seekpos() + Res);
+ if (Actual != 0)
+ *Actual += Res;
+ }
+
+ if (Size == 0)
+ return true;
+
+ // Eof handling
+ if (Actual != 0)
+ {
+ Flags |= HitEof;
+ return true;
+ }
+
+ return FileFdError(_("read, still have %llu to read but none left"), Size);
+}
+bool FileFd::Read(int const Fd, void *To, unsigned long long Size, unsigned long long * const Actual)
+{
+ ssize_t Res = 1;
+ errno = 0;
+ if (Actual != nullptr)
+ *Actual = 0;
+ *static_cast<char *>(To) = '\0';
+ while (Res > 0 && Size > 0)
+ {
+ Res = read(Fd, To, Size);
+ if (Res < 0)
+ {
+ if (errno == EINTR)
+ {
+ Res = 1;
+ errno = 0;
+ continue;
+ }
+ return _error->Errno("read", _("Read error"));
+ }
+ To = static_cast<char *>(To) + Res;
+ Size -= Res;
+ if (Actual != 0)
+ *Actual += Res;
+ }
+ if (Size == 0)
+ return true;
+ if (Actual != nullptr)
+ return true;
+ return _error->Error(_("read, still have %llu to read but none left"), Size);
+}
+ /*}}}*/
+// FileFd::ReadLine - Read a complete line from the file /*{{{*/
+char* FileFd::ReadLine(char *To, unsigned long long const Size)
+{
+ *To = '\0';
+ if (d == nullptr || Failed())
+ return nullptr;
+ return d->InternalReadLine(To, Size);
+}
+bool FileFd::ReadLine(std::string &To)
+{
+ To.clear();
+ if (d == nullptr || Failed())
+ return false;
+ constexpr size_t buflen = 4096;
+ char buffer[buflen];
+ size_t len;
+ do
+ {
+ if (d->InternalReadLine(buffer, buflen) == nullptr)
+ return false;
+ len = strlen(buffer);
+ To.append(buffer, len);
+ } while (len == buflen - 1 && buffer[len - 2] != '\n');
+ // remove the newline at the end
+ auto const i = To.find_last_not_of("\r\n");
+ if (i == std::string::npos)
+ To.clear();
+ else
+ To.erase(i + 1);
+ return true;
+}
+ /*}}}*/
+// FileFd::Flush - Flush the file /*{{{*/
+bool FileFd::Flush()
+{
+ if (Failed())
+ return false;
+ if (d == nullptr)
+ return true;
+
+ return d->InternalFlush();
+}
+ /*}}}*/
+// FileFd::Write - Write to the file /*{{{*/
+bool FileFd::Write(const void *From,unsigned long long Size)
+{
+ if (d == nullptr || Failed())
+ return false;
+ ssize_t Res = 1;
+ errno = 0;
+ while (Res > 0 && Size > 0)
+ {
+ Res = d->InternalWrite(From, Size);
+
+ if (Res < 0)
+ {
+ if (errno == EINTR)
+ {
+ // trick the while-loop into running again
+ Res = 1;
+ errno = 0;
+ continue;
+ }
+ return d->InternalWriteError();
+ }
+
+ From = (char const *)From + Res;
+ Size -= Res;
+ if (d != NULL)
+ d->set_seekpos(d->get_seekpos() + Res);
+ }
+
+ if (Size == 0)
+ return true;
+
+ return FileFdError(_("write, still have %llu to write but couldn't"), Size);
+}
+bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
+{
+ ssize_t Res = 1;
+ errno = 0;
+ while (Res > 0 && Size > 0)
+ {
+ Res = write(Fd,From,Size);
+ if (Res < 0 && errno == EINTR)
+ continue;
+ if (Res < 0)
+ return _error->Errno("write",_("Write error"));
+
+ From = (char const *)From + Res;
+ Size -= Res;
+ }
+
+ if (Size == 0)
+ return true;
+
+ return _error->Error(_("write, still have %llu to write but couldn't"), Size);
+}
+ /*}}}*/
+// FileFd::Seek - Seek in the file /*{{{*/
+bool FileFd::Seek(unsigned long long To)
+{
+ if (d == nullptr || Failed())
+ return false;
+ Flags &= ~HitEof;
+ return d->InternalSeek(To);
+}
+ /*}}}*/
+// FileFd::Skip - Skip over data in the file /*{{{*/
+bool FileFd::Skip(unsigned long long Over)
+{
+ if (d == nullptr || Failed())
+ return false;
+ return d->InternalSkip(Over);
+}
+ /*}}}*/
+// FileFd::Truncate - Truncate the file /*{{{*/
+bool FileFd::Truncate(unsigned long long To)
+{
+ if (d == nullptr || Failed())
+ return false;
+ // truncating /dev/null is always successful - as we get an error otherwise
+ if (To == 0 && FileName == "/dev/null")
+ return true;
+ return d->InternalTruncate(To);
+}
+ /*}}}*/
+// FileFd::Tell - Current seek position /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+unsigned long long FileFd::Tell()
+{
+ if (d == nullptr || Failed())
+ return false;
+ off_t const Res = d->InternalTell();
+ if (Res == (off_t)-1)
+ FileFdErrno("lseek","Failed to determine the current file position");
+ d->set_seekpos(Res);
+ return Res;
+}
+ /*}}}*/
+static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
+{
+ bool ispipe = (d != NULL && d->get_is_pipe() == true);
+ if (ispipe == false)
+ {
+ if (fstat(iFd,&Buf) != 0)
+ // higher-level code will generate more meaningful messages,
+ // even translated this would be meaningless for users
+ return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
+ if (FileName.empty() == false)
+ ispipe = S_ISFIFO(Buf.st_mode);
+ }
+
+ // for compressor pipes st_size is undefined and at 'best' zero
+ if (ispipe == true)
+ {
+ // we set it here, too, as we get the info here for free
+ // in theory the Open-methods should take care of it already
+ if (d != NULL)
+ d->set_is_pipe(true);
+ if (stat(FileName.c_str(), &Buf) != 0)
+ return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
+ }
+ return true;
+}
+ /*}}}*/
+// FileFd::FileSize - Return the size of the file /*{{{*/
+unsigned long long FileFd::FileSize()
+{
+ struct stat Buf;
+ if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
+ {
+ Flags |= Fail;
+ return 0;
+ }
+ return Buf.st_size;
+}
+ /*}}}*/
+// FileFd::ModificationTime - Return the time of last touch /*{{{*/
+time_t FileFd::ModificationTime()
+{
+ struct stat Buf;
+ if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
+ {
+ Flags |= Fail;
+ return 0;
+ }
+ return Buf.st_mtime;
+}
+ /*}}}*/
+// FileFd::Size - Return the size of the content in the file /*{{{*/
+unsigned long long FileFd::Size()
+{
+ if (d == nullptr)
+ return 0;
+ return d->InternalSize();
+}
+ /*}}}*/
+// FileFd::Close - Close the file if the close flag is set /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FileFd::Close()
+{
+ if (Failed() == false && Flush() == false)
+ return false;
+ if (iFd == -1)
+ return true;
+
+ bool Res = true;
+ if ((Flags & AutoClose) == AutoClose)
+ {
+ if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
+ Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
+ }
+
+ if (d != NULL)
+ {
+ Res &= d->InternalClose(FileName);
+ delete d;
+ d = NULL;
+ }
+
+ if ((Flags & Replace) == Replace) {
+ if (Failed() == false && rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
+ Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
+
+ FileName = TemporaryFileName; // for the unlink() below.
+ TemporaryFileName.clear();
+ }
+
+ iFd = -1;
+
+ if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
+ FileName.empty() == false)
+ Res &= RemoveFile("FileFd::Close", FileName);
+
+ if (Res == false)
+ Flags |= Fail;
+ return Res;
+}
+ /*}}}*/
+// FileFd::Sync - Sync the file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+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;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = _error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize);
+ va_end(args);
+ } while (retry);
+ 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;
+ bool retry;
+ do {
+ va_start(args,Description);
+ retry = _error->Insert(GlobalError::ERROR, Description, args, msgSize);
+ va_end(args);
+ } while (retry);
+ return false;
+}
+ /*}}}*/
+gzFile FileFd::gzFd() { /*{{{*/
+#ifdef HAVE_ZLIB
+ GzipFileFdPrivate * const gzipd = dynamic_cast<GzipFileFdPrivate*>(d);
+ if (gzipd == nullptr)
+ return nullptr;
+ else
+ return gzipd->gz;
+#else
+ return nullptr;
+#endif
+}
+ /*}}}*/
+
+// Glob - wrapper around "glob()" /*{{{*/
+std::vector<std::string> Glob(std::string const &pattern, int flags)
+{
+ std::vector<std::string> result;
+ glob_t globbuf;
+ int glob_res;
+ unsigned int i;
+
+ glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
+
+ if (glob_res != 0)
+ {
+ if(glob_res != GLOB_NOMATCH) {
+ _error->Errno("glob", "Problem with glob");
+ return result;
+ }
+ }
+
+ // append results
+ for(i=0;i<globbuf.gl_pathc;i++)
+ result.push_back(string(globbuf.gl_pathv[i]));
+
+ globfree(&globbuf);
+ return result;
+}
+ /*}}}*/
+static std::string APT_NONNULL(1) GetTempDirEnv(char const * const env) /*{{{*/
+{
+ const char *tmpdir = getenv(env);
+
+#ifdef P_tmpdir
+ if (!tmpdir)
+ tmpdir = P_tmpdir;
+#endif
+
+ struct stat st;
+ if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
+ stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0) // exists and is directory
+ tmpdir = "/tmp";
+ else if (geteuid() != 0 && // root can do everything anyway
+ faccessat(AT_FDCWD, tmpdir, R_OK | W_OK | X_OK, AT_EACCESS) != 0) // current user has rwx access to directory
+ tmpdir = "/tmp";
+
+ return string(tmpdir);
+}
+ /*}}}*/
+std::string GetTempDir() /*{{{*/
+{
+ return GetTempDirEnv("TMPDIR");
+}
+std::string GetTempDir(std::string const &User)
+{
+ // no need/possibility to drop privs
+ if(getuid() != 0 || User.empty() || User == "root")
+ return GetTempDir();
+
+ struct passwd const * const pw = getpwnam(User.c_str());
+ if (pw == NULL)
+ return GetTempDir();
+
+ gid_t const old_euid = geteuid();
+ gid_t const old_egid = getegid();
+ if (setegid(pw->pw_gid) != 0)
+ _error->Errno("setegid", "setegid %u failed", pw->pw_gid);
+ if (seteuid(pw->pw_uid) != 0)
+ _error->Errno("seteuid", "seteuid %u failed", pw->pw_uid);
+
+ std::string const tmp = GetTempDir();
+
+ if (seteuid(old_euid) != 0)
+ _error->Errno("seteuid", "seteuid %u failed", old_euid);
+ if (setegid(old_egid) != 0)
+ _error->Errno("setegid", "setegid %u failed", old_egid);
+
+ return tmp;
+}
+ /*}}}*/
+FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd) /*{{{*/
+{
+ return GetTempFile(Prefix, ImmediateUnlink, TmpFd, false);
+}
+FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd, bool Buffered)
+{
+ char fn[512];
+ FileFd * const Fd = TmpFd == nullptr ? new FileFd() : TmpFd;
+
+ std::string const tempdir = GetTempDir();
+ snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
+ tempdir.c_str(), Prefix.c_str());
+ int const fd = mkstemp(fn);
+ if (ImmediateUnlink)
+ unlink(fn);
+ if (fd < 0)
+ {
+ _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
+ if (TmpFd == nullptr)
+ delete Fd;
+ return nullptr;
+ }
+ if (!Fd->OpenDescriptor(fd, FileFd::ReadWrite | (Buffered ? FileFd::BufferedWrite : 0), FileFd::None, true))
+ {
+ _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
+ if (TmpFd == nullptr)
+ delete Fd;
+ return nullptr;
+ }
+ if (ImmediateUnlink == false)
+ Fd->SetFileName(fn);
+ return Fd;
+}
+ /*}}}*/
+bool Rename(std::string From, std::string To) /*{{{*/
+{
+ if (rename(From.c_str(),To.c_str()) != 0)
+ {
+ _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
+ From.c_str(),To.c_str());
+ return false;
+ }
+ return true;
+}
+ /*}}}*/
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
+{
+ return Popen(Args, Fd, Child, Mode, true);
+}
+ /*}}}*/
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr)/*{{{*/
+{
+ return Popen(Args, Fd, Child, Mode, CaptureStderr, false);
+}
+ /*}}}*/
+bool Popen(const char *Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr, bool Sandbox) /*{{{*/
+{
+ int fd;
+ if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
+ return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
+
+ int Pipe[2] = {-1, -1};
+ if(pipe(Pipe) != 0)
+ return _error->Errno("pipe", _("Failed to create subprocess IPC"));
+
+ std::set<int> keep_fds;
+ keep_fds.insert(Pipe[0]);
+ keep_fds.insert(Pipe[1]);
+ Child = ExecFork(keep_fds);
+ if(Child < 0)
+ return _error->Errno("fork", "Failed to fork");
+ if(Child == 0)
+ {
+ if (Sandbox && (getuid() == 0 || geteuid() == 0) && !DropPrivileges())
+ {
+ _error->DumpErrors();
+ _exit(1);
+ }
+ if(Mode == FileFd::ReadOnly)
+ {
+ close(Pipe[0]);
+ fd = Pipe[1];
+ }
+ else if(Mode == FileFd::WriteOnly)
+ {
+ close(Pipe[1]);
+ fd = Pipe[0];
+ }
+
+ if(Mode == FileFd::ReadOnly)
+ {
+ dup2(fd, 1);
+ if (CaptureStderr == true)
+ dup2(fd, 2);
+ } else if(Mode == FileFd::WriteOnly)
+ dup2(fd, 0);
+
+ execv(Args[0], (char**)Args);
+ _exit(100);
+ }
+ if(Mode == FileFd::ReadOnly)
+ {
+ close(Pipe[1]);
+ fd = Pipe[0];
+ }
+ else if(Mode == FileFd::WriteOnly)
+ {
+ close(Pipe[0]);
+ fd = Pipe[1];
+ }
+ else
+ return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
+ Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
+
+ return true;
+}
+ /*}}}*/
+bool DropPrivileges() /*{{{*/
+{
+ if(_config->FindB("Debug::NoDropPrivs", false) == true)
+ return true;
+
+#if __gnu_linux__
+#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
+#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
+#endif
+ // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
+ int ret = prctl(38, 1, 0, 0, 0);
+ // ignore EINVAL - kernel is too old to understand the option
+ if(ret < 0 && errno != EINVAL)
+ _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
+#endif
+
+ // empty setting disables privilege dropping - this also ensures
+ // backward compatibility, see bug #764506
+ const std::string toUser = _config->Find("APT::Sandbox::User");
+ if (toUser.empty() || toUser == "root")
+ return true;
+
+ // a lot can go wrong trying to drop privileges completely,
+ // so ideally we would like to verify that we have done it –
+ // but the verify asks for too much in case of fakeroot (and alike)
+ // [Specific checks can be overridden with dedicated options]
+ bool const VerifySandboxing = _config->FindB("APT::Sandbox::Verify", false);
+
+ // uid will be 0 in the end, but gid might be different anyway
+ uid_t const old_uid = getuid();
+ gid_t const old_gid = getgid();
+
+ if (old_uid != 0)
+ return true;
+
+ struct passwd *pw = getpwnam(toUser.c_str());
+ if (pw == NULL)
+ return _error->Error("No user %s, can not drop rights", toUser.c_str());
+
+ // Do not change the order here, it might break things
+ // Get rid of all our supplementary groups first
+ if (setgroups(1, &pw->pw_gid))
+ return _error->Errno("setgroups", "Failed to setgroups");
+
+ // Now change the group ids to the new user
+#ifdef HAVE_SETRESGID
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
+ return _error->Errno("setresgid", "Failed to set new group ids");
+#else
+ if (setegid(pw->pw_gid) != 0)
+ return _error->Errno("setegid", "Failed to setegid");
+
+ if (setgid(pw->pw_gid) != 0)
+ return _error->Errno("setgid", "Failed to setgid");
+#endif
+
+ // Change the user ids to the new user
+#ifdef HAVE_SETRESUID
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
+ return _error->Errno("setresuid", "Failed to set new user ids");
+#else
+ if (setuid(pw->pw_uid) != 0)
+ return _error->Errno("setuid", "Failed to setuid");
+ if (seteuid(pw->pw_uid) != 0)
+ return _error->Errno("seteuid", "Failed to seteuid");
+#endif
+
+ // disabled by default as fakeroot doesn't implement getgroups currently (#806521)
+ if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Groups", false) == true)
+ {
+ // Verify that the user isn't still in any supplementary groups
+ long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ std::unique_ptr<gid_t[]> gidlist(new gid_t[ngroups_max]);
+ if (unlikely(gidlist == NULL))
+ return _error->Error("Allocation of a list of size %lu for getgroups failed", ngroups_max);
+ ssize_t gidlist_nr;
+ if ((gidlist_nr = getgroups(ngroups_max, gidlist.get())) < 0)
+ return _error->Errno("getgroups", "Could not get new groups (%lu)", ngroups_max);
+ for (ssize_t i = 0; i < gidlist_nr; ++i)
+ if (gidlist[i] != pw->pw_gid)
+ return _error->Error("Could not switch group, user %s is still in group %d", toUser.c_str(), gidlist[i]);
+ }
+
+ // enabled by default as all fakeroot-lookalikes should fake that accordingly
+ if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::IDs", true) == true)
+ {
+ // Verify that gid, egid, uid, and euid changed
+ if (getgid() != pw->pw_gid)
+ return _error->Error("Could not switch group");
+ if (getegid() != pw->pw_gid)
+ return _error->Error("Could not switch effective group");
+ if (getuid() != pw->pw_uid)
+ return _error->Error("Could not switch user");
+ if (geteuid() != pw->pw_uid)
+ return _error->Error("Could not switch effective user");
+
+#ifdef HAVE_GETRESUID
+ // verify that the saved set-user-id was changed as well
+ uid_t ruid = 0;
+ uid_t euid = 0;
+ uid_t suid = 0;
+ if (getresuid(&ruid, &euid, &suid))
+ return _error->Errno("getresuid", "Could not get saved set-user-ID");
+ if (suid != pw->pw_uid)
+ return _error->Error("Could not switch saved set-user-ID");
+#endif
+
+#ifdef HAVE_GETRESGID
+ // verify that the saved set-group-id was changed as well
+ gid_t rgid = 0;
+ gid_t egid = 0;
+ gid_t sgid = 0;
+ if (getresgid(&rgid, &egid, &sgid))
+ return _error->Errno("getresuid", "Could not get saved set-group-ID");
+ if (sgid != pw->pw_gid)
+ return _error->Error("Could not switch saved set-group-ID");
+#endif
+ }
+
+ // disabled as fakeroot doesn't forbid (by design) (re)gaining root from unprivileged
+ if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Regain", false) == true)
+ {
+ // Check that uid and gid changes do not work anymore
+ if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
+ return _error->Error("Could restore a gid to root, privilege dropping did not work");
+
+ if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
+ return _error->Error("Could restore a uid to root, privilege dropping did not work");
+ }
+
+ if (_config->FindB("APT::Sandbox::ResetEnvironment", true))
+ {
+ setenv("HOME", pw->pw_dir, 1);
+ setenv("USER", pw->pw_name, 1);
+ setenv("USERNAME", pw->pw_name, 1);
+ setenv("LOGNAME", pw->pw_name, 1);
+ auto const shell = flNotDir(pw->pw_shell);
+ if (shell == "false" || shell == "nologin")
+ setenv("SHELL", "/bin/sh", 1);
+ else
+ setenv("SHELL", pw->pw_shell, 1);
+ auto const apt_setenv_tmp = [](char const * const env) {
+ auto const tmpdir = getenv(env);
+ if (tmpdir != nullptr)
+ {
+ auto const ourtmpdir = GetTempDirEnv(env);
+ if (ourtmpdir != tmpdir)
+ setenv(env, ourtmpdir.c_str(), 1);
+ }
+ };
+ apt_setenv_tmp("TMPDIR");
+ apt_setenv_tmp("TEMPDIR");
+ apt_setenv_tmp("TMP");
+ apt_setenv_tmp("TEMP");
+ }
+
+ return true;
+}
+ /*}}}*/
+bool OpenConfigurationFileFd(std::string const &File, FileFd &Fd) /*{{{*/
+{
+ int const fd = open(File.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY);
+ if (fd == -1)
+ return _error->WarningE("open", _("Unable to read %s"), File.c_str());
+ APT::Configuration::Compressor none(".", "", "", nullptr, nullptr, 0);
+ if (Fd.OpenDescriptor(fd, FileFd::ReadOnly, none, true) == false)
+ return false;
+ Fd.SetFileName(File);
+ return true;
+}
+ /*}}}*/
+int Inhibit(const char *what, const char *who, const char *why, const char *mode) /*{{{*/
+{
+#ifdef HAVE_SYSTEMD
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus_message *m = NULL;
+ sd_bus *bus = NULL;
+ int fd;
+ int r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ goto out;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "Inhibit",
+ &error,
+ &m,
+ "ssss",
+ what,
+ who,
+ why,
+ mode);
+ if (r < 0)
+ goto out;
+
+ r = sd_bus_message_read(m, "h", &fd);
+ if (r < 0)
+ goto out;
+
+ // We received a file descriptor, return it - systemd will close the read fd
+ // on free, so let's duplicate it here.
+ r = dup(fd);
+out:
+ sd_bus_error_free(&error);
+ sd_bus_message_unref(m);
+ sd_bus_unref(bus);
+ return r;
+#else
+ return -ENOTSUP;
+#endif
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h
new file mode 100644
index 0000000..9005b81
--- /dev/null
+++ b/apt-pkg/contrib/fileutl.h
@@ -0,0 +1,305 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ File Utilities
+
+ CopyFile - Buffered copy of a single file
+ GetLock - dpkg compatible lock file manipulation (fcntl)
+ FileExists - Returns true if the file exists
+ SafeGetCWD - Returns the CWD in a string with overrun protection
+
+ The file class is a handy abstraction for various functions+classes
+ that need to accept filenames.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_FILEUTL_H
+#define PKGLIB_FILEUTL_H
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/macros.h>
+
+#include <set>
+#include <string>
+#include <vector>
+#include <time.h>
+
+#include <zlib.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+#endif
+
+/* Define this for python-apt */
+#define APT_HAS_GZIP 1
+
+class FileFdPrivate;
+class FileFd
+{
+ friend class FileFdPrivate;
+ friend class GzipFileFdPrivate;
+ friend class Bz2FileFdPrivate;
+ friend class LzmaFileFdPrivate;
+ friend class Lz4FileFdPrivate;
+ friend class ZstdFileFdPrivate;
+ friend class DirectFileFdPrivate;
+ friend class PipedFileFdPrivate;
+ protected:
+ int iFd;
+
+ enum LocalFlags {AutoClose = (1<<0),Fail = (1<<1),DelOnFail = (1<<2),
+ HitEof = (1<<3), Replace = (1<<4), Compressed = (1<<5) };
+ unsigned long Flags;
+ std::string FileName;
+ std::string TemporaryFileName;
+
+ public:
+ enum OpenMode {
+ ReadOnly = (1 << 0),
+ WriteOnly = (1 << 1),
+ ReadWrite = ReadOnly | WriteOnly,
+
+ Create = (1 << 2),
+ Exclusive = (1 << 3),
+ Atomic = Exclusive | (1 << 4),
+ Empty = (1 << 5),
+ BufferedWrite = (1 << 6),
+
+ WriteEmpty = ReadWrite | Create | Empty,
+ WriteExists = ReadWrite,
+ WriteAny = ReadWrite | Create,
+ WriteTemp = ReadWrite | Create | Exclusive,
+ ReadOnlyGzip,
+ WriteAtomic = ReadWrite | Create | Atomic
+ };
+ enum CompressMode
+ {
+ Auto = 'A',
+ None = 'N',
+ Extension = 'E',
+ Gzip = 'G',
+ Bzip2 = 'B',
+ Lzma = 'L',
+ Xz = 'X',
+ Lz4 = '4',
+ Zstd = 'Z'
+ };
+
+ inline bool Read(void *To,unsigned long long Size,bool AllowEof)
+ {
+ unsigned long long Jnk;
+ if (AllowEof)
+ return Read(To,Size,&Jnk);
+ return Read(To,Size);
+ }
+ bool Read(void *To,unsigned long long Size,unsigned long long *Actual = 0);
+ bool static Read(int const Fd, void *To, unsigned long long Size, unsigned long long * const Actual = 0);
+ /** read a complete line or until buffer is full
+ *
+ * The buffer will always be \\0 terminated, so at most Size-1 characters are read.
+ * If the buffer holds a complete line the last character (before \\0) will be
+ * the newline character \\n otherwise the line was longer than the buffer.
+ *
+ * @param To buffer which will hold the line
+ * @param Size of the buffer to fill
+ * @param \b nullptr is returned in error cases, otherwise
+ * the parameter \b To now filled with the line.
+ */
+ char* ReadLine(char *To, unsigned long long const Size);
+ /** read a complete line from the file
+ *
+ * Similar to std::getline() the string does \b not include
+ * the newline, but just the content of the line as the newline
+ * is not needed to distinguish cases as for the other #ReadLine method.
+ *
+ * @param To string which will hold the line
+ * @return \b true if successful, otherwise \b false
+ */
+ bool ReadLine(std::string &To);
+ bool Flush();
+ bool Write(const void *From,unsigned long long Size);
+ bool static Write(int Fd, const void *From, unsigned long long Size);
+ bool Seek(unsigned long long To);
+ bool Skip(unsigned long long To);
+ bool Truncate(unsigned long long To);
+ unsigned long long Tell();
+ // the size of the file content (compressed files will be uncompressed first)
+ unsigned long long Size();
+ // the size of the file itself
+ unsigned long long FileSize();
+ time_t ModificationTime();
+
+ /* You want to use 'unsigned long long' if you are talking about a file
+ to be able to support large files (>2 or >4 GB) properly.
+ This shouldn't happen all to often for the indexes, but deb's might be…
+ And as the auto-conversation converts a 'unsigned long *' to a 'bool'
+ instead of 'unsigned long long *' we need to provide this explicitly -
+ otherwise applications magically start to fail… */
+ bool Read(void *To,unsigned long long Size,unsigned long *Actual) APT_DEPRECATED_MSG("The Actual variable you pass in should be an unsigned long long")
+ {
+ unsigned long long R;
+ bool const T = Read(To, Size, &R);
+ *Actual = R;
+ return T;
+ }
+
+ bool Open(std::string FileName,unsigned int const Mode,CompressMode Compress,unsigned long const AccessMode = 0666);
+ bool Open(std::string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor,unsigned long const AccessMode = 0666);
+ inline bool Open(std::string const &FileName,unsigned int const Mode, unsigned long const AccessMode = 0666) {
+ return Open(FileName, Mode, None, AccessMode);
+ };
+ bool OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose=false);
+ bool OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose=false);
+ inline bool OpenDescriptor(int Fd, unsigned int const Mode, bool AutoClose=false) {
+ return OpenDescriptor(Fd, Mode, None, AutoClose);
+ };
+ bool Close();
+ bool Sync();
+
+ // Simple manipulators
+ inline int Fd() {return iFd;};
+ inline void Fd(int fd) { OpenDescriptor(fd, ReadWrite);};
+ gzFile gzFd() APT_DEPRECATED_MSG("Implementation detail, do not use to be able to support bzip2, xz and co") APT_PURE;
+
+ inline bool IsOpen() {return iFd >= 0;};
+ inline bool Failed() {return (Flags & Fail) == Fail;};
+ inline void EraseOnFailure() {Flags |= DelOnFail;};
+ inline void OpFail() {Flags |= Fail;};
+ inline bool Eof() {return (Flags & HitEof) == HitEof;};
+ inline bool IsCompressed() {return (Flags & Compressed) == Compressed;};
+ inline std::string &Name() {return FileName;};
+ inline void SetFileName(std::string const &name) { FileName = name; };
+
+ FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode = 0666);
+ FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode = 0666);
+ FileFd();
+ FileFd(int const Fd, unsigned int const Mode = ReadWrite, CompressMode Compress = None);
+ FileFd(int const Fd, bool const AutoClose);
+ virtual ~FileFd();
+
+ private:
+ FileFdPrivate * d;
+ APT_HIDDEN FileFd(const FileFd &);
+ APT_HIDDEN FileFd & operator=(const FileFd &);
+ APT_HIDDEN bool OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor);
+
+ // private helpers to set Fail flag and call _error->Error
+ APT_HIDDEN bool FileFdErrno(const char* Function, const char* Description,...) APT_PRINTF(3) APT_COLD;
+ APT_HIDDEN bool FileFdError(const char* Description,...) APT_PRINTF(2) APT_COLD;
+};
+
+bool RunScripts(const char *Cnf);
+bool CopyFile(FileFd &From,FileFd &To);
+bool RemoveFile(char const * const Function, std::string const &FileName);
+bool RemoveFileAt(char const * const Function, int const dirfd, std::string const &FileName);
+int GetLock(std::string File,bool Errors = true);
+bool FileExists(std::string File);
+bool RealFileExists(std::string File);
+bool DirectoryExists(std::string const &Path);
+bool CreateDirectory(std::string const &Parent, std::string const &Path);
+time_t GetModificationTime(std::string const &Path);
+bool Rename(std::string From, std::string To);
+
+std::string GetTempDir();
+std::string GetTempDir(std::string const &User);
+FileFd* GetTempFile(std::string const &Prefix = "",
+ bool ImmediateUnlink = true,
+ FileFd * const TmpFd = NULL);
+
+// FIXME: GetTempFile should always return a buffered file
+FileFd* GetTempFile(std::string const &Prefix,
+ bool ImmediateUnlink ,
+ FileFd * const TmpFd,
+ bool Buffered) APT_HIDDEN;
+
+/** \brief Ensure the existence of the given Path
+ *
+ * \param Parent directory of the Path directory - a trailing
+ * /apt/ will be removed before CreateDirectory call.
+ * \param Path which should exist after (successful) call
+ */
+bool CreateAPTDirectoryIfNeeded(std::string const &Parent, std::string const &Path);
+
+std::vector<std::string> GetListOfFilesInDir(std::string const &Dir, std::string const &Ext,
+ bool const &SortList, bool const &AllowNoExt=false);
+std::vector<std::string> GetListOfFilesInDir(std::string const &Dir, std::vector<std::string> const &Ext,
+ bool const &SortList);
+std::vector<std::string> GetListOfFilesInDir(std::string const &Dir, bool SortList);
+std::string SafeGetCWD();
+void SetCloseExec(int Fd,bool Close);
+void SetNonBlock(int Fd,bool Block);
+bool WaitFd(int Fd,bool write = false,unsigned long timeout = 0);
+pid_t ExecFork();
+pid_t ExecFork(std::set<int> keep_fds);
+void MergeKeepFdsFromConfiguration(std::set<int> &keep_fds);
+bool ExecWait(pid_t Pid,const char *Name,bool Reap = false);
+
+// check if the given file starts with a PGP cleartext signature
+bool StartsWithGPGClearTextSignature(std::string const &FileName);
+
+/** change file attributes to requested known good values
+ *
+ * The method skips the user:group setting if not root.
+ *
+ * @param requester is printed as functionname in error cases
+ * @param file is the file to be modified
+ * @param user is the (new) owner of the file, e.g. _apt
+ * @param group is the (new) group owning the file, e.g. root
+ * @param mode is the access mode of the file, e.g. 0644
+ */
+bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode);
+
+/**
+ * \brief Drop privileges
+ *
+ * Drop the privileges to the user _apt (or the one specified in
+ * APT::Sandbox::User). This does not set the supplementary group
+ * ids up correctly, it only uses the default group. Also prevent
+ * the process from gaining any new privileges afterwards, at least
+ * on Linux.
+ *
+ * \return true on success, false on failure with _error set
+ */
+bool DropPrivileges();
+
+// File string manipulators
+std::string flNotDir(std::string File);
+std::string flNotFile(std::string File);
+std::string flNoLink(std::string File);
+std::string flExtension(std::string File);
+std::string flCombine(std::string Dir,std::string File);
+
+/** \brief Takes a file path and returns the absolute path
+ */
+std::string flAbsPath(std::string File);
+/** \brief removes superfluous /./ and // from path */
+APT_HIDDEN std::string flNormalize(std::string file);
+
+// simple c++ glob
+std::vector<std::string> Glob(std::string const &pattern, int flags=0);
+
+/** \brief Popen() implementation that execv() instead of using a shell
+ *
+ * \param Args the execv style command to run
+ * \param FileFd is a reference to the FileFd to use for input or output
+ * \param Child a reference to the integer that stores the child pid
+ * Note that you must call ExecWait() or similar to cleanup
+ * \param Mode is either FileFd::ReadOnly or FileFd::WriteOnly
+ * \param CaptureStderr True if we should capture stderr in addition to stdout.
+ * (default: True).
+ * \param Sandbox True if this should run sandboxed
+ * \return true on success, false on failure with _error set
+ */
+bool Popen(const char *Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr, bool Sandbox) APT_HIDDEN;
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr);
+bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode);
+
+APT_HIDDEN bool OpenConfigurationFileFd(std::string const &File, FileFd &Fd);
+
+APT_HIDDEN int Inhibit(const char *what, const char *who, const char *why, const char *mode);
+
+#endif
diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc
new file mode 100644
index 0000000..d956eaf
--- /dev/null
+++ b/apt-pkg/contrib/gpgv.cc
@@ -0,0 +1,580 @@
+// -*- mode: cpp; mode: fold -*-
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/gpgv.h>
+#include <apt-pkg/strutl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// syntactic sugar to wrap a raw pointer with a custom deleter in a std::unique_ptr
+static std::unique_ptr<char, decltype(&free)> make_unique_char(void *const str = nullptr)
+{
+ return {static_cast<char *>(str), &free};
+}
+static std::unique_ptr<FILE, decltype(&fclose)> make_unique_FILE(std::string const &filename, char const *const mode)
+{
+ return {fopen(filename.c_str(), mode), &fclose};
+}
+
+class LineBuffer /*{{{*/
+{
+ char *buffer = nullptr;
+ size_t buffer_size = 0;
+ int line_length = 0;
+ // a "normal" find_last_not_of returns npos if not found
+ int find_last_not_of_length(APT::StringView const bad) const
+ {
+ for (int result = line_length - 1; result >= 0; --result)
+ if (bad.find(buffer[result]) == APT::StringView::npos)
+ return result + 1;
+ return 0;
+ }
+
+ public:
+ bool empty() const noexcept { return view().empty(); }
+ APT::StringView view() const noexcept { return {buffer, static_cast<size_t>(line_length)}; }
+ bool starts_with(APT::StringView const start) const { return view().substr(0, start.size()) == start; }
+
+ bool writeTo(FileFd *const to, size_t offset = 0) const
+ {
+ if (to == nullptr)
+ return true;
+ return to->Write(buffer + offset, line_length - offset);
+ }
+ bool writeLineTo(FileFd *const to) const
+ {
+ if (to == nullptr)
+ return true;
+ buffer[line_length] = '\n';
+ bool const result = to->Write(buffer, line_length + 1);
+ buffer[line_length] = '\0';
+ return result;
+ }
+ bool writeNewLineIf(FileFd *const to, bool const condition) const
+ {
+ if (not condition || to == nullptr)
+ return true;
+ return to->Write("\n", 1);
+ }
+
+ bool readFrom(FILE *stream, std::string const &InFile, bool acceptEoF = false)
+ {
+ errno = 0;
+ line_length = getline(&buffer, &buffer_size, stream);
+ if (errno != 0)
+ return _error->Errno("getline", "Could not read from %s", InFile.c_str());
+ if (line_length == -1)
+ {
+ if (acceptEoF)
+ return false;
+ return _error->Error("Splitting of clearsigned file %s failed as it doesn't contain all expected parts", InFile.c_str());
+ }
+ // a) remove newline characters, so we can work consistently with lines
+ line_length = find_last_not_of_length("\n\r");
+ // b) remove trailing whitespaces as defined by rfc4880 §7.1
+ line_length = find_last_not_of_length(" \t");
+ buffer[line_length] = '\0';
+ return true;
+ }
+
+ ~LineBuffer() { free(buffer); }
+};
+static bool operator==(LineBuffer const &buf, APT::StringView const exp) noexcept
+{
+ return buf.view() == exp;
+}
+static bool operator!=(LineBuffer const &buf, APT::StringView const exp) noexcept
+{
+ return buf.view() != exp;
+}
+ /*}}}*/
+// ExecGPGV - returns the command needed for verify /*{{{*/
+// ---------------------------------------------------------------------
+/* Generating the commandline for calling gpg is somehow complicated as
+ we need to add multiple keyrings and user supplied options.
+ Also, as gpg has no options to enforce a certain reduced style of
+ clear-signed files (=the complete content of the file is signed and
+ the content isn't encoded) we do a divide and conquer approach here
+ and split up the clear-signed file in message and signature for gpg.
+ And as a cherry on the cake, we use our apt-key wrapper to do part
+ of the lifting in regards to merging keyrings. Fun for the whole family.
+*/
+static bool iovprintf(std::ostream &out, const char *format,
+ va_list &args, ssize_t &size) {
+ auto S = make_unique_char(malloc(size));
+ ssize_t const n = vsnprintf(S.get(), size, format, args);
+ if (n > -1 && n < size) {
+ out << S.get();
+ return true;
+ } else {
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+ }
+ return false;
+}
+static void APT_PRINTF(4) apt_error(std::ostream &outterm, int const statusfd, int fd[2], const char *format, ...)
+{
+ std::ostringstream outstr;
+ std::ostream &out = (statusfd == -1) ? outterm : outstr;
+ va_list args;
+ ssize_t size = 400;
+ while (true) {
+ bool ret;
+ va_start(args,format);
+ ret = iovprintf(out, format, args, size);
+ va_end(args);
+ if (ret)
+ break;
+ }
+ if (statusfd != -1)
+ {
+ auto const errtag = "[APTKEY:] ERROR ";
+ outstr << '\n';
+ auto const errtext = outstr.str();
+ if (not FileFd::Write(fd[1], errtag, strlen(errtag)) ||
+ not FileFd::Write(fd[1], errtext.data(), errtext.size()))
+ outterm << errtext << std::flush;
+ }
+}
+void ExecGPGV(std::string const &File, std::string const &FileGPG,
+ int const &statusfd, int fd[2], std::string const &key)
+{
+ #define EINTERNAL 111
+ std::string const aptkey = _config->Find("Dir::Bin::apt-key", CMAKE_INSTALL_FULL_BINDIR "/apt-key");
+
+ bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
+ struct exiter {
+ std::vector<const char *> files;
+ void operator ()(int code) APT_NORETURN {
+ std::for_each(files.begin(), files.end(), unlink);
+ exit(code);
+ }
+ } local_exit;
+
+
+ std::vector<const char *> Args;
+ Args.reserve(10);
+
+ Args.push_back(aptkey.c_str());
+ Args.push_back("--quiet");
+ Args.push_back("--readonly");
+ auto const keysFileFpr = VectorizeString(key, ',');
+ for (auto const &k: keysFileFpr)
+ {
+ if (unlikely(k.empty()))
+ continue;
+ if (k[0] == '/')
+ {
+ Args.push_back("--keyring");
+ Args.push_back(k.c_str());
+ }
+ else
+ {
+ Args.push_back("--keyid");
+ Args.push_back(k.c_str());
+ }
+ }
+ Args.push_back("verify");
+
+ char statusfdstr[10];
+ if (statusfd != -1)
+ {
+ Args.push_back("--status-fd");
+ snprintf(statusfdstr, sizeof(statusfdstr), "%i", statusfd);
+ Args.push_back(statusfdstr);
+ }
+
+ Configuration::Item const *Opts;
+ Opts = _config->Tree("Acquire::gpgv::Options");
+ if (Opts != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty())
+ continue;
+ Args.push_back(Opts->Value.c_str());
+ }
+ }
+
+ enum { DETACHED, CLEARSIGNED } releaseSignature = (FileGPG != File) ? DETACHED : CLEARSIGNED;
+ auto sig = make_unique_char();
+ auto data = make_unique_char();
+ auto conf = make_unique_char();
+
+ // Dump the configuration so apt-key picks up the correct Dir values
+ {
+ {
+ std::string tmpfile;
+ strprintf(tmpfile, "%s/apt.conf.XXXXXX", GetTempDir().c_str());
+ conf.reset(strdup(tmpfile.c_str()));
+ }
+ if (conf == nullptr) {
+ apt_error(std::cerr, statusfd, fd, "Couldn't create tempfile names for passing config to apt-key");
+ local_exit(EINTERNAL);
+ }
+ int confFd = mkstemp(conf.get());
+ if (confFd == -1) {
+ apt_error(std::cerr, statusfd, fd, "Couldn't create temporary file %s for passing config to apt-key", conf.get());
+ local_exit(EINTERNAL);
+ }
+ local_exit.files.push_back(conf.get());
+
+ std::ofstream confStream(conf.get());
+ close(confFd);
+ _config->Dump(confStream);
+ confStream.close();
+ setenv("APT_CONFIG", conf.get(), 1);
+ }
+
+ if (releaseSignature == DETACHED)
+ {
+ auto detached = make_unique_FILE(FileGPG, "r");
+ if (detached.get() == nullptr)
+ {
+ apt_error(std::cerr, statusfd, fd, "Detached signature file '%s' could not be opened", FileGPG.c_str());
+ local_exit(EINTERNAL);
+ }
+ LineBuffer buf;
+ bool open_signature = false;
+ bool found_badcontent = false;
+ size_t found_signatures = 0;
+ while (buf.readFrom(detached.get(), FileGPG, true))
+ {
+ if (open_signature)
+ {
+ if (buf == "-----END PGP SIGNATURE-----")
+ open_signature = false;
+ else if (buf.starts_with("-"))
+ {
+ // the used Radix-64 is not using dash for any value, so a valid line can't
+ // start with one. Header keys could, but no existent one does and seems unlikely.
+ // Instead it smells a lot like a header the parser didn't recognize.
+ apt_error(std::cerr, statusfd, fd, "Detached signature file '%s' contains unexpected line starting with a dash", FileGPG.c_str());
+ local_exit(112);
+ }
+ }
+ else //if (not open_signature)
+ {
+ if (buf == "-----BEGIN PGP SIGNATURE-----")
+ {
+ open_signature = true;
+ ++found_signatures;
+ if (found_badcontent)
+ break;
+ }
+ else
+ {
+ found_badcontent = true;
+ if (found_signatures != 0)
+ break;
+ }
+ }
+ }
+ if (found_signatures == 0 && statusfd != -1)
+ {
+ auto const errtag = "[GNUPG:] NODATA\n";
+ FileFd::Write(fd[1], errtag, strlen(errtag));
+ // guess if this is a binary signature, we never officially supported them,
+ // but silently accepted them via passing them unchecked to gpgv
+ if (found_badcontent)
+ {
+ rewind(detached.get());
+ auto ptag = fgetc(detached.get());
+ // §4.2 says that the first bit is always set and gpg seems to generate
+ // only old format which is indicated by the second bit not set
+ if (ptag != EOF && (ptag & 0x80) != 0 && (ptag & 0x40) == 0)
+ {
+ apt_error(std::cerr, statusfd, fd, "Detached signature file '%s' is in unsupported binary format", FileGPG.c_str());
+ local_exit(112);
+ }
+ }
+ // This is not an attack attempt but a file even gpgv would complain about
+ // likely the result of a paywall which is covered by the gpgv method
+ local_exit(113);
+ }
+ else if (found_badcontent)
+ {
+ apt_error(std::cerr, statusfd, fd, "Detached signature file '%s' contains lines not belonging to a signature", FileGPG.c_str());
+ local_exit(112);
+ }
+ if (open_signature)
+ {
+ apt_error(std::cerr, statusfd, fd, "Detached signature file '%s' contains unclosed signatures", FileGPG.c_str());
+ local_exit(112);
+ }
+
+ Args.push_back(FileGPG.c_str());
+ Args.push_back(File.c_str());
+ }
+ else // clear-signed file
+ {
+ FileFd signature;
+ if (GetTempFile("apt.sig", false, &signature) == nullptr)
+ local_exit(EINTERNAL);
+ sig.reset(strdup(signature.Name().c_str()));
+ local_exit.files.push_back(sig.get());
+ FileFd message;
+ if (GetTempFile("apt.data", false, &message) == nullptr)
+ local_exit(EINTERNAL);
+ data.reset(strdup(message.Name().c_str()));
+ local_exit.files.push_back(data.get());
+
+ if (signature.Failed() || message.Failed() ||
+ not SplitClearSignedFile(File, &message, nullptr, &signature))
+ {
+ apt_error(std::cerr, statusfd, fd, "Splitting up %s into data and signature failed", File.c_str());
+ local_exit(112);
+ }
+ Args.push_back(sig.get());
+ Args.push_back(data.get());
+ }
+
+ Args.push_back(NULL);
+
+ if (Debug)
+ {
+ std::clog << "Preparing to exec: ";
+ for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
+ std::clog << " " << *a;
+ std::clog << std::endl;
+ }
+
+ if (statusfd != -1)
+ {
+ int const nullfd = open("/dev/null", O_WRONLY);
+ close(fd[0]);
+ // Redirect output to /dev/null; we read from the status fd
+ if (statusfd != STDOUT_FILENO)
+ dup2(nullfd, STDOUT_FILENO);
+ if (statusfd != STDERR_FILENO)
+ dup2(nullfd, STDERR_FILENO);
+ // Redirect the pipe to the status fd (3)
+ dup2(fd[1], statusfd);
+
+ putenv((char *)"LANG=");
+ putenv((char *)"LC_ALL=");
+ putenv((char *)"LC_MESSAGES=");
+ }
+
+
+ // We have created tempfiles we have to clean up
+ // and we do an additional check, so fork yet another time …
+ pid_t pid = ExecFork();
+ if(pid < 0) {
+ apt_error(std::cerr, statusfd, fd, "Fork failed for %s to check %s", Args[0], File.c_str());
+ local_exit(EINTERNAL);
+ }
+ if(pid == 0)
+ {
+ if (statusfd != -1)
+ dup2(fd[1], statusfd);
+ execvp(Args[0], (char **) &Args[0]);
+ apt_error(std::cerr, statusfd, fd, "Couldn't execute %s to check %s", Args[0], File.c_str());
+ local_exit(EINTERNAL);
+ }
+
+ // Wait and collect the error code - taken from WaitPid as we need the exact Status
+ int Status;
+ while (waitpid(pid,&Status,0) != pid)
+ {
+ if (errno == EINTR)
+ continue;
+ apt_error(std::cerr, statusfd, fd, _("Waited for %s but it wasn't there"), "apt-key");
+ local_exit(EINTERNAL);
+ }
+
+ // check if it exit'ed normally …
+ if (not WIFEXITED(Status))
+ {
+ apt_error(std::cerr, statusfd, fd, _("Sub-process %s exited unexpectedly"), "apt-key");
+ local_exit(EINTERNAL);
+ }
+
+ // … and with a good exit code
+ if (WEXITSTATUS(Status) != 0)
+ {
+ // we forward the statuscode, so don't generate a message on the fd in this case
+ apt_error(std::cerr, -1, fd, _("Sub-process %s returned an error code (%u)"), "apt-key", WEXITSTATUS(Status));
+ local_exit(WEXITSTATUS(Status));
+ }
+
+ // everything fine
+ local_exit(0);
+}
+ /*}}}*/
+// SplitClearSignedFile - split message into data/signature /*{{{*/
+bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
+ std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile)
+{
+ auto in = make_unique_FILE(InFile, "r");
+ if (in.get() == nullptr)
+ return _error->Errno("fopen", "can not open %s", InFile.c_str());
+
+ struct ScopedErrors
+ {
+ ScopedErrors() { _error->PushToStack(); }
+ ~ScopedErrors() { _error->MergeWithStack(); }
+ } scoped;
+ LineBuffer buf;
+
+ // start of the message
+ if (not buf.readFrom(in.get(), InFile))
+ return false; // empty or read error
+ if (buf != "-----BEGIN PGP SIGNED MESSAGE-----")
+ {
+ // this might be an unsigned file we don't want to report errors for,
+ // but still finish unsuccessful none the less.
+ while (buf.readFrom(in.get(), InFile, true))
+ if (buf == "-----BEGIN PGP SIGNED MESSAGE-----")
+ return _error->Error("Clearsigned file '%s' does not start with a signed message block.", InFile.c_str());
+
+ return false;
+ }
+
+ // save "Hash" Armor Headers
+ while (true)
+ {
+ if (not buf.readFrom(in.get(), InFile))
+ return false;
+ if (buf.empty())
+ break; // empty line ends the Armor Headers
+ if (buf.starts_with("-"))
+ // § 6.2 says unknown keys should be reported to the user. We don't go that far,
+ // but we assume that there will never be a header key starting with a dash
+ return _error->Error("Clearsigned file '%s' contains unexpected line starting with a dash (%s)", InFile.c_str(), "armor");
+ if (ContentHeader != nullptr && buf.starts_with("Hash: "))
+ ContentHeader->push_back(buf.view().to_string());
+ }
+
+ // the message itself
+ bool first_line = true;
+ while (true)
+ {
+ if (not buf.readFrom(in.get(), InFile))
+ return false;
+
+ if (buf.starts_with("-"))
+ {
+ if (buf == "-----BEGIN PGP SIGNATURE-----")
+ {
+ if (not buf.writeLineTo(SignatureFile))
+ return false;
+ break;
+ }
+ else if (buf.starts_with("- "))
+ {
+ // we don't have any fields which need to be dash-escaped,
+ // but implementations are free to escape all lines …
+ if (not buf.writeNewLineIf(ContentFile, not first_line) || not buf.writeTo(ContentFile, 2))
+ return false;
+ }
+ else
+ // § 7.1 says a client should warn, but we don't really work with files which
+ // should contain lines starting with a dash, so it is a lot more likely that
+ // this is an attempt to trick our parser vs. gpgv parser into ignoring a header
+ return _error->Error("Clearsigned file '%s' contains unexpected line starting with a dash (%s)", InFile.c_str(), "msg");
+ }
+ else if (not buf.writeNewLineIf(ContentFile, not first_line) || not buf.writeTo(ContentFile))
+ return false;
+ first_line = false;
+ }
+
+ // collect all signatures
+ bool open_signature = true;
+ while (true)
+ {
+ if (not buf.readFrom(in.get(), InFile, true))
+ break;
+
+ if (open_signature)
+ {
+ if (buf == "-----END PGP SIGNATURE-----")
+ open_signature = false;
+ else if (buf.starts_with("-"))
+ // the used Radix-64 is not using dash for any value, so a valid line can't
+ // start with one. Header keys could, but no existent one does and seems unlikely.
+ // Instead it smells a lot like a header the parser didn't recognize.
+ return _error->Error("Clearsigned file '%s' contains unexpected line starting with a dash (%s)", InFile.c_str(), "sig");
+ }
+ else //if (not open_signature)
+ {
+ if (buf == "-----BEGIN PGP SIGNATURE-----")
+ open_signature = true;
+ else
+ return _error->Error("Clearsigned file '%s' contains unsigned lines.", InFile.c_str());
+ }
+
+ if (not buf.writeLineTo(SignatureFile))
+ return false;
+ }
+ if (open_signature)
+ return _error->Error("Signature in file %s wasn't closed", InFile.c_str());
+
+ // Flush the files
+ if (SignatureFile != nullptr)
+ SignatureFile->Flush();
+ if (ContentFile != nullptr)
+ ContentFile->Flush();
+
+ // Catch-all for "unhandled" read/sync errors
+ if (_error->PendingError())
+ return false;
+ return true;
+}
+ /*}}}*/
+bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile) /*{{{*/
+{
+ // Buffered file
+ if (GetTempFile("clearsigned.message", true, &MessageFile, true) == nullptr)
+ return false;
+ if (MessageFile.Failed())
+ return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
+
+ _error->PushToStack();
+ bool const splitDone = SplitClearSignedFile(ClearSignedFileName, &MessageFile, NULL, NULL);
+ bool const errorDone = _error->PendingError();
+ _error->MergeWithStack();
+ if (not splitDone)
+ {
+ MessageFile.Close();
+
+ if (errorDone)
+ return false;
+
+ // we deal with an unsigned file
+ MessageFile.Open(ClearSignedFileName, FileFd::ReadOnly);
+ }
+ else // clear-signed
+ {
+ if (not MessageFile.Seek(0))
+ return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
+ }
+
+ return not MessageFile.Failed();
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/gpgv.h b/apt-pkg/contrib/gpgv.h
new file mode 100644
index 0000000..2a4cdad
--- /dev/null
+++ b/apt-pkg/contrib/gpgv.h
@@ -0,0 +1,92 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Helpers to deal with gpgv better and more easily
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef CONTRIB_GPGV_H
+#define CONTRIB_GPGV_H
+
+#include <apt-pkg/macros.h>
+
+#include <string>
+#include <vector>
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+#endif
+
+class FileFd;
+
+/** \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. 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 notable 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)
+ * @param statusfd is the fd given to gpgv as --status-fd
+ * @param fd is used as a pipe for the standard output of gpgv
+ * @param key is the specific one to be used instead of using all
+ */
+void ExecGPGV(std::string const &File, std::string const &FileSig,
+ int const &statusfd, int fd[2], std::string const &Key = "") APT_NORETURN;
+inline APT_NORETURN void ExecGPGV(std::string const &File, std::string const &FileSig,
+ int const &statusfd = -1) {
+ int fd[2];
+ ExecGPGV(File, FileSig, statusfd, fd);
+}
+
+/** \brief Split an inline signature into message and signature
+ *
+ * Takes a clear-signed message and puts the first signed message
+ * in the content file and all signatures following it into the
+ * second. Unsigned messages, additional messages as well as
+ * whitespaces are discarded. The resulting files are suitable to
+ * be checked with gpgv.
+ *
+ * If a FileFd pointers is NULL it will not be used and the content
+ * which would have been written to it is silently discarded.
+ *
+ * The content of the split files is undefined if the splitting was
+ * unsuccessful.
+ *
+ * Note that trying to split an unsigned file will fail, but
+ * not generate an error message.
+ *
+ * @param InFile is the clear-signed file
+ * @param ContentFile is the FileFd the message will be written to
+ * @param ContentHeader is a list of all required Amored Headers for the message
+ * @param SignatureFile is the FileFd all signatures will be written to
+ * @return true if the splitting was successful, false otherwise
+ */
+bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
+ std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile);
+
+/** \brief open a file which might be clear-signed
+ *
+ * This method tries to extract the (signed) message of a file.
+ * If the file isn't signed it will just open the given filename.
+ * Otherwise the message is extracted to a temporary file which
+ * will be opened instead.
+ *
+ * @param ClearSignedFileName is the name of the file to open
+ * @param[out] MessageFile is the FileFd in which the file will be opened
+ * @return true if opening was successful, otherwise false
+ */
+bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile);
+
+#endif
diff --git a/apt-pkg/contrib/hashes.cc b/apt-pkg/contrib/hashes.cc
new file mode 100644
index 0000000..98b92cc
--- /dev/null
+++ b/apt-pkg/contrib/hashes.cc
@@ -0,0 +1,408 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Hashes - Simple wrapper around the hash functions
+
+ This is just used to make building the methods simpler, this is the
+ only interface required..
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/sha2.h>
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+ /*}}}*/
+
+const char * HashString::_SupportedHashes[] =
+{
+ "SHA512", "SHA256", "SHA1", "MD5Sum", "Checksum-FileSize", NULL
+};
+
+HashString::HashString()
+{
+}
+
+HashString::HashString(std::string Type, std::string Hash) : Type(Type), Hash(Hash)
+{
+}
+
+HashString::HashString(std::string StringedHash) /*{{{*/
+{
+ if (StringedHash.find(":") == std::string::npos)
+ {
+ // legacy: md5sum without "MD5Sum:" prefix
+ if (StringedHash.size() == 32)
+ {
+ Type = "MD5Sum";
+ Hash = StringedHash;
+ }
+ if(_config->FindB("Debug::Hashes",false) == true)
+ std::clog << "HashString(string): invalid StringedHash " << StringedHash << std::endl;
+ return;
+ }
+ std::string::size_type pos = StringedHash.find(":");
+ Type = StringedHash.substr(0,pos);
+ Hash = StringedHash.substr(pos+1, StringedHash.size() - pos);
+
+ if(_config->FindB("Debug::Hashes",false) == true)
+ std::clog << "HashString(string): " << Type << " : " << Hash << std::endl;
+}
+ /*}}}*/
+bool HashString::VerifyFile(std::string filename) const /*{{{*/
+{
+ std::string fileHash = GetHashForFile(filename);
+
+ if(_config->FindB("Debug::Hashes",false) == true)
+ std::clog << "HashString::VerifyFile: got: " << fileHash << " expected: " << toStr() << std::endl;
+
+ return (fileHash == Hash);
+}
+ /*}}}*/
+bool HashString::FromFile(std::string filename) /*{{{*/
+{
+ // pick the strongest hash
+ if (Type == "")
+ Type = _SupportedHashes[0];
+
+ Hash = GetHashForFile(filename);
+ return true;
+}
+ /*}}}*/
+std::string HashString::GetHashForFile(std::string filename) const /*{{{*/
+{
+ std::string fileHash;
+
+ FileFd Fd(filename, FileFd::ReadOnly);
+ if(strcasecmp(Type.c_str(), "MD5Sum") == 0)
+ {
+ MD5Summation MD5;
+ MD5.AddFD(Fd);
+ fileHash = (std::string)MD5.Result();
+ }
+ else if (strcasecmp(Type.c_str(), "SHA1") == 0)
+ {
+ SHA1Summation SHA1;
+ SHA1.AddFD(Fd);
+ fileHash = (std::string)SHA1.Result();
+ }
+ else if (strcasecmp(Type.c_str(), "SHA256") == 0)
+ {
+ SHA256Summation SHA256;
+ SHA256.AddFD(Fd);
+ fileHash = (std::string)SHA256.Result();
+ }
+ else if (strcasecmp(Type.c_str(), "SHA512") == 0)
+ {
+ SHA512Summation SHA512;
+ SHA512.AddFD(Fd);
+ fileHash = (std::string)SHA512.Result();
+ }
+ else if (strcasecmp(Type.c_str(), "Checksum-FileSize") == 0)
+ strprintf(fileHash, "%llu", Fd.FileSize());
+ Fd.Close();
+
+ return fileHash;
+}
+ /*}}}*/
+const char** HashString::SupportedHashes() /*{{{*/
+{
+ return _SupportedHashes;
+}
+ /*}}}*/
+APT_PURE bool HashString::empty() const /*{{{*/
+{
+ return (Type.empty() || Hash.empty());
+}
+ /*}}}*/
+
+APT_PURE static bool IsConfigured(const char *name, const char *what)
+{
+ std::string option;
+ strprintf(option, "APT::Hashes::%s::%s", name, what);
+ return _config->FindB(option, false);
+}
+
+APT_PURE bool HashString::usable() const /*{{{*/
+{
+ return (
+ (Type != "Checksum-FileSize") &&
+ (Type != "MD5Sum") &&
+ (Type != "SHA1") &&
+ !IsConfigured(Type.c_str(), "Untrusted")
+ );
+}
+ /*}}}*/
+std::string HashString::toStr() const /*{{{*/
+{
+ return Type + ":" + Hash;
+}
+ /*}}}*/
+APT_PURE bool HashString::operator==(HashString const &other) const /*{{{*/
+{
+ return (strcasecmp(Type.c_str(), other.Type.c_str()) == 0 && Hash == other.Hash);
+}
+APT_PURE bool HashString::operator!=(HashString const &other) const
+{
+ return !(*this == other);
+}
+ /*}}}*/
+
+bool HashStringList::usable() const /*{{{*/
+{
+ if (empty() == true)
+ return false;
+ std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+ if (forcedType.empty() == true)
+ {
+ // See if there is at least one usable hash
+ for (auto const &hs: list)
+ if (hs.usable())
+ return true;
+ return false;
+ }
+ return find(forcedType) != NULL;
+}
+ /*}}}*/
+HashString const * HashStringList::find(char const * const type) const /*{{{*/
+{
+ if (type == NULL || type[0] == '\0')
+ {
+ std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+ if (forcedType.empty() == false)
+ return find(forcedType.c_str());
+ for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+ for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
+ if (strcasecmp(hs->HashType().c_str(), *t) == 0)
+ return &*hs;
+ return NULL;
+ }
+ for (std::vector<HashString>::const_iterator hs = list.begin(); hs != list.end(); ++hs)
+ if (strcasecmp(hs->HashType().c_str(), type) == 0)
+ return &*hs;
+ return NULL;
+}
+ /*}}}*/
+unsigned long long HashStringList::FileSize() const /*{{{*/
+{
+ HashString const * const hsf = find("Checksum-FileSize");
+ if (hsf == NULL)
+ return 0;
+ std::string const hv = hsf->HashValue();
+ return strtoull(hv.c_str(), NULL, 10);
+}
+ /*}}}*/
+bool HashStringList::FileSize(unsigned long long const Size) /*{{{*/
+{
+ return push_back(HashString("Checksum-FileSize", std::to_string(Size)));
+}
+ /*}}}*/
+bool HashStringList::supported(char const * const type) /*{{{*/
+{
+ for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+ if (strcasecmp(*t, type) == 0)
+ return true;
+ return false;
+}
+ /*}}}*/
+bool HashStringList::push_back(const HashString &hashString) /*{{{*/
+{
+ if (hashString.HashType().empty() == true ||
+ hashString.HashValue().empty() == true ||
+ supported(hashString.HashType().c_str()) == false)
+ return false;
+
+ // ensure that each type is added only once
+ HashString const * const hs = find(hashString.HashType().c_str());
+ if (hs != NULL)
+ return *hs == hashString;
+
+ list.push_back(hashString);
+ return true;
+}
+ /*}}}*/
+bool HashStringList::VerifyFile(std::string filename) const /*{{{*/
+{
+ if (usable() == false)
+ return false;
+
+ Hashes hashes(*this);
+ FileFd file(filename, FileFd::ReadOnly);
+ HashString const * const hsf = find("Checksum-FileSize");
+ if (hsf != NULL)
+ {
+ std::string fileSize;
+ strprintf(fileSize, "%llu", file.FileSize());
+ if (hsf->HashValue() != fileSize)
+ return false;
+ }
+ hashes.AddFD(file);
+ HashStringList const hsl = hashes.GetHashStringList();
+ return hsl == *this;
+}
+ /*}}}*/
+bool HashStringList::operator==(HashStringList const &other) const /*{{{*/
+{
+ std::string const forcedType = _config->Find("Acquire::ForceHash", "");
+ if (forcedType.empty() == false)
+ {
+ HashString const * const hs = find(forcedType);
+ HashString const * const ohs = other.find(forcedType);
+ if (hs == NULL || ohs == NULL)
+ return false;
+ return *hs == *ohs;
+ }
+ short matches = 0;
+ for (const_iterator hs = begin(); hs != end(); ++hs)
+ {
+ HashString const * const ohs = other.find(hs->HashType());
+ if (ohs == NULL)
+ continue;
+ if (*hs != *ohs)
+ return false;
+ ++matches;
+ }
+ if (matches == 0)
+ return false;
+ return true;
+}
+bool HashStringList::operator!=(HashStringList const &other) const
+{
+ return !(*this == other);
+}
+ /*}}}*/
+
+// PrivateHashes /*{{{*/
+class PrivateHashes {
+public:
+ unsigned long long FileSize;
+ unsigned int CalcHashes;
+
+ explicit PrivateHashes(unsigned int const CalcHashes) : FileSize(0), CalcHashes(CalcHashes) {}
+ explicit PrivateHashes(HashStringList const &Hashes) : FileSize(0) {
+ unsigned int calcHashes = Hashes.usable() ? 0 : ~0;
+ if (Hashes.find("MD5Sum") != NULL)
+ calcHashes |= Hashes::MD5SUM;
+ if (Hashes.find("SHA1") != NULL)
+ calcHashes |= Hashes::SHA1SUM;
+ if (Hashes.find("SHA256") != NULL)
+ calcHashes |= Hashes::SHA256SUM;
+ if (Hashes.find("SHA512") != NULL)
+ calcHashes |= Hashes::SHA512SUM;
+ CalcHashes = calcHashes;
+ }
+};
+ /*}}}*/
+// Hashes::Add* - Add the contents of data or FD /*{{{*/
+bool Hashes::Add(const unsigned char * const Data, unsigned long long const Size)
+{
+ if (Size == 0)
+ return true;
+ bool Res = true;
+APT_IGNORE_DEPRECATED_PUSH
+ if ((d->CalcHashes & MD5SUM) == MD5SUM)
+ Res &= MD5.Add(Data, Size);
+ if ((d->CalcHashes & SHA1SUM) == SHA1SUM)
+ Res &= SHA1.Add(Data, Size);
+ if ((d->CalcHashes & SHA256SUM) == SHA256SUM)
+ Res &= SHA256.Add(Data, Size);
+ if ((d->CalcHashes & SHA512SUM) == SHA512SUM)
+ Res &= SHA512.Add(Data, Size);
+APT_IGNORE_DEPRECATED_POP
+ d->FileSize += Size;
+ return Res;
+}
+bool Hashes::Add(const unsigned char * const Data, unsigned long long const Size, unsigned int const Hashes)
+{
+ d->CalcHashes = Hashes;
+ return Add(Data, Size);
+}
+bool Hashes::AddFD(int const Fd,unsigned long long Size)
+{
+ unsigned char Buf[64*64];
+ bool const ToEOF = (Size == UntilEOF);
+ while (Size != 0 || ToEOF)
+ {
+ decltype(Size) n = sizeof(Buf);
+ if (!ToEOF) n = std::min(Size, n);
+ ssize_t const Res = read(Fd,Buf,n);
+ if (Res < 0 || (!ToEOF && Res != (ssize_t) n)) // error, or short read
+ return false;
+ if (ToEOF && Res == 0) // EOF
+ break;
+ Size -= Res;
+ if (Add(Buf, Res) == false)
+ return false;
+ }
+ return true;
+}
+bool Hashes::AddFD(int const Fd,unsigned long long Size, unsigned int const Hashes)
+{
+ d->CalcHashes = Hashes;
+ return AddFD(Fd, Size);
+}
+bool Hashes::AddFD(FileFd &Fd,unsigned long long Size)
+{
+ unsigned char Buf[64*64];
+ bool const ToEOF = (Size == 0);
+ while (Size != 0 || ToEOF)
+ {
+ decltype(Size) n = sizeof(Buf);
+ if (!ToEOF) n = std::min(Size, n);
+ decltype(Size) a = 0;
+ if (Fd.Read(Buf, n, &a) == false) // error
+ return false;
+ if (ToEOF == false)
+ {
+ if (a != n) // short read
+ return false;
+ }
+ else if (a == 0) // EOF
+ break;
+ Size -= a;
+ if (Add(Buf, a) == false)
+ return false;
+ }
+ return true;
+}
+bool Hashes::AddFD(FileFd &Fd,unsigned long long Size, unsigned int const Hashes)
+{
+ d->CalcHashes = Hashes;
+ return AddFD(Fd, Size);
+}
+ /*}}}*/
+HashStringList Hashes::GetHashStringList()
+{
+ HashStringList hashes;
+APT_IGNORE_DEPRECATED_PUSH
+ if ((d->CalcHashes & MD5SUM) == MD5SUM)
+ hashes.push_back(HashString("MD5Sum", MD5.Result().Value()));
+ if ((d->CalcHashes & SHA1SUM) == SHA1SUM)
+ hashes.push_back(HashString("SHA1", SHA1.Result().Value()));
+ if ((d->CalcHashes & SHA256SUM) == SHA256SUM)
+ hashes.push_back(HashString("SHA256", SHA256.Result().Value()));
+ if ((d->CalcHashes & SHA512SUM) == SHA512SUM)
+ hashes.push_back(HashString("SHA512", SHA512.Result().Value()));
+APT_IGNORE_DEPRECATED_POP
+ hashes.FileSize(d->FileSize);
+ return hashes;
+}
+APT_IGNORE_DEPRECATED_PUSH
+Hashes::Hashes() : d(new PrivateHashes(~0)) { }
+Hashes::Hashes(unsigned int const Hashes) : d(new PrivateHashes(Hashes)) {}
+Hashes::Hashes(HashStringList const &Hashes) : d(new PrivateHashes(Hashes)) {}
+Hashes::~Hashes() { delete d; }
+APT_IGNORE_DEPRECATED_POP
diff --git a/apt-pkg/contrib/hashes.h b/apt-pkg/contrib/hashes.h
new file mode 100644
index 0000000..28bfd34
--- /dev/null
+++ b/apt-pkg/contrib/hashes.h
@@ -0,0 +1,250 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Hashes - Simple wrapper around the hash functions
+
+ This is just used to make building the methods simpler, this is the
+ only interface required..
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_HASHES_H
+#define APTPKG_HASHES_H
+
+#include <apt-pkg/macros.h>
+#include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/sha2.h>
+
+#include <cstring>
+#include <string>
+
+#ifndef APT_8_CLEANER_HEADERS
+using std::min;
+using std::vector;
+#endif
+#ifndef APT_10_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+#include <algorithm>
+#include <vector>
+#endif
+
+
+class FileFd;
+
+// helper class that contains hash function name
+// and hash
+class HashString
+{
+ protected:
+ std::string Type;
+ std::string Hash;
+ static const char * _SupportedHashes[10];
+
+ // internal helper
+ std::string GetHashForFile(std::string filename) const;
+
+ public:
+ HashString(std::string Type, std::string Hash);
+ HashString(std::string StringedHashString); // init from str as "type:hash"
+ HashString();
+
+ // get hash type used
+ std::string HashType() const { return Type; };
+ std::string HashValue() const { return Hash; };
+ APT_DEPRECATED_MSG("method was const-ified") std::string HashType() { return Type; };
+ APT_DEPRECATED_MSG("method was const-ified") std::string HashValue() { return Hash; };
+
+ // verify the given filename against the currently loaded hash
+ bool VerifyFile(std::string filename) const;
+
+ // generate a hash string from the given filename
+ bool FromFile(std::string filename);
+
+
+ // helper
+ std::string toStr() const; // convert to str as "type:hash"
+ bool empty() const;
+ bool usable() const;
+ bool operator==(HashString const &other) const;
+ bool operator!=(HashString const &other) const;
+
+ // return the list of hashes we support
+ static APT_PURE const char** SupportedHashes();
+};
+
+class HashStringList
+{
+ public:
+ /** find best hash if no specific one is requested
+ *
+ * @param type of the checksum to return, can be \b NULL
+ * @return If type is \b NULL (or the empty string) it will
+ * return the 'best' hash; otherwise the hash which was
+ * specifically requested. If no hash is found \b NULL will be returned.
+ */
+ HashString const * find(char const * const type) const;
+ HashString const * find(std::string const &type) const { return find(type.c_str()); }
+
+ /** finds the filesize hash and returns it as number
+ *
+ * @return beware: if the size isn't known we return \b 0 here,
+ * just like we would do for an empty file. If that is a problem
+ * for you have to get the size manually out of the list.
+ */
+ unsigned long long FileSize() const;
+
+ /** sets the filesize hash
+ *
+ * @param Size of the file
+ * @return @see #push_back
+ */
+ bool FileSize(unsigned long long const Size);
+
+ /** check if the given hash type is supported
+ *
+ * @param type to check
+ * @return true if supported, otherwise false
+ */
+ static APT_PURE bool supported(char const * const type);
+ /** add the given #HashString to the list
+ *
+ * @param hashString to add
+ * @return true if the hash is added because it is supported and
+ * not already a different hash of the same type included, otherwise false
+ */
+ bool push_back(const HashString &hashString);
+ /** @return size of the list of HashStrings */
+ size_t size() const { return list.size(); }
+
+ /** verify file against all hashes in the list
+ *
+ * @param filename to verify
+ * @return true if the file matches the hashsum, otherwise false
+ */
+ bool VerifyFile(std::string filename) const;
+
+ /** is the list empty ?
+ *
+ * @return \b true if the list is empty, otherwise \b false
+ */
+ bool empty() const { return list.empty(); }
+
+ /** has the list at least one good entry
+ *
+ * similar to #empty, but handles forced hashes.
+ *
+ * @return if no hash is forced, same result as #empty,
+ * if one is forced \b true if this has is available, \b false otherwise
+ */
+ bool usable() const;
+
+ typedef std::vector<HashString>::const_iterator const_iterator;
+
+ /** iterator to the first element */
+ const_iterator begin() const { return list.begin(); }
+
+ /** iterator to the end element */
+ const_iterator end() const { return list.end(); }
+
+ /** start fresh with a clear list */
+ void clear() { list.clear(); }
+
+ /** compare two HashStringList for similarity.
+ *
+ * Two lists are similar if at least one hashtype is in both lists
+ * and the hashsum matches. All hashes are checked by default,
+ * if one doesn't match false is returned regardless of how many
+ * matched before. If a hash is forced, only this hash is compared,
+ * all others are ignored.
+ */
+ bool operator==(HashStringList const &other) const;
+ bool operator!=(HashStringList const &other) const;
+
+ HashStringList() {}
+
+ // simplifying API-compatibility constructors
+ HashStringList(std::string const &hash) {
+ if (hash.empty() == false)
+ list.push_back(HashString(hash));
+ }
+ HashStringList(char const * const hash) {
+ if (hash != NULL && hash[0] != '\0')
+ list.push_back(HashString(hash));
+ }
+
+ private:
+ std::vector<HashString> list;
+};
+
+class PrivateHashes;
+class Hashes
+{
+ PrivateHashes * const d;
+
+ public:
+ /* those will disappear in the future as it is hard to add new ones this way.
+ * Use Add* to build the results and get them via GetHashStringList() instead */
+ APT_DEPRECATED_MSG("Use general .Add* and .GetHashStringList methods instead of hardcoding specific hashes") MD5Summation MD5;
+ APT_DEPRECATED_MSG("Use general .Add* and .GetHashStringList methods instead of hardcoding specific hashes") SHA1Summation SHA1;
+ APT_DEPRECATED_MSG("Use general .Add* and .GetHashStringList methods instead of hardcoding specific hashes") SHA256Summation SHA256;
+ APT_DEPRECATED_MSG("Use general .Add* and .GetHashStringList methods instead of hardcoding specific hashes") SHA512Summation SHA512;
+
+ static const int UntilEOF = 0;
+
+ bool Add(const unsigned char * const Data, unsigned long long const Size) APT_NONNULL(2);
+ APT_DEPRECATED_MSG("Construct accordingly instead of choosing hashes while adding") bool Add(const unsigned char * const Data, unsigned long long const Size, unsigned int const Hashes) APT_NONNULL(2);
+ inline bool Add(const char * const Data) APT_NONNULL(2)
+ {return Add(reinterpret_cast<unsigned char const *>(Data),strlen(Data));};
+ inline bool Add(const unsigned char * const Beg,const unsigned char * const End) APT_NONNULL(2,3)
+ {return Add(Beg,End-Beg);};
+
+ enum SupportedHashes { MD5SUM = (1 << 0), SHA1SUM = (1 << 1), SHA256SUM = (1 << 2),
+ SHA512SUM = (1 << 3) };
+ bool AddFD(int const Fd,unsigned long long Size = 0);
+ APT_DEPRECATED_MSG("Construct accordingly instead of choosing hashes while adding") bool AddFD(int const Fd,unsigned long long Size, unsigned int const Hashes);
+ bool AddFD(FileFd &Fd,unsigned long long Size = 0);
+ APT_DEPRECATED_MSG("Construct accordingly instead of choosing hashes while adding") bool AddFD(FileFd &Fd,unsigned long long Size, unsigned int const Hashes);
+
+ HashStringList GetHashStringList();
+
+APT_IGNORE_DEPRECATED_PUSH
+ /** create a Hashes object to calculate all supported hashes
+ *
+ * If ALL is too much, you can limit which Hashes are calculated
+ * with the following other constructors which mention explicitly
+ * which hashes to generate. */
+ Hashes();
+ /** @param Hashes bitflag composed of #SupportedHashes */
+ Hashes(unsigned int const Hashes);
+ /** @param Hashes is a list of hashes */
+ Hashes(HashStringList const &Hashes);
+ virtual ~Hashes();
+APT_IGNORE_DEPRECATED_POP
+
+ private:
+ APT_HIDDEN APT_PURE inline unsigned int boolsToFlag(bool const addMD5, bool const addSHA1, bool const addSHA256, bool const addSHA512)
+ {
+ unsigned int hashes = ~0;
+ if (addMD5 == false) hashes &= ~MD5SUM;
+ if (addSHA1 == false) hashes &= ~SHA1SUM;
+ if (addSHA256 == false) hashes &= ~SHA256SUM;
+ if (addSHA512 == false) hashes &= ~SHA512SUM;
+ return hashes;
+ }
+
+ public:
+APT_IGNORE_DEPRECATED_PUSH
+ APT_DEPRECATED_MSG("Construct accordingly instead of choosing hashes while adding") bool AddFD(int const Fd, unsigned long long Size, bool const addMD5,
+ bool const addSHA1, bool const addSHA256, bool const addSHA512) {
+ return AddFD(Fd, Size, boolsToFlag(addMD5, addSHA1, addSHA256, addSHA512));
+ };
+ APT_DEPRECATED_MSG("Construct accordingly instead of choosing hashes while adding") bool AddFD(FileFd &Fd, unsigned long long Size, bool const addMD5,
+ bool const addSHA1, bool const addSHA256, bool const addSHA512) {
+ return AddFD(Fd, Size, boolsToFlag(addMD5, addSHA1, addSHA256, addSHA512));
+ };
+APT_IGNORE_DEPRECATED_POP
+};
+
+#endif
diff --git a/apt-pkg/contrib/hashsum.cc b/apt-pkg/contrib/hashsum.cc
new file mode 100644
index 0000000..e8e86e9
--- /dev/null
+++ b/apt-pkg/contrib/hashsum.cc
@@ -0,0 +1,52 @@
+// Cryptographic API Base
+#include <config.h>
+
+#include <apt-pkg/fileutl.h>
+
+#include "hashsum_template.h"
+#include <algorithm>
+#include <unistd.h>
+
+// Summation::AddFD - Add content of file into the checksum /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool SummationImplementation::AddFD(int const Fd, unsigned long long Size) {
+ unsigned char Buf[64 * 64];
+ bool const ToEOF = (Size == 0);
+ while (Size != 0 || ToEOF)
+ {
+ unsigned long long n = sizeof(Buf);
+ if (!ToEOF) n = std::min(Size, n);
+ ssize_t const Res = read(Fd, Buf, n);
+ if (Res < 0 || (!ToEOF && Res != (ssize_t) n)) // error, or short read
+ return false;
+ if (ToEOF && Res == 0) // EOF
+ break;
+ Size -= Res;
+ Add(Buf,Res);
+ }
+ return true;
+}
+bool SummationImplementation::AddFD(FileFd &Fd, unsigned long long Size) {
+ unsigned char Buf[64 * 64];
+ bool const ToEOF = (Size == 0);
+ while (Size != 0 || ToEOF)
+ {
+ unsigned long long n = sizeof(Buf);
+ if (!ToEOF) n = std::min(Size, n);
+ unsigned long long a = 0;
+ if (Fd.Read(Buf, n, &a) == false) // error
+ return false;
+ if (ToEOF == false)
+ {
+ if (a != n) // short read
+ return false;
+ }
+ else if (a == 0) // EOF
+ break;
+ Size -= a;
+ Add(Buf, a);
+ }
+ return true;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/hashsum_template.h b/apt-pkg/contrib/hashsum_template.h
new file mode 100644
index 0000000..f11fc2f
--- /dev/null
+++ b/apt-pkg/contrib/hashsum_template.h
@@ -0,0 +1,141 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ HashSumValueTemplate - Generic Storage for a hash value
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_HASHSUM_TEMPLATE_H
+#define APTPKG_HASHSUM_TEMPLATE_H
+
+#include <cstring>
+#include <string>
+#ifdef APT_PKG_EXPOSE_STRING_VIEW
+#include <apt-pkg/string_view.h>
+#endif
+
+#include <apt-pkg/strutl.h>
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+#include <algorithm>
+#include <stdint.h>
+#endif
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+using std::min;
+#endif
+
+class FileFd;
+
+template<int N>
+class HashSumValue
+{
+ unsigned char Sum[N/8];
+
+ public:
+
+ // Accessors
+ bool operator ==(const HashSumValue &rhs) const
+ {
+ return memcmp(Sum,rhs.Sum,sizeof(Sum)) == 0;
+ }
+ bool operator !=(const HashSumValue &rhs) const
+ {
+ return memcmp(Sum,rhs.Sum,sizeof(Sum)) != 0;
+ }
+
+ std::string Value() const
+ {
+ char Conv[16] =
+ { '0','1','2','3','4','5','6','7','8','9','a','b',
+ 'c','d','e','f'
+ };
+ char Result[((N/8)*2)+1];
+ Result[(N/8)*2] = 0;
+
+ // Convert each char into two letters
+ int J = 0;
+ int I = 0;
+ for (; I != (N/8)*2; J++,I += 2)
+ {
+ Result[I] = Conv[Sum[J] >> 4];
+ Result[I + 1] = Conv[Sum[J] & 0xF];
+ }
+ return std::string(Result);
+ }
+
+ inline void Value(unsigned char S[N/8])
+ {
+ for (int I = 0; I != sizeof(Sum); ++I)
+ S[I] = Sum[I];
+ }
+
+ inline operator std::string() const
+ {
+ return Value();
+ }
+
+#ifdef APT_PKG_EXPOSE_STRING_VIEW
+ APT_HIDDEN bool Set(APT::StringView Str)
+ {
+ return Hex2Num(Str,Sum,sizeof(Sum));
+ }
+#else
+ bool Set(std::string Str)
+ {
+ return Hex2Num(Str,Sum,sizeof(Sum));
+ }
+#endif
+ inline void Set(unsigned char S[N/8])
+ {
+ for (int I = 0; I != sizeof(Sum); ++I)
+ Sum[I] = S[I];
+ }
+
+ explicit HashSumValue(std::string const &Str)
+ {
+ memset(Sum,0,sizeof(Sum));
+ Set(Str);
+ }
+#ifdef APT_PKG_EXPOSE_STRING_VIEW
+ APT_HIDDEN explicit HashSumValue(APT::StringView const &Str)
+ {
+ memset(Sum,0,sizeof(Sum));
+ Set(Str);
+ }
+ APT_HIDDEN explicit HashSumValue(const char *Str)
+ {
+ memset(Sum,0,sizeof(Sum));
+ Set(Str);
+ }
+#endif
+ HashSumValue()
+ {
+ memset(Sum,0,sizeof(Sum));
+ }
+};
+
+class SummationImplementation
+{
+ public:
+ virtual bool Add(const unsigned char *inbuf, unsigned long long inlen) APT_NONNULL(2) = 0;
+ inline bool Add(const char *inbuf, unsigned long long const inlen) APT_NONNULL(2)
+ { return Add(reinterpret_cast<const unsigned char *>(inbuf), inlen); }
+
+ inline bool Add(const unsigned char *Data) APT_NONNULL(2)
+ { return Add(Data, strlen(reinterpret_cast<const char *>(Data))); }
+ inline bool Add(const char *Data) APT_NONNULL(2)
+ { return Add(reinterpret_cast<const unsigned char *>(Data), strlen(Data)); }
+
+ inline bool Add(const unsigned char *Beg, const unsigned char *End) APT_NONNULL(2,3)
+ { return Add(Beg, End - Beg); }
+ inline bool Add(const char *Beg, const char *End) APT_NONNULL(2,3)
+ { return Add(reinterpret_cast<const unsigned char *>(Beg), End - Beg); }
+
+ bool AddFD(int Fd, unsigned long long Size = 0);
+ bool AddFD(FileFd &Fd, unsigned long long Size = 0);
+};
+
+#endif
diff --git a/apt-pkg/contrib/macros.h b/apt-pkg/contrib/macros.h
new file mode 100644
index 0000000..57d3f6c
--- /dev/null
+++ b/apt-pkg/contrib/macros.h
@@ -0,0 +1,173 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Macros Header - Various useful macro definitions
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Brian C. White.
+
+ ##################################################################### */
+ /*}}}*/
+// Private header
+#ifndef MACROS_H
+#define MACROS_H
+
+// MIN_VAL(SINT16) will return -0x8000 and MAX_VAL(SINT16) = 0x7FFF
+#define MIN_VAL(t) (((t)(-1) > 0) ? (t)( 0) : (t)(((1L<<(sizeof(t)*8-1)) )))
+#define MAX_VAL(t) (((t)(-1) > 0) ? (t)(-1) : (t)(((1L<<(sizeof(t)*8-1))-1)))
+
+// Min/Max functions
+#if !defined(MIN)
+#if defined(__HIGHC__)
+#define MIN(x,y) _min(x,y)
+#define MAX(x,y) _max(x,y)
+#endif
+
+// GNU C++ has a min/max operator <coolio>
+#if defined(__GNUG__)
+#define MIN(A,B) ((A) <? (B))
+#define MAX(A,B) ((A) >? (B))
+#endif
+
+/* Templates tend to mess up existing code that uses min/max because of the
+ strict matching requirements */
+#if !defined(MIN)
+#define MIN(A,B) ((A) < (B)?(A):(B))
+#define MAX(A,B) ((A) > (B)?(A):(B))
+#endif
+#endif
+
+/* Bound functions, bound will return the value b within the limits a-c
+ bounv will change b so that it is within the limits of a-c. */
+#define _bound(a,b,c) MIN(c,MAX(b,a))
+#define _boundv(a,b,c) b = _bound(a,b,c)
+#define ABS(a) (((a) < (0)) ?-(a) : (a))
+
+/* Useful count macro, use on an array of things and it will return the
+ number of items in the array */
+#define _count(a) (sizeof(a)/sizeof(a[0]))
+
+// Flag Macros
+#define FLAG(f) (1L << (f))
+#define SETFLAG(v,f) ((v) |= FLAG(f))
+#define CLRFLAG(v,f) ((v) &=~FLAG(f))
+#define CHKFLAG(v,f) ((v) & FLAG(f) ? true : false)
+
+#ifdef __GNUC__
+#define APT_GCC_VERSION (__GNUC__ << 8 | __GNUC_MINOR__)
+#else
+#define APT_GCC_VERSION 0
+#endif
+
+/* likely() and unlikely() can be used to mark boolean expressions
+ as (not) likely true which will help the compiler to optimise */
+#if APT_GCC_VERSION >= 0x0300
+ #define likely(x) __builtin_expect (!!(x), 1)
+ #define unlikely(x) __builtin_expect (!!(x), 0)
+#else
+ #define likely(x) (x)
+ #define unlikely(x) (x)
+#endif
+
+#if APT_GCC_VERSION >= 0x0300
+ #define APT_DEPRECATED __attribute__ ((deprecated))
+ #define APT_DEPRECATED_MSG(X) __attribute__ ((deprecated(X)))
+ // __attribute__((const)) is too dangerous for us, we end up using it wrongly
+ #define APT_CONST __attribute__((pure))
+ #define APT_PURE __attribute__((pure))
+ #define APT_NORETURN __attribute__((noreturn))
+ #define APT_PRINTF(n) __attribute__((format(printf, n, n + 1)))
+ #define APT_WEAK __attribute__((weak));
+#else
+ #define APT_DEPRECATED
+ #define APT_DEPRECATED_MSG
+ #define APT_CONST
+ #define APT_PURE
+ #define APT_NORETURN
+ #define APT_PRINTF(n)
+ #define APT_WEAK
+#endif
+
+#if APT_GCC_VERSION > 0x0302
+ #define APT_NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
+ #define APT_MUSTCHECK __attribute__((warn_unused_result))
+#else
+ #define APT_NONNULL(...)
+ #define APT_MUSTCHECK
+#endif
+
+#if APT_GCC_VERSION >= 0x0400
+ #define APT_SENTINEL __attribute__((sentinel))
+ #define APT_PUBLIC __attribute__ ((visibility ("default")))
+ #define APT_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+ #define APT_SENTINEL
+ #define APT_PUBLIC
+ #define APT_HIDDEN
+#endif
+
+// cold functions are unlikely() to be called
+#if APT_GCC_VERSION >= 0x0403
+ #define APT_COLD __attribute__ ((__cold__))
+ #define APT_HOT __attribute__ ((__hot__))
+#else
+ #define APT_COLD
+ #define APT_HOT
+#endif
+
+#ifndef APT_10_CLEANER_HEADERS
+#if APT_GCC_VERSION >= 0x0300
+ #define __must_check __attribute__ ((warn_unused_result))
+ #define __deprecated __attribute__ ((deprecated))
+ #define __attrib_const __attribute__ ((__const__))
+ #define __like_printf(n) __attribute__((format(printf, n, n + 1)))
+#else
+ #define __must_check /* no warn_unused_result */
+ #define __deprecated /* no deprecated */
+ #define __attrib_const /* no const attribute */
+ #define __like_printf(n) /* no like-printf */
+#endif
+#if APT_GCC_VERSION >= 0x0403
+ #define __cold __attribute__ ((__cold__))
+ #define __hot __attribute__ ((__hot__))
+#else
+ #define __cold /* no cold marker */
+ #define __hot /* no hot marker */
+#endif
+#endif
+
+#if __GNUC__ >= 4
+ #define APT_IGNORE_DEPRECATED_PUSH \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+ #define APT_IGNORE_DEPRECATED_POP \
+ _Pragma("GCC diagnostic pop")
+ /* gcc has various problems with this shortcut, so prefer the long form */
+ #define APT_IGNORE_DEPRECATED(XXX) \
+ APT_IGNORE_DEPRECATED_PUSH \
+ XXX \
+ APT_IGNORE_DEPRECATED_POP
+#else
+ #define APT_IGNORE_DEPRECATED_PUSH
+ #define APT_IGNORE_DEPRECATED_POP
+ #define APT_IGNORE_DEPRECATED(XXX) XXX
+#endif
+
+#if __cplusplus >= 201103L
+ #define APT_OVERRIDE override
+#else
+ #define APT_OVERRIDE /* no c++11 standard */
+#endif
+
+// These lines are extracted by the makefiles and the buildsystem
+// Increasing MAJOR or MINOR results in the need of recompiling all
+// reverse-dependencies of libapt-pkg against the new SONAME.
+// Non-ABI-Breaks should only increase RELEASE number.
+// See also buildlib/libversion.mak
+#define APT_PKG_MAJOR 5
+#define APT_PKG_MINOR 0
+#define APT_PKG_RELEASE 2
+#define APT_PKG_ABI ((APT_PKG_MAJOR * 100) + APT_PKG_MINOR)
+
+#endif
diff --git a/apt-pkg/contrib/md5.cc b/apt-pkg/contrib/md5.cc
new file mode 100644
index 0000000..c3b5299
--- /dev/null
+++ b/apt-pkg/contrib/md5.cc
@@ -0,0 +1,279 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ MD5Sum - MD5 Message Digest Algorithm.
+
+ This code implements the MD5 message-digest algorithm. The algorithm is
+ due to Ron Rivest. This code was written by Colin Plumb in 1993, no
+ copyright is claimed. This code is in the public domain; do with it what
+ you wish.
+
+ Equivalent code is available from RSA Data Security, Inc. This code has
+ been tested against that, and is equivalent, except that you don't need to
+ include two pages of legalese with every copy.
+
+ To compute the message digest of a chunk of bytes, instantiate the class,
+ and repeatedly call one of the Add() members. When finished the Result
+ method will return the Hash and finalize the value.
+
+ Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ definitions; now uses stuff from dpkg's config.h.
+ - Ian Jackson <ijackson@nyx.cs.du.edu>.
+
+ Changed into a C++ interface and made work with APT's config.h.
+ - Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
+
+ Still in the public domain.
+
+ The classes use arrays of char that are a specific size. We cast those
+ arrays to uint8_t's and go from there. This allows us to advoid using
+ the uncommon inttypes.h in a public header or internally newing memory.
+ In theory if C9x becomes nicely accepted
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/md5.h>
+
+#include <stdint.h>
+#include <string.h>
+ /*}}}*/
+
+// byteSwap - Swap bytes in a buffer /*{{{*/
+// ---------------------------------------------------------------------
+/* Swap n 32 bit longs in given buffer */
+#ifdef WORDS_BIGENDIAN
+static void byteSwap(uint32_t *buf, unsigned words)
+{
+ uint8_t *p = (uint8_t *)buf;
+
+ do
+ {
+ *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
+ ((unsigned)p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+}
+#else
+#define byteSwap(buf,words)
+#endif
+ /*}}}*/
+// MD5Transform - Alters an existing MD5 hash /*{{{*/
+// ---------------------------------------------------------------------
+/* The core of the MD5 algorithm, this alters an existing MD5 hash to
+ reflect the addition of 16 longwords of new data. Add blocks
+ the data and converts bytes into longwords for this routine. */
+
+// The four core functions - F1 is optimized somewhat
+// #define F1(x, y, z) (x & y | ~x & z)
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+// This is the central step in the MD5 algorithm.
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+ /*}}}*/
+// MD5Summation::MD5Summation - Initialize the summer /*{{{*/
+// ---------------------------------------------------------------------
+/* This assigns the deep magic initial values */
+MD5Summation::MD5Summation()
+{
+ uint32_t *buf = (uint32_t *)Buf;
+ uint32_t *bytes = (uint32_t *)Bytes;
+
+ buf[0] = 0x67452301;
+ buf[1] = 0xefcdab89;
+ buf[2] = 0x98badcfe;
+ buf[3] = 0x10325476;
+
+ bytes[0] = 0;
+ bytes[1] = 0;
+ Done = false;
+}
+ /*}}}*/
+// MD5Summation::Add - 'Add' a data set to the hash /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool MD5Summation::Add(const unsigned char *data,unsigned long long len)
+{
+ if (Done == true)
+ return false;
+ if (len == 0)
+ return true;
+
+ uint32_t *buf = (uint32_t *)Buf;
+ uint32_t *bytes = (uint32_t *)Bytes;
+ uint32_t *in = (uint32_t *)In;
+
+ // Update byte count and carry (this could be done with a long long?)
+ uint32_t t = bytes[0];
+ if ((bytes[0] = t + len) < t)
+ bytes[1]++;
+
+ // Space available (at least 1)
+ t = 64 - (t & 0x3f);
+ if (t > len)
+ {
+ memcpy((unsigned char *)in + 64 - t,data,len);
+ return true;
+ }
+
+ // First chunk is an odd size
+ memcpy((unsigned char *)in + 64 - t,data,t);
+ byteSwap(in, 16);
+ MD5Transform(buf,in);
+ data += t;
+ len -= t;
+
+ // Process data in 64-byte chunks
+ while (len >= 64)
+ {
+ memcpy(in,data,64);
+ byteSwap(in,16);
+ MD5Transform(buf,in);
+ data += 64;
+ len -= 64;
+ }
+
+ // Handle any remaining bytes of data.
+ memcpy(in,data,len);
+
+ return true;
+}
+ /*}}}*/
+// MD5Summation::Result - Returns the value of the sum /*{{{*/
+// ---------------------------------------------------------------------
+/* Because this must add in the last bytes of the series it prevents anyone
+ from calling add after. */
+MD5SumValue MD5Summation::Result()
+{
+ uint32_t *buf = (uint32_t *)Buf;
+ uint32_t *bytes = (uint32_t *)Bytes;
+ uint32_t *in = (uint32_t *)In;
+
+ if (Done == false)
+ {
+ // Number of bytes in In
+ int count = bytes[0] & 0x3f;
+ unsigned char *p = (unsigned char *)in + count;
+
+ // Set the first char of padding to 0x80. There is always room.
+ *p++ = 0x80;
+
+ // Bytes of padding needed to make 56 bytes (-8..55)
+ count = 56 - 1 - count;
+
+ // Padding forces an extra block
+ if (count < 0)
+ {
+ memset(p,0,count + 8);
+ byteSwap(in, 16);
+ MD5Transform(buf,in);
+ p = (unsigned char *)in;
+ count = 56;
+ }
+
+ memset(p, 0, count);
+ byteSwap(in, 14);
+
+ // Append length in bits and transform
+ in[14] = bytes[0] << 3;
+ in[15] = bytes[1] << 3 | bytes[0] >> 29;
+ MD5Transform(buf,in);
+ byteSwap(buf,4);
+ Done = true;
+ }
+
+ MD5SumValue V;
+ V.Set((unsigned char *)buf);
+ return V;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/md5.h b/apt-pkg/contrib/md5.h
new file mode 100644
index 0000000..38b6c68
--- /dev/null
+++ b/apt-pkg/contrib/md5.h
@@ -0,0 +1,58 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ MD5SumValue - Storage for a MD5Sum
+ MD5Summation - MD5 Message Digest Algorithm.
+
+ This is a C++ interface to a set of MD5Sum functions. The class can
+ store a MD5Sum in 16 bytes of memory.
+
+ A MD5Sum is used to generate a (hopefully) unique 16 byte number for a
+ block of data. This can be used to guard against corruption of a file.
+ MD5 should not be used for tamper protection, use SHA or something more
+ secure.
+
+ There are two classes because computing a MD5 is not a continual
+ operation unless 64 byte blocks are used. Also the summation requires an
+ extra 18*4 bytes to operate.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_MD5_H
+#define APTPKG_MD5_H
+
+#include <stdint.h>
+
+#include "hashsum_template.h"
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <algorithm>
+#include <cstring>
+#include <string>
+#endif
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+using std::min;
+#endif
+
+typedef HashSumValue<128> MD5SumValue;
+
+class MD5Summation : public SummationImplementation
+{
+ uint32_t Buf[4];
+ unsigned char Bytes[2*4];
+ unsigned char In[16*4];
+ bool Done;
+
+ public:
+
+ bool Add(const unsigned char *inbuf, unsigned long long inlen) APT_OVERRIDE APT_NONNULL(2);
+ using SummationImplementation::Add;
+
+ MD5SumValue Result();
+
+ MD5Summation();
+};
+
+#endif
diff --git a/apt-pkg/contrib/mmap.cc b/apt-pkg/contrib/mmap.cc
new file mode 100644
index 0000000..ee6a21c
--- /dev/null
+++ b/apt-pkg/contrib/mmap.cc
@@ -0,0 +1,502 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ MMap Class - Provides 'real' mmap or a faked mmap using read().
+
+ MMap cover class.
+
+ Some broken versions of glibc2 (libc6) have a broken definition
+ of mmap that accepts a char * -- all other systems (and libc5) use
+ void *. We can't safely do anything here that would be portable, so
+ libc6 generates warnings -- which should be errors, g++ isn't properly
+ strict.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#define _DEFAULT_SOURCE
+#include <config.h>
+
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/mmap.h>
+
+#include <cstring>
+#include <string>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// MMap::MMap - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0),
+ Base(nullptr), SyncToFd(nullptr)
+{
+ if ((Flags & NoImmMap) != NoImmMap)
+ Map(F);
+}
+ /*}}}*/
+// MMap::MMap - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0),
+ Base(nullptr), SyncToFd(nullptr)
+{
+}
+ /*}}}*/
+// MMap::~MMap - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+MMap::~MMap()
+{
+ Close();
+}
+ /*}}}*/
+// MMap::Map - Perform the mapping /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool MMap::Map(FileFd &Fd)
+{
+ iSize = Fd.Size();
+
+ // Set the permissions.
+ int Prot = PROT_READ;
+ int Map = MAP_SHARED;
+ if ((Flags & ReadOnly) != ReadOnly)
+ Prot |= PROT_WRITE;
+ if ((Flags & Public) != Public)
+ Map = MAP_PRIVATE;
+
+ if (iSize == 0)
+ return _error->Error(_("Can't mmap an empty file"));
+
+ // We can't mmap compressed fd's directly, so we need to read it completely
+ if (Fd.IsCompressed() == true)
+ {
+ if ((Flags & ReadOnly) != ReadOnly)
+ return _error->Error("Compressed file %s can only be mapped readonly", Fd.Name().c_str());
+ Base = malloc(iSize);
+ if (unlikely(Base == nullptr))
+ return _error->Errno("MMap-compressed-malloc", _("Couldn't make mmap of %llu bytes"), iSize);
+ SyncToFd = new FileFd();
+ if (Fd.Seek(0L) == false || Fd.Read(Base, iSize) == false)
+ return _error->Error("Compressed file %s can't be read into mmap", Fd.Name().c_str());
+ return true;
+ }
+
+ // Map it.
+ Base = (Flags & Fallback) ? MAP_FAILED : mmap(0,iSize,Prot,Map,Fd.Fd(),0);
+ if (Base == MAP_FAILED)
+ {
+ if (errno == ENODEV || errno == EINVAL || (Flags & Fallback))
+ {
+ // The filesystem doesn't support this particular kind of mmap.
+ // So we allocate a buffer and read the whole file into it.
+ if ((Flags & ReadOnly) == ReadOnly)
+ {
+ // for readonly, we don't need sync, so make it simple
+ Base = malloc(iSize);
+ if (unlikely(Base == nullptr))
+ return _error->Errno("MMap-malloc", _("Couldn't make mmap of %llu bytes"), iSize);
+ SyncToFd = new FileFd();
+ return Fd.Read(Base, iSize);
+ }
+ // FIXME: Writing to compressed fd's ?
+ int const dupped_fd = dup(Fd.Fd());
+ if (dupped_fd == -1)
+ return _error->Errno("mmap", _("Couldn't duplicate file descriptor %i"), Fd.Fd());
+
+ Base = calloc(iSize, 1);
+ if (unlikely(Base == nullptr))
+ return _error->Errno("MMap-calloc", _("Couldn't make mmap of %llu bytes"), iSize);
+ SyncToFd = new FileFd (dupped_fd);
+ if (!SyncToFd->Seek(0L) || !SyncToFd->Read(Base, iSize))
+ return false;
+ }
+ else
+ return _error->Errno("MMap-mmap", _("Couldn't make mmap of %llu bytes"), iSize);
+ }
+
+ return true;
+}
+ /*}}}*/
+// MMap::Close - Close the map /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool MMap::Close(bool DoSync)
+{
+ if ((Flags & UnMapped) == UnMapped || validData() == false || iSize == 0)
+ return true;
+
+ if (DoSync == true)
+ Sync();
+
+ if (SyncToFd != NULL)
+ {
+ free(Base);
+ delete SyncToFd;
+ SyncToFd = NULL;
+ }
+ else
+ {
+ if (munmap((char *)Base, iSize) != 0)
+ _error->WarningE("mmap", _("Unable to close mmap"));
+ }
+
+ iSize = 0;
+ Base = 0;
+ return true;
+}
+ /*}}}*/
+// MMap::Sync - Synchronize the map with the disk /*{{{*/
+// ---------------------------------------------------------------------
+/* This is done in synchronous mode - the docs indicate that this will
+ not return till all IO is complete */
+bool MMap::Sync()
+{
+ if ((Flags & UnMapped) == UnMapped)
+ return true;
+
+ if ((Flags & ReadOnly) != ReadOnly)
+ {
+ if (SyncToFd != NULL)
+ {
+ if (!SyncToFd->Seek(0) || !SyncToFd->Write(Base, iSize))
+ return false;
+ }
+ else
+ {
+#ifdef _POSIX_SYNCHRONIZED_IO
+ if (msync((char *)Base, iSize, MS_SYNC) < 0)
+ return _error->Errno("msync", _("Unable to synchronize mmap"));
+#endif
+ }
+ }
+ return true;
+}
+ /*}}}*/
+// MMap::Sync - Synchronize a section of the file to disk /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool MMap::Sync(unsigned long Start,unsigned long Stop)
+{
+ if ((Flags & UnMapped) == UnMapped)
+ return true;
+
+ if ((Flags & ReadOnly) != ReadOnly)
+ {
+ if (SyncToFd != 0)
+ {
+ if (!SyncToFd->Seek(0) ||
+ !SyncToFd->Write (((char *)Base)+Start, Stop-Start))
+ return false;
+ }
+ else
+ {
+#ifdef _POSIX_SYNCHRONIZED_IO
+ unsigned long long const PSize = sysconf(_SC_PAGESIZE);
+ if (msync((char *)Base+(Start/PSize)*PSize, Stop - Start, MS_SYNC) < 0)
+ return _error->Errno("msync", _("Unable to synchronize mmap"));
+#endif
+ }
+ }
+ return true;
+}
+ /*}}}*/
+
+// DynamicMMap::DynamicMMap - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long const &Workspace,
+ unsigned long const &Grow, unsigned long const &Limit) :
+ MMap(F,Flags | NoImmMap), Fd(&F), WorkSpace(Workspace),
+ GrowFactor(Grow), Limit(Limit)
+{
+ // disable Moveable if we don't grow
+ if (Grow == 0)
+ this->Flags &= ~Moveable;
+
+#ifndef __linux__
+ // kfreebsd doesn't have mremap, so we use the fallback
+ if ((this->Flags & Moveable) == Moveable)
+ this->Flags |= Fallback;
+#endif
+
+ unsigned long long EndOfFile = Fd->Size();
+ if (EndOfFile > WorkSpace)
+ WorkSpace = EndOfFile;
+ else if(WorkSpace > 0)
+ {
+ Fd->Seek(WorkSpace - 1);
+ char C = 0;
+ Fd->Write(&C,sizeof(C));
+ }
+
+ Map(F);
+ iSize = EndOfFile;
+}
+ /*}}}*/
+// DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
+// ---------------------------------------------------------------------
+/* We try here to use mmap to reserve some space - this is much more
+ cooler than the fallback solution to simply allocate a char array
+ and could come in handy later than we are able to grow such an mmap */
+DynamicMMap::DynamicMMap(unsigned long Flags,unsigned long const &WorkSpace,
+ unsigned long const &Grow, unsigned long const &Limit) :
+ MMap(Flags | NoImmMap | UnMapped), Fd(0), WorkSpace(WorkSpace),
+ GrowFactor(Grow), Limit(Limit)
+{
+ // disable Moveable if we don't grow
+ if (Grow == 0)
+ this->Flags &= ~Moveable;
+
+#ifndef __linux__
+ // kfreebsd doesn't have mremap, so we use the fallback
+ if ((this->Flags & Moveable) == Moveable)
+ this->Flags |= Fallback;
+#endif
+
+#ifdef _POSIX_MAPPED_FILES
+ if ((this->Flags & Fallback) != Fallback) {
+ // Set the permissions.
+ int Prot = PROT_READ;
+#ifdef MAP_ANONYMOUS
+ int Map = MAP_PRIVATE | MAP_ANONYMOUS;
+#else
+ int Map = MAP_PRIVATE | MAP_ANON;
+#endif
+ if ((this->Flags & ReadOnly) != ReadOnly)
+ Prot |= PROT_WRITE;
+ if ((this->Flags & Public) == Public)
+#ifdef MAP_ANONYMOUS
+ Map = MAP_SHARED | MAP_ANONYMOUS;
+#else
+ Map = MAP_SHARED | MAP_ANON;
+#endif
+
+ // use anonymous mmap() to get the memory
+ Base = (unsigned char*) mmap(0, WorkSpace, Prot, Map, -1, 0);
+
+ if(Base == MAP_FAILED)
+ _error->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace);
+
+ iSize = 0;
+ return;
+ }
+#endif
+ // fallback to a static allocated space
+ Base = calloc(WorkSpace, 1);
+ iSize = 0;
+}
+ /*}}}*/
+// DynamicMMap::~DynamicMMap - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* We truncate the file to the size of the memory data set */
+DynamicMMap::~DynamicMMap()
+{
+ if (Fd == 0)
+ {
+ if (validData() == false)
+ return;
+#ifdef _POSIX_MAPPED_FILES
+ munmap(Base, WorkSpace);
+#else
+ free(Base);
+#endif
+ return;
+ }
+
+ unsigned long long EndOfFile = iSize;
+ iSize = WorkSpace;
+ Close(false);
+ if(ftruncate(Fd->Fd(),EndOfFile) < 0)
+ _error->Errno("ftruncate", _("Failed to truncate file"));
+}
+ /*}}}*/
+// DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
+// ---------------------------------------------------------------------
+/* This allocates a block of memory aligned to the given size */
+unsigned long DynamicMMap::RawAllocate(unsigned long long Size,unsigned long Aln)
+{
+ unsigned long long Result = iSize;
+ if (Aln != 0)
+ Result += Aln - (iSize%Aln);
+
+ iSize = Result + Size;
+
+ // try to grow the buffer
+ while(Result + Size > WorkSpace)
+ {
+ if(!Grow())
+ {
+ _error->Fatal(_("Dynamic MMap ran out of room. Please increase the size "
+ "of APT::Cache-Start. Current value: %lu. (man 5 apt.conf)"), WorkSpace);
+ return 0;
+ }
+ }
+ return Result;
+}
+ /*}}}*/
+// DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
+// ---------------------------------------------------------------------
+/* This allocates an Item of size ItemSize so that it is aligned to its
+ size in the file. */
+unsigned long DynamicMMap::Allocate(unsigned long ItemSize)
+{
+ if (unlikely(ItemSize == 0))
+ {
+ _error->Fatal("Can't allocate an item of size zero");
+ return 0;
+ }
+
+ // Look for a matching pool entry
+ Pool *I;
+ Pool *Empty = 0;
+ for (I = Pools; I != Pools + PoolCount; ++I)
+ {
+ if (I->ItemSize == 0)
+ Empty = I;
+ if (I->ItemSize == ItemSize)
+ break;
+ }
+ // No pool is allocated, use an unallocated one
+ if (I == Pools + PoolCount)
+ {
+ // Woops, we ran out, the calling code should allocate more.
+ if (Empty == 0)
+ {
+ _error->Error("Ran out of allocation pools");
+ return 0;
+ }
+
+ I = Empty;
+ I->ItemSize = ItemSize;
+ I->Count = 0;
+ }
+
+ unsigned long Result = 0;
+ // Out of space, allocate some more
+ if (I->Count == 0)
+ {
+ const unsigned long size = 20*1024;
+ I->Count = size/ItemSize;
+ Pool* oldPools = Pools;
+ _error->PushToStack();
+ Result = RawAllocate(size,ItemSize);
+ bool const newError = _error->PendingError();
+ _error->MergeWithStack();
+ if (Pools != oldPools)
+ I += Pools - oldPools;
+
+ // Does the allocation failed ?
+ if (Result == 0 && newError)
+ return 0;
+ I->Start = Result;
+ }
+ else
+ Result = I->Start;
+
+ I->Count--;
+ I->Start += ItemSize;
+ return Result/ItemSize;
+}
+ /*}}}*/
+// DynamicMMap::WriteString - Write a string to the file /*{{{*/
+// ---------------------------------------------------------------------
+/* Strings are aligned to 16 bytes */
+unsigned long DynamicMMap::WriteString(const char *String,
+ unsigned long Len)
+{
+ if (Len == std::numeric_limits<unsigned long>::max())
+ Len = strlen(String);
+
+ _error->PushToStack();
+ unsigned long Result = RawAllocate(Len+1+sizeof(uint16_t),sizeof(uint16_t));
+ bool const newError = _error->PendingError();
+ _error->MergeWithStack();
+
+ if (Base == NULL || (Result == 0 && newError))
+ return 0;
+
+ if (Len >= std::numeric_limits<uint16_t>::max())
+ abort();
+
+ uint16_t LenToWrite = Len;
+ memcpy((char *)Base + Result, &LenToWrite, sizeof(LenToWrite));
+ Result += + sizeof(LenToWrite);
+
+ memcpy((char *)Base + Result,String,Len);
+ ((char *)Base)[Result + Len] = 0;
+ return Result;
+}
+ /*}}}*/
+// DynamicMMap::Grow - Grow the mmap /*{{{*/
+// ---------------------------------------------------------------------
+/* This method is a wrapper around different methods to (try to) grow
+ a mmap (or our char[]-fallback). Encounterable environments:
+ 1. Moveable + !Fallback + linux -> mremap with MREMAP_MAYMOVE
+ 2. Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
+ 3. Moveable + Fallback -> realloc
+ 4. !Moveable + !Fallback + linux -> mremap alone - which will fail in 99,9%
+ 5. !Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
+ 6. !Moveable + Fallback -> not possible
+ [ While Moveable and Fallback stands for the equally named flags and
+ "linux" indicates a linux kernel instead of a freebsd kernel. ]
+ So what you can see here is, that a MMAP which want to be growable need
+ to be moveable to have a real chance but that this method will at least try
+ the nearly impossible 4 to grow it before it finally give up: Never say never. */
+bool DynamicMMap::Grow() {
+ if (Limit != 0 && WorkSpace >= Limit)
+ return _error->Error(_("Unable to increase the size of the MMap as the "
+ "limit of %lu bytes is already reached."), Limit);
+ if (GrowFactor <= 0)
+ return _error->Error(_("Unable to increase size of the MMap as automatic growing is disabled by user."));
+
+ unsigned long long const newSize = WorkSpace + GrowFactor;
+
+ if(Fd != 0) {
+ Fd->Seek(newSize - 1);
+ char C = 0;
+ Fd->Write(&C,sizeof(C));
+ }
+
+ unsigned long const poolOffset = Pools - ((Pool*) Base);
+
+ if ((Flags & Fallback) != Fallback) {
+#if defined(_POSIX_MAPPED_FILES) && defined(__linux__)
+ #ifdef MREMAP_MAYMOVE
+
+ if ((Flags & Moveable) == Moveable)
+ Base = mremap(Base, WorkSpace, newSize, MREMAP_MAYMOVE);
+ else
+ #endif
+ Base = mremap(Base, WorkSpace, newSize, 0);
+
+ if(Base == MAP_FAILED)
+ return false;
+#else
+ return false;
+#endif
+ } else {
+ if ((Flags & Moveable) != Moveable)
+ return false;
+
+ Base = realloc(Base, newSize);
+ if (Base == NULL)
+ return false;
+ else
+ /* Set new memory to 0 */
+ memset((char*)Base + WorkSpace, 0, newSize - WorkSpace);
+ }
+
+ Pools =(Pool*) Base + poolOffset;
+ WorkSpace = newSize;
+ return true;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/mmap.h b/apt-pkg/contrib/mmap.h
new file mode 100644
index 0000000..63d54d2
--- /dev/null
+++ b/apt-pkg/contrib/mmap.h
@@ -0,0 +1,120 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ MMap Class - Provides 'real' mmap or a faked mmap using read().
+
+ The purpose of this code is to provide a generic way for clients to
+ access the mmap function. In environments that do not support mmap
+ from file fd's this function will use read and normal allocated
+ memory.
+
+ Writing to a public mmap will always fully commit all changes when the
+ class is deleted. Ie it will rewrite the file, unless it is readonly
+
+ The DynamicMMap class is used to help the on-disk data structure
+ generators. It provides a large allocated workspace and members
+ to allocate space from the workspace in an efficient fashion.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_MMAP_H
+#define PKGLIB_MMAP_H
+
+#include <string>
+#include <limits>
+
+#include <sys/mman.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+using std::string;
+#endif
+
+class FileFd;
+
+/* This should be a 32 bit type, larger types use too much ram and smaller
+ types are too small. Where ever possible 'unsigned long' should be used
+ instead of this internal type */
+typedef unsigned int map_ptrloc;
+
+class MMap
+{
+ protected:
+
+ unsigned long Flags;
+ unsigned long long iSize;
+ void *Base;
+
+ // In case mmap can not be used, we keep a dup of the file
+ // descriptor that should have been mmaped so that we can write to
+ // the file in Sync().
+ FileFd *SyncToFd;
+
+ bool Map(FileFd &Fd);
+ bool Close(bool DoSync = true);
+
+ public:
+
+ enum OpenFlags {NoImmMap = (1<<0),Public = (1<<1),ReadOnly = (1<<2),
+ UnMapped = (1<<3), Moveable = (1<<4), Fallback = (1 << 5)};
+
+ // Simple accessors
+ inline operator void *() {return Base;};
+ inline void *Data() {return Base;};
+ inline unsigned long long Size() {return iSize;};
+ inline void AddSize(unsigned long long const size) {iSize += size;};
+ inline bool validData() const { return Base != MAP_FAILED && Base != 0; };
+
+ // File manipulators
+ bool Sync();
+ bool Sync(unsigned long Start,unsigned long Stop);
+
+ MMap(FileFd &F,unsigned long Flags);
+ MMap(unsigned long Flags);
+ virtual ~MMap();
+};
+
+class DynamicMMap : public MMap
+{
+ public:
+
+ // This is the allocation pool structure
+ struct Pool
+ {
+ unsigned long ItemSize;
+ unsigned long Start;
+ unsigned long Count;
+ };
+
+ protected:
+
+ FileFd *Fd;
+ unsigned long WorkSpace;
+ unsigned long const GrowFactor;
+ unsigned long const Limit;
+ Pool *Pools;
+ unsigned int PoolCount;
+
+ bool Grow();
+
+ public:
+
+ // Allocation
+ unsigned long RawAllocate(unsigned long long Size,unsigned long Aln = 0);
+ unsigned long Allocate(unsigned long ItemSize);
+ unsigned long WriteString(const char *String,unsigned long Len = std::numeric_limits<unsigned long>::max());
+ inline unsigned long WriteString(const std::string &S) {return WriteString(S.c_str(),S.length());};
+ void UsePools(Pool &P,unsigned int Count) {Pools = &P; PoolCount = Count;};
+
+ DynamicMMap(FileFd &F,unsigned long Flags,unsigned long const &WorkSpace = 2*1024*1024,
+ unsigned long const &Grow = 1024*1024, unsigned long const &Limit = 0);
+ DynamicMMap(unsigned long Flags,unsigned long const &WorkSpace = 2*1024*1024,
+ unsigned long const &Grow = 1024*1024, unsigned long const &Limit = 0);
+ virtual ~DynamicMMap();
+};
+
+#endif
diff --git a/apt-pkg/contrib/netrc.cc b/apt-pkg/contrib/netrc.cc
new file mode 100644
index 0000000..48114ba
--- /dev/null
+++ b/apt-pkg/contrib/netrc.cc
@@ -0,0 +1,195 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ netrc file parser - returns the login and password of a give host in
+ a specified netrc-type file
+
+ Originally written by Daniel Stenberg, <daniel@haxx.se>, et al. and
+ placed into the Public Domain, do with it what you will.
+
+ ##################################################################### */
+ /*}}}*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <iostream>
+
+#include "netrc.h"
+
+/* Get user and password from .netrc when given a machine name */
+bool MaybeAddAuth(FileFd &NetRCFile, URI &Uri)
+{
+ if (Uri.User.empty() == false || Uri.Password.empty() == false)
+ return true;
+ if (NetRCFile.IsOpen() == false || NetRCFile.Failed())
+ return false;
+ auto const Debug = _config->FindB("Debug::Acquire::netrc", false);
+
+ std::string lookfor;
+ if (Uri.Port != 0)
+ strprintf(lookfor, "%s:%i%s", Uri.Host.c_str(), Uri.Port, Uri.Path.c_str());
+ else
+ lookfor.append(Uri.Host).append(Uri.Path);
+
+ enum
+ {
+ NO,
+ MACHINE,
+ GOOD_MACHINE,
+ LOGIN,
+ PASSWORD
+ } active_token = NO;
+ std::string line;
+ while (NetRCFile.Eof() == false || line.empty() == false)
+ {
+ if (line.empty())
+ {
+ if (NetRCFile.ReadLine(line) == false)
+ break;
+ else if (line.empty())
+ continue;
+ }
+ auto tokenend = line.find_first_of("\t ");
+ std::string token;
+ if (tokenend != std::string::npos)
+ {
+ token = line.substr(0, tokenend);
+ line.erase(0, tokenend + 1);
+ }
+ else
+ std::swap(line, token);
+ if (token.empty())
+ continue;
+ switch (active_token)
+ {
+ case NO:
+ if (token == "machine")
+ active_token = MACHINE;
+ break;
+ case MACHINE:
+ if (token.find('/') == std::string::npos)
+ {
+ if (Uri.Port != 0 && Uri.Host == token)
+ active_token = GOOD_MACHINE;
+ else if (lookfor.compare(0, lookfor.length() - Uri.Path.length(), token) == 0)
+ active_token = GOOD_MACHINE;
+ else
+ active_token = NO;
+ }
+ else
+ {
+ if (APT::String::Startswith(lookfor, token))
+ active_token = GOOD_MACHINE;
+ else
+ active_token = NO;
+ }
+ break;
+ case GOOD_MACHINE:
+ if (token == "login")
+ active_token = LOGIN;
+ else if (token == "password")
+ active_token = PASSWORD;
+ else if (token == "machine")
+ {
+ if (Debug)
+ std::clog << "MaybeAddAuth: Found matching host adding '" << Uri.User << "' and '" << Uri.Password << "' for "
+ << (std::string)Uri << " from " << NetRCFile.Name() << std::endl;
+ return true;
+ }
+ break;
+ case LOGIN:
+ std::swap(Uri.User, token);
+ active_token = GOOD_MACHINE;
+ break;
+ case PASSWORD:
+ std::swap(Uri.Password, token);
+ active_token = GOOD_MACHINE;
+ break;
+ }
+ }
+ if (active_token == GOOD_MACHINE)
+ {
+ if (Debug)
+ std::clog << "MaybeAddAuth: Found matching host adding '" << Uri.User << "' and '" << Uri.Password << "' for "
+ << (std::string)Uri << " from " << NetRCFile.Name() << std::endl;
+ return true;
+ }
+ else if (active_token == NO)
+ {
+ if (Debug)
+ std::clog << "MaybeAddAuth: Found no matching host for "
+ << (std::string)Uri << " from " << NetRCFile.Name() << std::endl;
+ return true;
+ }
+ else if (Debug)
+ {
+ std::clog << "MaybeAddAuth: Found no matching host (syntax error: token:";
+ switch (active_token)
+ {
+ case NO: std::clog << "NO"; break;
+ case MACHINE: std::clog << "MACHINE"; break;
+ case GOOD_MACHINE: std::clog << "GOOD_MACHINE"; break;
+ case LOGIN: std::clog << "LOGIN"; break;
+ case PASSWORD: std::clog << "PASSWORD"; break;
+ }
+ std::clog << ") for " << (std::string)Uri << " from " << NetRCFile.Name() << std::endl;
+ }
+ return false;
+}
+
+void maybe_add_auth(URI &Uri, std::string NetRCFile)
+{
+ if (FileExists(NetRCFile) == false)
+ return;
+ FileFd fd;
+ if (fd.Open(NetRCFile, FileFd::ReadOnly))
+ MaybeAddAuth(fd, Uri);
+}
+
+/* Check if we are authorized. */
+bool IsAuthorized(pkgCache::PkgFileIterator const I, std::vector<std::unique_ptr<FileFd>> &authconfs)
+{
+ if (authconfs.empty())
+ {
+ _error->PushToStack();
+ auto const netrc = _config->FindFile("Dir::Etc::netrc");
+ if (not netrc.empty())
+ {
+ authconfs.emplace_back(new FileFd());
+ authconfs.back()->Open(netrc, FileFd::ReadOnly);
+ }
+
+ auto const netrcparts = _config->FindDir("Dir::Etc::netrcparts");
+ if (not netrcparts.empty())
+ {
+ for (auto const &netrc : GetListOfFilesInDir(netrcparts, "conf", true, true))
+ {
+ authconfs.emplace_back(new FileFd());
+ authconfs.back()->Open(netrc, FileFd::ReadOnly);
+ }
+ }
+ _error->RevertToStack();
+ }
+
+ // FIXME: Use the full base url
+ URI uri(std::string("http://") + I.Site() + "/");
+ for (auto &authconf : authconfs)
+ {
+ if (not authconf->IsOpen())
+ continue;
+ if (not authconf->Seek(0))
+ continue;
+
+ MaybeAddAuth(*authconf, uri);
+
+ if (not uri.User.empty() || not uri.Password.empty())
+ return true;
+ }
+
+ return false;
+}
diff --git a/apt-pkg/contrib/netrc.h b/apt-pkg/contrib/netrc.h
new file mode 100644
index 0000000..80d95ac
--- /dev/null
+++ b/apt-pkg/contrib/netrc.h
@@ -0,0 +1,39 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ netrc file parser - returns the login and password of a give host in
+ a specified netrc-type file
+
+ Originally written by Daniel Stenberg, <daniel@haxx.se>, et al. and
+ placed into the Public Domain, do with it what you will.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef NETRC_H
+#define NETRC_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <apt-pkg/macros.h>
+#include <apt-pkg/pkgcache.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/strutl.h>
+#endif
+
+#ifndef APT_15_CLEANER_HEADERS
+#define DOT_CHAR "."
+#define DIR_CHAR "/"
+#endif
+
+class URI;
+class FileFd;
+
+APT_DEPRECATED_MSG("Use FileFd-based MaybeAddAuth instead")
+void maybe_add_auth(URI &Uri, std::string NetRCFile);
+bool MaybeAddAuth(FileFd &NetRCFile, URI &Uri);
+bool IsAuthorized(pkgCache::PkgFileIterator const I, std::vector<std::unique_ptr<FileFd>> &authconfs) APT_HIDDEN;
+#endif
diff --git a/apt-pkg/contrib/progress.cc b/apt-pkg/contrib/progress.cc
new file mode 100644
index 0000000..806bd47
--- /dev/null
+++ b/apt-pkg/contrib/progress.cc
@@ -0,0 +1,221 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ OpProgress - Operation Progress
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/progress.h>
+
+#include <cmath>
+#include <chrono>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// OpProgress::OpProgress - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+OpProgress::OpProgress() : Current(0), Total(0), Size(0), SubTotal(1),
+ LastPercent(0), Percent(0)
+{
+ memset(&LastTime,0,sizeof(LastTime));
+}
+ /*}}}*/
+// OpProgress::Progress - Sub progress with no state change /*{{{*/
+// ---------------------------------------------------------------------
+/* Current is the Base Overall progress in units of Total. Cur is the sub
+ progress in units of SubTotal. Size is a scaling factor that says what
+ percent of Total SubTotal is. */
+void OpProgress::Progress(unsigned long long Cur)
+{
+ if (Total == 0 || Size == 0 || SubTotal == 0)
+ Percent = 0;
+ else
+ Percent = (Current + Cur/((double)SubTotal)*Size)*100.0/Total;
+ Update();
+}
+ /*}}}*/
+// OpProgress::OverallProgress - Set the overall progress /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void OpProgress::OverallProgress(unsigned long long Current, unsigned long long Total,
+ unsigned long long Size,const string &Op)
+{
+ this->Current = Current;
+ this->Total = Total;
+ this->Size = Size;
+ this->Op = Op;
+ SubOp = string();
+ if (Total == 0)
+ Percent = 0;
+ else
+ Percent = Current*100.0/Total;
+ Update();
+}
+ /*}}}*/
+// OpProgress::SubProgress - Set the sub progress state /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void OpProgress::SubProgress(unsigned long long SubTotal,const string &Op,
+ float const Percent)
+{
+ this->SubTotal = SubTotal;
+ if (Op.empty() == false)
+ SubOp = Op;
+ if (Total == 0 || Percent == 0)
+ this->Percent = 0;
+ else if (Percent != -1)
+ this->Percent = this->Current += (Size*Percent)/SubTotal;
+ else
+ this->Percent = Current*100.0/Total;
+ Update();
+}
+ /*}}}*/
+// OpProgress::CheckChange - See if the display should be updated /*{{{*/
+// ---------------------------------------------------------------------
+/* Progress calls are made so frequently that if every one resulted in
+ an update the display would be swamped and the system much slower.
+ This provides an upper bound on the update rate. */
+bool OpProgress::CheckChange(float Interval)
+{
+ // New major progress indication
+ if (Op != LastOp)
+ {
+ MajorChange = true;
+ LastOp = Op;
+ return true;
+ }
+ MajorChange = false;
+
+ if (SubOp != LastSubOp)
+ {
+ LastSubOp = SubOp;
+ return true;
+ }
+
+ if (std::lround(LastPercent) == std::lround(Percent))
+ return false;
+
+ LastPercent = Percent;
+
+ if (Interval == 0)
+ return false;
+
+ // Check time delta
+ auto const Now = std::chrono::steady_clock::now().time_since_epoch();
+ auto const Now_sec = std::chrono::duration_cast<std::chrono::seconds>(Now);
+ auto const Now_usec = std::chrono::duration_cast<std::chrono::microseconds>(Now - Now_sec);
+ struct timeval NowTime = { Now_sec.count(), Now_usec.count() };
+
+ std::chrono::duration<decltype(Interval)> Delta =
+ std::chrono::seconds(NowTime.tv_sec - LastTime.tv_sec) +
+ std::chrono::microseconds(NowTime.tv_sec - LastTime.tv_usec);
+
+ if (Delta.count() < Interval)
+ return false;
+ LastTime = NowTime;
+ return true;
+}
+ /*}}}*/
+// OpTextProgress::OpTextProgress - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+OpTextProgress::OpTextProgress(Configuration &Config) :
+ NoUpdate(false), NoDisplay(false), LastLen(0)
+{
+ if (Config.FindI("quiet",0) >= 1 || Config.FindB("quiet::NoUpdate", false) == true)
+ NoUpdate = true;
+ if (Config.FindI("quiet",0) >= 2 || Config.FindB("quiet::NoProgress", false) == true)
+ NoDisplay = true;
+}
+ /*}}}*/
+// OpTextProgress::Done - Clean up the display /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void OpTextProgress::Done()
+{
+ if (NoUpdate == false && OldOp.empty() == false)
+ {
+ char S[300];
+ if (_error->PendingError() == true)
+ snprintf(S,sizeof(S),_("%c%s... Error!"),'\r',OldOp.c_str());
+ else
+ snprintf(S,sizeof(S),_("%c%s... Done"),'\r',OldOp.c_str());
+ Write(S);
+ cout << endl;
+ OldOp = string();
+ }
+
+ if (NoUpdate == true && NoDisplay == false && OldOp.empty() == false)
+ {
+ OldOp = string();
+ cout << endl;
+ }
+}
+ /*}}}*/
+// OpTextProgress::Update - Simple text spinner /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void OpTextProgress::Update()
+{
+ if (CheckChange((NoUpdate == true?0:0.7)) == false)
+ return;
+
+ // No percent spinner
+ if (NoUpdate == true)
+ {
+ if (MajorChange == false)
+ return;
+ if (NoDisplay == false)
+ {
+ if (OldOp.empty() == false)
+ cout << endl;
+ OldOp = "a";
+ cout << Op << _("...") << flush;
+ }
+
+ return;
+ }
+
+ // Erase the old text and 'log' the event
+ char S[300];
+ if (MajorChange == true && OldOp.empty() == false)
+ {
+ snprintf(S,sizeof(S),"\r%s",OldOp.c_str());
+ Write(S);
+ cout << endl;
+ }
+
+ // Print the spinner
+ snprintf(S,sizeof(S),_("%c%s... %u%%"),'\r',Op.c_str(),(unsigned int)Percent);
+ Write(S);
+
+ OldOp = Op;
+}
+ /*}}}*/
+// OpTextProgress::Write - Write the progress string /*{{{*/
+// ---------------------------------------------------------------------
+/* This space fills the end to overwrite the previous text */
+void OpTextProgress::Write(const char *S)
+{
+ cout << S;
+ for (unsigned int I = strlen(S); I < LastLen; I++)
+ cout << ' ';
+ cout << '\r' << flush;
+ LastLen = strlen(S);
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/progress.h b/apt-pkg/contrib/progress.h
new file mode 100644
index 0000000..4406a38
--- /dev/null
+++ b/apt-pkg/contrib/progress.h
@@ -0,0 +1,89 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ OpProgress - Operation Progress
+
+ This class allows lengthy operations to communicate their progress
+ to the GUI. The progress model is simple and is not designed to handle
+ the complex case of the multi-activity acquire class.
+
+ The model is based on the concept of an overall operation consisting
+ of a series of small sub operations. Each sub operation has it's own
+ completion status and the overall operation has it's completion status.
+ The units of the two are not mixed and are completely independent.
+
+ The UI is expected to subclass this to provide the visuals to the user.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_PROGRESS_H
+#define PKGLIB_PROGRESS_H
+
+#include <apt-pkg/macros.h>
+#include <string>
+#include <sys/time.h>
+
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+#endif
+
+class Configuration;
+class OpProgress
+{
+ unsigned long long Current;
+ unsigned long long Total;
+ unsigned long long Size;
+ unsigned long long SubTotal;
+ float LastPercent;
+
+ // Change reduction code
+ struct timeval LastTime;
+ std::string LastOp;
+ std::string LastSubOp;
+
+ protected:
+
+ std::string Op;
+ std::string SubOp;
+ float Percent;
+
+ bool MajorChange;
+
+ bool CheckChange(float Interval = 0.7);
+ virtual void Update() {};
+
+ public:
+
+ void Progress(unsigned long long Current);
+ void SubProgress(unsigned long long SubTotal, const std::string &Op = "", float const Percent = -1);
+ void OverallProgress(unsigned long long Current,unsigned long long Total,
+ unsigned long long Size,const std::string &Op);
+ virtual void Done() {};
+
+ OpProgress();
+ virtual ~OpProgress() {};
+};
+
+class OpTextProgress : public OpProgress
+{
+ protected:
+
+ std::string OldOp;
+ bool NoUpdate;
+ bool NoDisplay;
+ unsigned long LastLen;
+ virtual void Update() APT_OVERRIDE;
+ void Write(const char *S);
+
+ public:
+
+ virtual void Done() APT_OVERRIDE;
+
+ OpTextProgress(bool NoUpdate = false) : NoUpdate(NoUpdate),
+ NoDisplay(false), LastLen(0) {};
+ OpTextProgress(Configuration &Config);
+ virtual ~OpTextProgress() {Done();};
+};
+
+#endif
diff --git a/apt-pkg/contrib/proxy.cc b/apt-pkg/contrib/proxy.cc
new file mode 100644
index 0000000..6dc3b06
--- /dev/null
+++ b/apt-pkg/contrib/proxy.cc
@@ -0,0 +1,97 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Proxy - Proxy related functions
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "proxy.h"
+ /*}}}*/
+
+// AutoDetectProxy - auto detect proxy /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+static std::vector<std::string> CompatibleProxies(URI const &URL)
+{
+ if (URL.Access == "http" || URL.Access == "https")
+ return {"http", "https", "socks5h"};
+ return {URL.Access};
+}
+
+bool AutoDetectProxy(URI &URL)
+{
+ // we support both http/https debug options
+ bool Debug = _config->FindB("Debug::Acquire::"+URL.Access,false);
+
+ // the user already explicitly set a proxy for this host
+ if(_config->Find("Acquire::"+URL.Access+"::proxy::"+URL.Host, "") != "")
+ return true;
+
+ // option is "Acquire::http::Proxy-Auto-Detect" but we allow the old
+ // name without the dash ("-")
+ std::string AutoDetectProxyCmd = _config->Find("Acquire::"+URL.Access+"::Proxy-Auto-Detect",
+ _config->Find("Acquire::"+URL.Access+"::ProxyAutoDetect"));
+
+ if (AutoDetectProxyCmd.empty())
+ return true;
+
+ if (Debug)
+ std::clog << "Using auto proxy detect command: " << AutoDetectProxyCmd << std::endl;
+
+ if (faccessat(AT_FDCWD, AutoDetectProxyCmd.c_str(), R_OK | X_OK, AT_EACCESS) != 0)
+ return _error->Errno("access", "ProxyAutoDetect command '%s' can not be executed!", AutoDetectProxyCmd.c_str());
+
+ std::string const urlstring = URL;
+ std::vector<const char *> Args;
+ Args.push_back(AutoDetectProxyCmd.c_str());
+ Args.push_back(urlstring.c_str());
+ Args.push_back(nullptr);
+ FileFd PipeFd;
+ pid_t Child;
+ if (Popen(&Args[0], PipeFd, Child, FileFd::ReadOnly, false, true) == false)
+ return _error->Error("ProxyAutoDetect command '%s' failed!", AutoDetectProxyCmd.c_str());
+ char buf[512];
+ bool const goodread = PipeFd.ReadLine(buf, sizeof(buf)) != nullptr;
+ PipeFd.Close();
+ if (ExecWait(Child, "ProxyAutoDetect") == false)
+ return false;
+ // no output means the detector has no idea which proxy to use
+ // and apt will use the generic proxy settings
+ if (goodread == false)
+ return true;
+ auto const cleanedbuf = _strstrip(buf);
+ // We warn about this as the implementor probably meant to use DIRECT instead
+ if (cleanedbuf[0] == '\0')
+ {
+ _error->Warning("ProxyAutoDetect command returned an empty line");
+ return true;
+ }
+
+ if (Debug)
+ std::clog << "auto detect command returned: '" << cleanedbuf << "'" << std::endl;
+
+ auto compatibleTypes = CompatibleProxies(URL);
+ bool compatible = strcmp(cleanedbuf, "DIRECT") == 0 ||
+ compatibleTypes.end() != std::find_if(compatibleTypes.begin(),
+ compatibleTypes.end(), [cleanedbuf](std::string &compat) {
+ return strstr(cleanedbuf, compat.c_str()) == cleanedbuf;
+ });
+
+ if (compatible)
+ _config->Set("Acquire::"+URL.Access+"::proxy::"+URL.Host, cleanedbuf);
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/proxy.h b/apt-pkg/contrib/proxy.h
new file mode 100644
index 0000000..2cbcd07
--- /dev/null
+++ b/apt-pkg/contrib/proxy.h
@@ -0,0 +1,16 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Proxy - Proxy operations
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_PROXY_H
+#define PKGLIB_PROXY_H
+
+class URI;
+bool AutoDetectProxy(URI &URL);
+
+
+#endif
diff --git a/apt-pkg/contrib/sha1.cc b/apt-pkg/contrib/sha1.cc
new file mode 100644
index 0000000..bf0b9d6
--- /dev/null
+++ b/apt-pkg/contrib/sha1.cc
@@ -0,0 +1,273 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ SHA1 - SHA-1 Secure Hash Algorithm.
+
+ This file is a Public Domain wrapper for the Public Domain SHA1
+ calculation code that is at it's end.
+
+ The algorithm was originally implemented by
+ Steve Reid <sreid@sea-to-sky.net> and later modified by
+ James H. Brown <jbrown@burgoyne.com>.
+
+ Modifications for APT were done by Alfredo K. Kojima and Jason
+ Gunthorpe.
+
+ Still in the public domain.
+
+ Test Vectors (from FIPS PUB 180-1)
+ "abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+
+ #####################################################################
+ */
+ /*}}} */
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/sha1.h>
+
+#include <stdint.h>
+#include <string.h>
+ /*}}}*/
+
+// SHA1Transform - Alters an existing SHA-1 hash /*{{{*/
+// ---------------------------------------------------------------------
+/* The core of the SHA-1 algorithm. This alters an existing SHA-1 hash to
+ reflect the addition of 16 longwords of new data. Other routines convert
+ incoming stream data into 16 long word chunks for this routine */
+
+#define rol(value,bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1),R2,R3,R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+static void SHA1Transform(uint32_t state[5],uint8_t const buffer[64])
+{
+ uint32_t a,b,c,d,e;
+ typedef union
+ {
+ uint8_t c[64];
+ uint32_t l[16];
+ }
+ CHAR64LONG16;
+ CHAR64LONG16 workspace, *block;
+
+ block = &workspace;
+ memcpy(block,buffer,sizeof(workspace));
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e,0);
+ R0(e,a,b,c,d,1);
+ R0(d,e,a,b,c,2);
+ R0(c,d,e,a,b,3);
+ R0(b,c,d,e,a,4);
+ R0(a,b,c,d,e,5);
+ R0(e,a,b,c,d,6);
+ R0(d,e,a,b,c,7);
+ R0(c,d,e,a,b,8);
+ R0(b,c,d,e,a,9);
+ R0(a,b,c,d,e,10);
+ R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12);
+ R0(c,d,e,a,b,13);
+ R0(b,c,d,e,a,14);
+ R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16);
+ R1(d,e,a,b,c,17);
+ R1(c,d,e,a,b,18);
+ R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20);
+ R2(e,a,b,c,d,21);
+ R2(d,e,a,b,c,22);
+ R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24);
+ R2(a,b,c,d,e,25);
+ R2(e,a,b,c,d,26);
+ R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28);
+ R2(b,c,d,e,a,29);
+ R2(a,b,c,d,e,30);
+ R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32);
+ R2(c,d,e,a,b,33);
+ R2(b,c,d,e,a,34);
+ R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36);
+ R2(d,e,a,b,c,37);
+ R2(c,d,e,a,b,38);
+ R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40);
+ R3(e,a,b,c,d,41);
+ R3(d,e,a,b,c,42);
+ R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44);
+ R3(a,b,c,d,e,45);
+ R3(e,a,b,c,d,46);
+ R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48);
+ R3(b,c,d,e,a,49);
+ R3(a,b,c,d,e,50);
+ R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52);
+ R3(c,d,e,a,b,53);
+ R3(b,c,d,e,a,54);
+ R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56);
+ R3(d,e,a,b,c,57);
+ R3(c,d,e,a,b,58);
+ R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60);
+ R4(e,a,b,c,d,61);
+ R4(d,e,a,b,c,62);
+ R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64);
+ R4(a,b,c,d,e,65);
+ R4(e,a,b,c,d,66);
+ R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68);
+ R4(b,c,d,e,a,69);
+ R4(a,b,c,d,e,70);
+ R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72);
+ R4(c,d,e,a,b,73);
+ R4(b,c,d,e,a,74);
+ R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76);
+ R4(d,e,a,b,c,77);
+ R4(c,d,e,a,b,78);
+ R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+}
+ /*}}}*/
+
+// SHA1Summation::SHA1Summation - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+SHA1Summation::SHA1Summation()
+{
+ uint32_t *state = (uint32_t *)State;
+ uint32_t *count = (uint32_t *)Count;
+
+ /* SHA1 initialization constants */
+ state[0] = 0x67452301;
+ state[1] = 0xEFCDAB89;
+ state[2] = 0x98BADCFE;
+ state[3] = 0x10325476;
+ state[4] = 0xC3D2E1F0;
+ count[0] = 0;
+ count[1] = 0;
+ Done = false;
+}
+ /*}}}*/
+// SHA1Summation::Result - Return checksum value /*{{{*/
+// ---------------------------------------------------------------------
+/* Add() may not be called after this */
+SHA1SumValue SHA1Summation::Result()
+{
+ uint32_t *state = (uint32_t *)State;
+ uint32_t *count = (uint32_t *)Count;
+
+ // Apply the padding
+ if (Done == false)
+ {
+ unsigned char finalcount[8];
+
+ for (unsigned i = 0; i < 8; i++)
+ {
+ // Endian independent
+ finalcount[i] = (unsigned char) ((count[(i >= 4 ? 0 : 1)]
+ >> ((3 - (i & 3)) * 8)) & 255);
+ }
+
+ Add((unsigned char *) "\200",1);
+ while ((count[0] & 504) != 448)
+ Add((unsigned char *) "\0",1);
+
+ Add(finalcount,8); /* Should cause a SHA1Transform() */
+
+ }
+
+ Done = true;
+
+ // Transfer over the result
+ SHA1SumValue Value;
+ unsigned char res[20];
+ for (unsigned i = 0; i < 20; i++)
+ {
+ res[i] = (unsigned char)
+ ((state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ Value.Set(res);
+ return Value;
+}
+ /*}}}*/
+// SHA1Summation::Add - Adds content of buffer into the checksum /*{{{*/
+// ---------------------------------------------------------------------
+/* May not be called after Result() is called */
+bool SHA1Summation::Add(const unsigned char *data,unsigned long long len)
+{
+ if (Done)
+ return false;
+ if (len == 0)
+ return true;
+
+ uint32_t *state = (uint32_t *)State;
+ uint32_t *count = (uint32_t *)Count;
+ uint8_t *buffer = (uint8_t *)Buffer;
+ uint32_t i,j;
+
+ j = (count[0] >> 3) & 63;
+ if ((count[0] += len << 3) < (len << 3))
+ count[1]++;
+ count[1] += (len >> 29);
+ if ((j + len) > 63)
+ {
+ memcpy(&buffer[j],data,(i = 64 - j));
+ SHA1Transform(state,buffer);
+ for (; i + 63 < len; i += 64)
+ {
+ SHA1Transform(state,&data[i]);
+ }
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&buffer[j],&data[i],len - i);
+
+ return true;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/sha1.h b/apt-pkg/contrib/sha1.h
new file mode 100644
index 0000000..dffb950
--- /dev/null
+++ b/apt-pkg/contrib/sha1.h
@@ -0,0 +1,47 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ SHA1SumValue - Storage for a SHA-1 hash.
+ SHA1Summation - SHA-1 Secure Hash Algorithm.
+
+ This is a C++ interface to a set of SHA1Sum functions, that mirrors
+ the equivalent MD5 classes.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_SHA1_H
+#define APTPKG_SHA1_H
+
+#include "hashsum_template.h"
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <algorithm>
+#include <cstring>
+#include <string>
+#endif
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+using std::min;
+#endif
+
+typedef HashSumValue<160> SHA1SumValue;
+
+class SHA1Summation : public SummationImplementation
+{
+ /* assumes 64-bit alignment just in case */
+ unsigned char Buffer[64] __attribute__((aligned(8)));
+ unsigned char State[5*4] __attribute__((aligned(8)));
+ unsigned char Count[2*4] __attribute__((aligned(8)));
+ bool Done;
+
+ public:
+ bool Add(const unsigned char *inbuf, unsigned long long inlen) APT_OVERRIDE APT_NONNULL(2);
+ using SummationImplementation::Add;
+
+ SHA1SumValue Result();
+
+ SHA1Summation();
+};
+
+#endif
diff --git a/apt-pkg/contrib/sha2.h b/apt-pkg/contrib/sha2.h
new file mode 100644
index 0000000..e1a8c6c
--- /dev/null
+++ b/apt-pkg/contrib/sha2.h
@@ -0,0 +1,108 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ SHA{512,256}SumValue - Storage for a SHA-{512,256} hash.
+ SHA{512,256}Summation - SHA-{512,256} Secure Hash Algorithm.
+
+ This is a C++ interface to a set of SHA{512,256}Sum functions, that mirrors
+ the equivalent MD5 & SHA1 classes.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APTPKG_SHA2_H
+#define APTPKG_SHA2_H
+
+#include <cstring>
+
+#include "hashsum_template.h"
+#include "sha2_internal.h"
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <algorithm>
+#include <string>
+#include <stdint.h>
+#endif
+
+
+typedef HashSumValue<512> SHA512SumValue;
+typedef HashSumValue<256> SHA256SumValue;
+
+class SHA2SummationBase : public SummationImplementation
+{
+ protected:
+ bool Done;
+ public:
+ bool Add(const unsigned char *inbuf, unsigned long long len) APT_OVERRIDE APT_NONNULL(2) = 0;
+
+ void Result();
+};
+
+class SHA256Summation : public SHA2SummationBase
+{
+ SHA256_CTX ctx;
+ unsigned char Sum[32];
+
+ public:
+ bool Add(const unsigned char *inbuf, unsigned long long len) APT_OVERRIDE APT_NONNULL(2)
+ {
+ if (Done)
+ return false;
+ SHA256_Update(&ctx, inbuf, len);
+ return true;
+ };
+ using SummationImplementation::Add;
+
+ SHA256SumValue Result()
+ {
+ if (!Done) {
+ SHA256_Final(Sum, &ctx);
+ Done = true;
+ }
+ SHA256SumValue res;
+ res.Set(Sum);
+ return res;
+ };
+ SHA256Summation()
+ {
+ SHA256_Init(&ctx);
+ Done = false;
+ memset(&Sum, 0, sizeof(Sum));
+ };
+};
+
+class SHA512Summation : public SHA2SummationBase
+{
+ SHA512_CTX ctx;
+ unsigned char Sum[64];
+
+ public:
+ bool Add(const unsigned char *inbuf, unsigned long long len) APT_OVERRIDE APT_NONNULL(2)
+ {
+ if (Done)
+ return false;
+ SHA512_Update(&ctx, inbuf, len);
+ return true;
+ };
+ using SummationImplementation::Add;
+
+ SHA512SumValue Result()
+ {
+ if (!Done) {
+ SHA512_Final(Sum, &ctx);
+ Done = true;
+ }
+ SHA512SumValue res;
+ res.Set(Sum);
+ return res;
+ };
+ SHA512Summation()
+ {
+ SHA512_Init(&ctx);
+ Done = false;
+ memset(&Sum, 0, sizeof(Sum));
+ };
+};
+
+
+#endif
diff --git a/apt-pkg/contrib/sha256.h b/apt-pkg/contrib/sha256.h
new file mode 100644
index 0000000..15146c9
--- /dev/null
+++ b/apt-pkg/contrib/sha256.h
@@ -0,0 +1,8 @@
+#ifndef APTPKG_SHA256_H
+#define APTPKG_SHA256_H
+
+#include "sha2.h"
+
+#warning "This header is deprecated, please include sha2.h instead"
+
+#endif
diff --git a/apt-pkg/contrib/sha2_internal.cc b/apt-pkg/contrib/sha2_internal.cc
new file mode 100644
index 0000000..92a4a86
--- /dev/null
+++ b/apt-pkg/contrib/sha2_internal.cc
@@ -0,0 +1,1089 @@
+/*
+ * FILE: sha2.c
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <config.h>
+
+#include "sha2_internal.h"
+#include <assert.h> /* assert() */
+#include <endian.h>
+#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert(). On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined. Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file). Either define on the command line, for example:
+ *
+ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ * #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER. If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivalent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ * #define LITTLE_ENDIAN 1234
+ * #define BIG_ENDIAN 4321
+ *
+ * And for little-endian machines, add:
+ *
+ * #define BYTE_ORDER LITTLE_ENDIAN
+ *
+ * Or for big-endian machines:
+ *
+ * #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+/*
+ * Define the followingsha2_* types to types of the correct length on
+ * the native architecture. Most BSD systems and Linux define u_intXX_t
+ * types. Machines with very recent ANSI C headers, can use the
+ * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H
+ * during compile or in the sha.h header file.
+ *
+ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
+ * will need to define these three typedefs below (and the appropriate
+ * ones in sha.h too) by hand according to their system architecture.
+ *
+ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
+ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef uint8_t sha2_byte; /* Exactly 1 byte */
+typedef uint32_t sha2_word32; /* Exactly 4 bytes */
+typedef uint64_t sha2_word64; /* Exactly 8 bytes */
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef u_int8_t sha2_byte; /* Exactly 1 byte */
+typedef u_int32_t sha2_word32; /* Exactly 4 bytes */
+typedef u_int64_t sha2_word64; /* Exactly 8 bytes */
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+#define REVERSE32(w,x) { \
+ (x) = __builtin_bswap32(w); \
+}
+#define REVERSE64(w,x) { \
+ (x) = __builtin_bswap64(w); \
+}
+#else
+#define REVERSE32(w,x) { \
+ sha2_word32 tmp = (w); \
+ tmp = (tmp >> 16) | (tmp << 16); \
+ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+ sha2_word64 tmp = (w); \
+ tmp = (tmp >> 32) | (tmp << 32); \
+ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+ ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+ (w)[0] += (sha2_word64)(n); \
+ if ((w)[0] < (n)) { \
+ (w)[1]++; \
+ } \
+}
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory. Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define SHA2_USE_MEMSET_MEMCPY 1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l) memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l) bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
+ * S is a ROTATION) because the SHA-256/384/512 description document
+ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ * same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
+#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
+#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA512_Last(SHA512_CTX*);
+static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
+static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static sha2_word32 K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static sha2_word32 sha256_initial_hash_value[8] = {
+ 0x6a09e667UL,
+ 0xbb67ae85UL,
+ 0x3c6ef372UL,
+ 0xa54ff53aUL,
+ 0x510e527fUL,
+ 0x9b05688cUL,
+ 0x1f83d9abUL,
+ 0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static sha2_word64 K512[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static sha2_word64 sha384_initial_hash_value[8] = {
+ 0xcbbb9d5dc1059ed8ULL,
+ 0x629a292a367cd507ULL,
+ 0x9159015a3070dd17ULL,
+ 0x152fecd8f70e5939ULL,
+ 0x67332667ffc00b31ULL,
+ 0x8eb44a8768581511ULL,
+ 0xdb0c2e0d64f98fa7ULL,
+ 0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static sha2_word64 sha512_initial_hash_value[8] = {
+ 0x6a09e667f3bcc908ULL,
+ 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL,
+ 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL,
+ 0x5be0cd19137e2179ULL
+};
+
+/*
+ * Constant used by SHA256/384/512_End() functions for converting the
+ * digest to a readable hexadecimal character string:
+ */
+static const char *sha2_hex_digits = "0123456789abcdef";
+
+
+/*** SHA-256: *********************************************************/
+void SHA256_Init(SHA256_CTX* context) {
+ if (context == (SHA256_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE32(*data++, W256[j]); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + W256[j]; \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + (W256[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h) \
+ s0 = W256[(j+1)&0x0f]; \
+ s0 = sigma0_256(s0); \
+ s1 = W256[(j+14)&0x0f]; \
+ s1 = sigma1_256(s1); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ /* Rounds 0 to 15 (unrolled): */
+ ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds to 64: */
+ do {
+ ROUND256(a,b,c,d,e,f,g,h);
+ ROUND256(h,a,b,c,d,e,f,g);
+ ROUND256(g,h,a,b,c,d,e,f);
+ ROUND256(f,g,h,a,b,c,d,e);
+ ROUND256(e,f,g,h,a,b,c,d);
+ ROUND256(d,e,f,g,h,a,b,c);
+ ROUND256(c,d,e,f,g,h,a,b);
+ ROUND256(b,c,d,e,f,g,h,a);
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, T2, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Copy data while converting to host byte order */
+ REVERSE32(*data++,W256[j]);
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-256 compression function to update a..h with copy */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W256[(j+1)&0x0f];
+ s0 = sigma0_256(s0);
+ s1 = W256[(j+14)&0x0f];
+ s1 = sigma1_256(s1);
+
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ context->bitcount += freespace << 3;
+ len -= freespace;
+ data += freespace;
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ context->bitcount += len << 3;
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA256_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ sha2_byte buffer[SHA256_BLOCK_LENGTH];
+ MEMCPY_BCOPY(buffer, data, SHA256_BLOCK_LENGTH);
+ SHA256_Transform(context, (sha2_word32*)buffer);
+ context->bitcount += SHA256_BLOCK_LENGTH << 3;
+ len -= SHA256_BLOCK_LENGTH;
+ data += SHA256_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ context->bitcount += len << 3;
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+ unsigned int usedspace;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount,context->bitcount);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA256_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+ }
+ } else {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Set the bit count: */
+ union {
+ sha2_byte* c;
+ sha2_word64* l;
+ } bitcount;
+ bitcount.c = &context->buffer[SHA256_SHORT_BLOCK_LENGTH];
+ *(bitcount.l) = context->bitcount;
+
+ /* Final transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+ usedspace = 0;
+}
+
+char *SHA256_End(SHA256_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA256_Final(digest, context);
+
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) {
+ SHA256_CTX context;
+
+ SHA256_Init(&context);
+ SHA256_Update(&context, data, len);
+ return SHA256_End(&context, digest);
+}
+
+
+/*** SHA-512: *********************************************************/
+void SHA512_Init(SHA512_CTX* context) {
+ if (context == (SHA512_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE64(*data++, W512[j]); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + W512[j]; \
+ (d) += T1, \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + (W512[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h) \
+ s0 = W512[(j+1)&0x0f]; \
+ s0 = sigma0_512(s0); \
+ s1 = W512[(j+14)&0x0f]; \
+ s1 = sigma1_512(s1); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds up to 79: */
+ do {
+ ROUND512(a,b,c,d,e,f,g,h);
+ ROUND512(h,a,b,c,d,e,f,g);
+ ROUND512(g,h,a,b,c,d,e,f);
+ ROUND512(f,g,h,a,b,c,d,e);
+ ROUND512(e,f,g,h,a,b,c,d);
+ ROUND512(d,e,f,g,h,a,b,c);
+ ROUND512(c,d,e,f,g,h,a,b);
+ ROUND512(b,c,d,e,f,g,h,a);
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+static void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert TO host byte order */
+ REVERSE64(*data++, W512[j]);
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-512 compression function to update a..h with copy */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W512[(j+1)&0x0f];
+ s0 = sigma0_512(s0);
+ s1 = W512[(j+14)&0x0f];
+ s1 = sigma1_512(s1);
+
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ ADDINC128(context->bitcount, freespace << 3);
+ len -= freespace;
+ data += freespace;
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ ADDINC128(context->bitcount, len << 3);
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA512_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ sha2_byte buffer[SHA512_BLOCK_LENGTH];
+ MEMCPY_BCOPY(buffer, data, SHA512_BLOCK_LENGTH);
+ SHA512_Transform(context, (sha2_word64*)buffer);
+ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+ len -= SHA512_BLOCK_LENGTH;
+ data += SHA512_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ ADDINC128(context->bitcount, len << 3);
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+static void SHA512_Last(SHA512_CTX* context) {
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount[0],context->bitcount[0]);
+ REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA512_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
+ }
+ } else {
+ /* Prepare for final transform: */
+ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Store the length of input data (in bits): */
+ union {
+ sha2_byte* c;
+ sha2_word64* l;
+ } bitcount;
+ bitcount.c = &context->buffer[SHA512_SHORT_BLOCK_LENGTH];
+ bitcount.l[0] = context->bitcount[1];
+ bitcount.l[1] = context->bitcount[0];
+
+ /* Final transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+}
+
+void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last(context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA512_End(SHA512_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA512_Final(digest, context);
+
+ for (i = 0; i < SHA512_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) {
+ SHA512_CTX context;
+
+ SHA512_Init(&context);
+ SHA512_Update(&context, data, len);
+ return SHA512_End(&context, digest);
+}
+
+
+/*** SHA-384: *********************************************************/
+void SHA384_Init(SHA384_CTX* context) {
+ if (context == (SHA384_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
+ SHA512_Update((SHA512_CTX*)context, data, len);
+}
+
+void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last((SHA512_CTX*)context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 6; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA384_End(SHA384_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA384_Final(digest, context);
+
+ for (i = 0; i < SHA384_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) {
+ SHA384_CTX context;
+
+ SHA384_Init(&context);
+ SHA384_Update(&context, data, len);
+ return SHA384_End(&context, digest);
+}
+
diff --git a/apt-pkg/contrib/sha2_internal.h b/apt-pkg/contrib/sha2_internal.h
new file mode 100644
index 0000000..78d1d36
--- /dev/null
+++ b/apt-pkg/contrib/sha2_internal.h
@@ -0,0 +1,188 @@
+/*
+ * FILE: sha2.h
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef __SHA2_H__
+#define __SHA2_H__
+
+/*
+ * Import u_intXX_t size_t type definitions from system headers. You
+ * may need to change this, or define these things yourself in this
+ * file.
+ */
+#include <sys/types.h>
+
+#ifdef SHA2_USE_INTTYPES_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+/* NOTE: If your architecture does not define either u_intXX_t types or
+ * uintXX_t (from inttypes.h), you may need to define things by hand
+ * for your system:
+ */
+#if 0
+typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
+typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
+typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
+#endif
+/*
+ * Most BSD systems already define u_intXX_t types, as does Linux.
+ * Some systems, however, like Compaq's Tru64 Unix instead can use
+ * uintXX_t types defined by very recent ANSI C standards and included
+ * in the file:
+ *
+ * #include <inttypes.h>
+ *
+ * If you choose to use <inttypes.h> then please define:
+ *
+ * #define SHA2_USE_INTTYPES_H
+ *
+ * Or on the command line during compile:
+ *
+ * cc -DSHA2_USE_INTTYPES_H ...
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef struct _SHA256_CTX {
+ uint32_t state[8];
+ uint64_t bitcount;
+ uint8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef struct _SHA256_CTX {
+ u_int32_t state[8];
+ u_int64_t bitcount;
+ u_int8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ u_int64_t state[8];
+ u_int64_t bitcount[2];
+ u_int8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+typedef SHA512_CTX SHA384_CTX;
+
+
+/*** SHA-256/384/512 Function Prototypes ******************************/
+#ifndef NOPROTO
+#ifdef SHA2_USE_INTTYPES_H
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
+void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
+void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
+void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#else /* SHA2_USE_INTTYPES_H */
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
+void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t);
+void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t);
+void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+#else /* NOPROTO */
+
+void SHA256_Init();
+void SHA256_Update();
+void SHA256_Final();
+char* SHA256_End();
+char* SHA256_Data();
+
+void SHA384_Init();
+void SHA384_Update();
+void SHA384_Final();
+char* SHA384_End();
+char* SHA384_Data();
+
+void SHA512_Init();
+void SHA512_Update();
+void SHA512_Final();
+char* SHA512_End();
+char* SHA512_Data();
+
+#endif /* NOPROTO */
+
+#endif /* __SHA2_H__ */
+
diff --git a/apt-pkg/contrib/sptr.h b/apt-pkg/contrib/sptr.h
new file mode 100644
index 0000000..77806d9
--- /dev/null
+++ b/apt-pkg/contrib/sptr.h
@@ -0,0 +1,74 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Trivial non-ref counted 'smart pointer'
+
+ This is really only good to eliminate
+ {
+ delete Foo;
+ return;
+ }
+
+ Blocks from functions.
+
+ I think G++ has become good enough that doing this won't have much
+ code size implications.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef SMART_POINTER_H
+#define SMART_POINTER_H
+#include <apt-pkg/macros.h>
+
+template <class T>
+class APT_DEPRECATED_MSG("use std::unique_ptr instead") SPtr
+{
+ public:
+ T *Ptr;
+
+ inline T *operator ->() {return Ptr;};
+ inline T &operator *() {return *Ptr;};
+ inline operator T *() {return Ptr;};
+ inline operator void *() {return Ptr;};
+ inline T *UnGuard() {T *Tmp = Ptr; Ptr = 0; return Tmp;};
+ inline void operator =(T *N) {Ptr = N;};
+ inline bool operator ==(T *lhs) const {return Ptr == lhs;};
+ inline bool operator !=(T *lhs) const {return Ptr != lhs;};
+ inline T*Get() {return Ptr;};
+
+ inline SPtr(T *Ptr) : Ptr(Ptr) {};
+ inline SPtr() : Ptr(0) {};
+ inline ~SPtr() {delete Ptr;};
+};
+
+template <class T>
+class APT_DEPRECATED_MSG("use std::unique_ptr instead") SPtrArray
+{
+ public:
+ T *Ptr;
+
+ //inline T &operator *() {return *Ptr;};
+ inline operator T *() {return Ptr;};
+ inline operator void *() {return Ptr;};
+ inline T *UnGuard() {T *Tmp = Ptr; Ptr = 0; return Tmp;};
+ //inline T &operator [](signed long I) {return Ptr[I];};
+ inline void operator =(T *N) {Ptr = N;};
+ inline bool operator ==(T *lhs) const {return Ptr == lhs;};
+ inline bool operator !=(T *lhs) const {return Ptr != lhs;};
+ inline T *Get() {return Ptr;};
+
+ inline SPtrArray(T *Ptr) : Ptr(Ptr) {};
+ inline SPtrArray() : Ptr(0) {};
+#if __GNUC__ >= 4
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wunsafe-loop-optimizations"
+ // gcc warns about this, but we can do nothing here…
+#endif
+ inline ~SPtrArray() {delete [] Ptr;};
+#if __GNUC__ >= 4
+ #pragma GCC diagnostic pop
+#endif
+};
+
+#endif
diff --git a/apt-pkg/contrib/srvrec.cc b/apt-pkg/contrib/srvrec.cc
new file mode 100644
index 0000000..a97d9c6
--- /dev/null
+++ b/apt-pkg/contrib/srvrec.cc
@@ -0,0 +1,204 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ SRV record support
+
+ ##################################################################### */
+ /*}}}*/
+#include <config.h>
+
+#include <netdb.h>
+
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <time.h>
+
+#include <algorithm>
+#include <tuple>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/strutl.h>
+
+#include "srvrec.h"
+
+bool SrvRec::operator==(SrvRec const &other) const
+{
+ return (std::tie(target, priority, weight, port) ==
+ std::tie(other.target, other.priority, other.weight, other.port));
+}
+
+bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
+{
+ // try SRV only for hostnames, not for IP addresses
+ {
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ if (inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
+ inet_pton(AF_INET6, host.c_str(), &addr6) == 1)
+ return true;
+ }
+
+ std::string target;
+ int res;
+ struct servent s_ent_buf;
+ struct servent *s_ent = nullptr;
+ std::vector<char> buf(1024);
+
+ res = getservbyport_r(htons(port), "tcp", &s_ent_buf, buf.data(), buf.size(), &s_ent);
+ if (res != 0 || s_ent == nullptr)
+ return false;
+
+ strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str());
+ return GetSrvRecords(target, Result);
+}
+
+bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
+{
+ unsigned char answer[PACKETSZ];
+ int answer_len, compressed_name_len;
+ int answer_count;
+
+ if (res_init() != 0)
+ return _error->Errno("res_init", "Failed to init resolver");
+
+ answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer));
+ if (answer_len == -1)
+ return false;
+ if (answer_len < (int)sizeof(HEADER))
+ return _error->Warning("Not enough data from res_query (%i)", answer_len);
+
+ // check the header
+ HEADER *header = (HEADER*)answer;
+ if (header->rcode != NOERROR)
+ return _error->Warning("res_query returned rcode %i", header->rcode);
+ answer_count = ntohs(header->ancount);
+ if (answer_count <= 0)
+ return _error->Warning("res_query returned no answers (%i) ", answer_count);
+
+ // skip the header
+ compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len);
+ if(compressed_name_len < 0)
+ return _error->Warning("dn_skipname failed %i", compressed_name_len);
+
+ // pt points to the first answer record, go over all of them now
+ unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ;
+ while ((int)Result.size() < answer_count && pt < answer+answer_len)
+ {
+ u_int16_t type, klass, priority, weight, port, dlen;
+ char buf[MAXDNAME];
+
+ compressed_name_len = dn_skipname(pt, answer+answer_len);
+ if (compressed_name_len < 0)
+ return _error->Warning("dn_skipname failed (2): %i",
+ compressed_name_len);
+ pt += compressed_name_len;
+ if (((answer+answer_len) - pt) < 16)
+ return _error->Warning("packet too short");
+
+ // extract the data out of the result buffer
+ #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
+
+ extract_u16(type, pt);
+ if(type != T_SRV)
+ return _error->Warning("Unexpected type excepted %x != %x",
+ T_SRV, type);
+ extract_u16(klass, pt);
+ if(klass != C_IN)
+ return _error->Warning("Unexpected class excepted %x != %x",
+ C_IN, klass);
+ pt += 4; // ttl
+ extract_u16(dlen, pt);
+ extract_u16(priority, pt);
+ extract_u16(weight, pt);
+ extract_u16(port, pt);
+
+ #undef extract_u16
+
+ compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf));
+ if(compressed_name_len < 0)
+ return _error->Warning("dn_expand failed %i", compressed_name_len);
+ pt += compressed_name_len;
+
+ // add it to our class
+ Result.emplace_back(buf, priority, weight, port);
+ }
+
+ // implement load balancing as specified in RFC-2782
+
+ // sort them by priority
+ std::stable_sort(Result.begin(), Result.end());
+
+ for(std::vector<SrvRec>::iterator I = Result.begin();
+ I != Result.end(); ++I)
+ {
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ {
+ std::cerr << "SrvRecs: got " << I->target
+ << " prio: " << I->priority
+ << " weight: " << I->weight
+ << std::endl;
+ }
+ }
+
+ return true;
+}
+
+SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs)
+{
+ // FIXME: instead of the simplistic shuffle below use the algorithm
+ // described in rfc2782 (with weights)
+ // and figure out how the weights need to be adjusted if
+ // a host refuses connections
+
+#if 0 // all code below is only needed for the weight adjusted selection
+ // assign random number ranges
+ int prev_weight = 0;
+ int prev_priority = 0;
+ for(std::vector<SrvRec>::iterator I = Result.begin();
+ I != Result.end(); ++I)
+ {
+ if(prev_priority != I->priority)
+ prev_weight = 0;
+ I->random_number_range_start = prev_weight;
+ I->random_number_range_end = prev_weight + I->weight;
+ prev_weight = I->random_number_range_end;
+ prev_priority = I->priority;
+
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ std::cerr << "SrvRecs: got " << I->target
+ << " prio: " << I->priority
+ << " weight: " << I->weight
+ << std::endl;
+ }
+
+ // go over the code in reverse order and note the max random range
+ int max = 0;
+ prev_priority = 0;
+ for(std::vector<SrvRec>::iterator I = Result.end();
+ I != Result.begin(); --I)
+ {
+ if(prev_priority != I->priority)
+ max = I->random_number_range_end;
+ I->random_number_range_max = max;
+ }
+#endif
+
+ // shuffle in a very simplistic way for now (equal weights)
+ std::vector<SrvRec>::iterator I = Recs.begin();
+ std::vector<SrvRec>::iterator const J = std::find_if(Recs.begin(), Recs.end(),
+ [&I](SrvRec const &J) { return I->priority != J.priority; });
+
+ // clock seems random enough.
+ I += std::max(static_cast<clock_t>(0), clock()) % std::distance(I, J);
+ SrvRec const selected = std::move(*I);
+ Recs.erase(I);
+
+ if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
+ std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl;
+
+ return selected;
+}
diff --git a/apt-pkg/contrib/srvrec.h b/apt-pkg/contrib/srvrec.h
new file mode 100644
index 0000000..e22b7a1
--- /dev/null
+++ b/apt-pkg/contrib/srvrec.h
@@ -0,0 +1,54 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ SRV record support
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef SRVREC_H
+#define SRVREC_H
+
+#include <string>
+#include <vector>
+#include <arpa/nameser.h>
+
+class SrvRec
+{
+ public:
+ std::string target;
+ u_int16_t priority;
+ u_int16_t weight;
+ u_int16_t port;
+
+ // each server is assigned a interval [start, end] in the space of [0, max]
+ int random_number_range_start;
+ int random_number_range_end;
+ int random_number_range_max;
+
+ bool operator<(SrvRec const &other) const {
+ return this->priority < other.priority;
+ }
+ bool operator==(SrvRec const &other) const;
+
+ SrvRec(std::string const Target, u_int16_t const Priority,
+ u_int16_t const Weight, u_int16_t const Port) :
+ target(Target), priority(Priority), weight(Weight), port(Port),
+ random_number_range_start(0), random_number_range_end(0),
+ random_number_range_max(0) {}
+};
+
+/** \brief Get SRV records from host/port (builds the query string internally)
+ */
+bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result);
+
+/** \brief Get SRV records for query string like: _http._tcp.example.com
+ */
+bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result);
+
+/** \brief Pop a single SRV record from the vector of SrvRec taking
+ * priority and weight into account
+ */
+SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs);
+
+#endif
diff --git a/apt-pkg/contrib/string_view.h b/apt-pkg/contrib/string_view.h
new file mode 100644
index 0000000..536744e
--- /dev/null
+++ b/apt-pkg/contrib/string_view.h
@@ -0,0 +1,132 @@
+/*
+ * Basic implementation of string_view
+ *
+ * (C) 2015 Julian Andres Klode <jak@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#if !defined(APT_STRINGVIEW_H) && defined(APT_PKG_EXPOSE_STRING_VIEW)
+#define APT_STRINGVIEW_H
+#include <apt-pkg/macros.h>
+#include <string>
+#include <string.h>
+
+namespace APT {
+
+/**
+ * \brief Simple subset of std::string_view from C++17
+ *
+ * This is an internal implementation of the subset of std::string_view
+ * used by APT. It is not meant to be used in programs, only inside the
+ * library for performance critical paths.
+ */
+class APT_HIDDEN StringView {
+ const char *data_;
+ size_t size_;
+
+public:
+ static constexpr size_t npos = static_cast<size_t>(-1);
+ static_assert(APT::StringView::npos == std::string::npos, "npos values are different");
+
+ /* Constructors */
+ constexpr StringView() : data_(""), size_(0) {}
+ constexpr StringView(const char *data, size_t size) : data_(data), size_(size) {}
+
+ StringView(const char *data) : data_(data), size_(strlen(data)) {}
+ StringView(std::string const & str): data_(str.data()), size_(str.size()) {}
+
+
+ /* Viewers */
+ constexpr StringView substr(size_t pos, size_t n = npos) const {
+ return StringView(data_ + pos, n > (size_ - pos) ? (size_ - pos) : n);
+ }
+
+ size_t find(int c, size_t pos) const {
+ if (pos == 0)
+ return find(c);
+ size_t const found = substr(pos).find(c);
+ if (found == npos)
+ return npos;
+ return pos + found;
+ }
+ size_t find(int c) const {
+ const char *found = static_cast<const char*>(memchr(data_, c, size_));
+
+ if (found == NULL)
+ return npos;
+
+ return found - data_;
+ }
+
+ size_t rfind(int c, size_t pos) const {
+ if (pos == npos)
+ return rfind(c);
+ return APT::StringView(data_, pos).rfind(c);
+ }
+ size_t rfind(int c) const {
+ const char *found = static_cast<const char*>(memrchr(data_, c, size_));
+
+ if (found == NULL)
+ return npos;
+
+ return found - data_;
+ }
+
+ /* Conversions */
+ std::string to_string() const {
+ return std::string(data_, size_);
+ }
+
+ /* Comparisons */
+ int compare(size_t pos, size_t n, StringView other) const {
+ return substr(pos, n).compare(other);
+ }
+
+ int compare(StringView other) const {
+ int res;
+
+ res = memcmp(data_, other.data_, std::min(size_, other.size_));
+ if (res != 0)
+ return res;
+ if (size_ == other.size_)
+ return res;
+
+ return (size_ > other.size_) ? 1 : -1;
+ }
+
+ /* Optimization: If size not equal, string cannot be equal */
+ bool operator ==(StringView other) const { return size_ == other.size_ && compare(other) == 0; }
+ bool operator !=(StringView other) const { return !(*this == other); }
+
+ /* Accessors */
+ constexpr bool empty() const { return size_ == 0; }
+ constexpr const char* data() const { return data_; }
+ constexpr const char* begin() const { return data_; }
+ constexpr const char* end() const { return data_ + size_; }
+ constexpr char operator [](size_t i) const { return data_[i]; }
+ constexpr size_t size() const { return size_; }
+ constexpr size_t length() const { return size_; }
+};
+
+/**
+ * \brief Faster comparison for string views (compare size before data)
+ *
+ * Still stable, but faster than the normal ordering. */
+static inline int StringViewCompareFast(StringView a, StringView b) {
+ if (a.size() != b.size())
+ return a.size() - b.size();
+
+ return memcmp(a.data(), b.data(), a.size());
+}
+
+
+}
+
+inline bool operator ==(const char *other, APT::StringView that);
+inline bool operator ==(const char *other, APT::StringView that) { return that.operator==(other); }
+
+#endif
diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc
new file mode 100644
index 0000000..50344d1
--- /dev/null
+++ b/apt-pkg/contrib/strutl.cc
@@ -0,0 +1,1840 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ String Util - Some useful string functions.
+
+ These have been collected from here and there to do all sorts of useful
+ things to strings. They are useful in file parsers, URI handlers and
+ especially in APT methods.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
+
+ ##################################################################### */
+ /*}}}*/
+// Includes /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <array>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <ctype.h>
+#include <errno.h>
+#include <iconv.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+using namespace std;
+
+// Strip - Remove white space from the front and back of a string /*{{{*/
+// ---------------------------------------------------------------------
+namespace APT {
+ namespace String {
+std::string Strip(const std::string &str)
+{
+ // ensure we have at least one character
+ if (str.empty() == true)
+ return str;
+
+ char const * const s = str.c_str();
+ size_t start = 0;
+ for (; isspace(s[start]) != 0; ++start)
+ ; // find the first not-space
+
+ // string contains only whitespaces
+ if (s[start] == '\0')
+ return "";
+
+ size_t end = str.length() - 1;
+ for (; isspace(s[end]) != 0; --end)
+ ; // find the last not-space
+
+ return str.substr(start, end - start + 1);
+}
+
+bool Endswith(const std::string &s, const std::string &end)
+{
+ if (end.size() > s.size())
+ return false;
+ return (s.compare(s.size() - end.size(), end.size(), end) == 0);
+}
+
+bool Startswith(const std::string &s, const std::string &start)
+{
+ if (start.size() > s.size())
+ return false;
+ return (s.compare(0, start.size(), start) == 0);
+}
+
+std::string Join(std::vector<std::string> list, const std::string &sep)
+{
+ std::ostringstream oss;
+ for (auto it = list.begin(); it != list.end(); it++)
+ {
+ if (it != list.begin()) oss << sep;
+ oss << *it;
+ }
+ return oss.str();
+}
+
+}
+}
+ /*}}}*/
+// UTF8ToCodeset - Convert some UTF-8 string for some codeset /*{{{*/
+// ---------------------------------------------------------------------
+/* This is handy to use before display some information for enduser */
+bool UTF8ToCodeset(const char *codeset, const string &orig, string *dest)
+{
+ iconv_t cd;
+ const char *inbuf;
+ char *inptr, *outbuf;
+ size_t insize, bufsize;
+ dest->clear();
+
+ cd = iconv_open(codeset, "UTF-8");
+ if (cd == (iconv_t)(-1)) {
+ // Something went wrong
+ if (errno == EINVAL)
+ _error->Error("conversion from 'UTF-8' to '%s' not available",
+ codeset);
+ else
+ perror("iconv_open");
+
+ return false;
+ }
+
+ insize = bufsize = orig.size();
+ inbuf = orig.data();
+ inptr = (char *)inbuf;
+ outbuf = new char[bufsize];
+ size_t lastError = -1;
+
+ while (insize != 0)
+ {
+ char *outptr = outbuf;
+ size_t outsize = bufsize;
+ size_t const err = iconv(cd, &inptr, &insize, &outptr, &outsize);
+ dest->append(outbuf, outptr - outbuf);
+ if (err == (size_t)(-1))
+ {
+ switch (errno)
+ {
+ case EILSEQ:
+ insize--;
+ inptr++;
+ // replace a series of unknown multibytes with a single "?"
+ if (lastError != insize) {
+ lastError = insize - 1;
+ dest->append("?");
+ }
+ break;
+ case EINVAL:
+ insize = 0;
+ break;
+ case E2BIG:
+ if (outptr == outbuf)
+ {
+ bufsize *= 2;
+ delete[] outbuf;
+ outbuf = new char[bufsize];
+ }
+ break;
+ }
+ }
+ }
+
+ delete[] outbuf;
+
+ iconv_close(cd);
+
+ return true;
+}
+ /*}}}*/
+// strstrip - Remove white space from the front and back of a string /*{{{*/
+// ---------------------------------------------------------------------
+/* This is handy to use when parsing a file. It also removes \n's left
+ over from fgets and company */
+char *_strstrip(char *String)
+{
+ for (;*String != 0 && (*String == ' ' || *String == '\t'); String++);
+
+ if (*String == 0)
+ return String;
+ return _strrstrip(String);
+}
+ /*}}}*/
+// strrstrip - Remove white space from the back of a string /*{{{*/
+// ---------------------------------------------------------------------
+char *_strrstrip(char *String)
+{
+ char *End = String + strlen(String) - 1;
+ for (;End != String - 1 && (*End == ' ' || *End == '\t' || *End == '\n' ||
+ *End == '\r'); End--);
+ End++;
+ *End = 0;
+ return String;
+}
+ /*}}}*/
+// strtabexpand - Converts tabs into 8 spaces /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+char *_strtabexpand(char *String,size_t Len)
+{
+ for (char *I = String; I != I + Len && *I != 0; I++)
+ {
+ if (*I != '\t')
+ continue;
+ if (I + 8 > String + Len)
+ {
+ *I = 0;
+ return String;
+ }
+
+ /* Assume the start of the string is 0 and find the next 8 char
+ division */
+ int Len;
+ if (String == I)
+ Len = 1;
+ else
+ Len = 8 - ((String - I) % 8);
+ Len -= 2;
+ if (Len <= 0)
+ {
+ *I = ' ';
+ continue;
+ }
+
+ memmove(I + Len,I + 1,strlen(I) + 1);
+ for (char *J = I; J + Len != I; *I = ' ', I++);
+ }
+ return String;
+}
+ /*}}}*/
+// ParseQuoteWord - Parse a single word out of a string /*{{{*/
+// ---------------------------------------------------------------------
+/* This grabs a single word, converts any % escaped characters to their
+ proper values and advances the pointer. Double quotes are understood
+ and striped out as well. This is for URI/URL parsing. It also can
+ understand [] brackets.*/
+bool ParseQuoteWord(const char *&String,string &Res)
+{
+ // Skip leading whitespace
+ const char *C = String;
+ for (;*C != 0 && *C == ' '; C++);
+ if (*C == 0)
+ return false;
+
+ // Jump to the next word
+ for (;*C != 0 && isspace(*C) == 0; C++)
+ {
+ if (*C == '"')
+ {
+ C = strchr(C + 1, '"');
+ if (C == NULL)
+ return false;
+ }
+ if (*C == '[')
+ {
+ C = strchr(C + 1, ']');
+ if (C == NULL)
+ return false;
+ }
+ }
+
+ // Now de-quote characters
+ char Buffer[1024];
+ char Tmp[3];
+ const char *Start = String;
+ char *I;
+ for (I = Buffer; I < Buffer + sizeof(Buffer) && Start != C; I++)
+ {
+ if (*Start == '%' && Start + 2 < C &&
+ isxdigit(Start[1]) && isxdigit(Start[2]))
+ {
+ Tmp[0] = Start[1];
+ Tmp[1] = Start[2];
+ Tmp[2] = 0;
+ *I = (char)strtol(Tmp,0,16);
+ Start += 3;
+ continue;
+ }
+ if (*Start != '"')
+ *I = *Start;
+ else
+ I--;
+ Start++;
+ }
+ *I = 0;
+ Res = Buffer;
+
+ // Skip ending white space
+ for (;*C != 0 && isspace(*C) != 0; C++);
+ String = C;
+ return true;
+}
+ /*}}}*/
+// ParseCWord - Parses a string like a C "" expression /*{{{*/
+// ---------------------------------------------------------------------
+/* This expects a series of space separated strings enclosed in ""'s.
+ It concatenates the ""'s into a single string. */
+bool ParseCWord(const char *&String,string &Res)
+{
+ // Skip leading whitespace
+ const char *C = String;
+ for (;*C != 0 && *C == ' '; C++);
+ if (*C == 0)
+ return false;
+
+ char Buffer[1024];
+ char *Buf = Buffer;
+ if (strlen(String) >= sizeof(Buffer))
+ return false;
+
+ for (; *C != 0; C++)
+ {
+ if (*C == '"')
+ {
+ for (C++; *C != 0 && *C != '"'; C++)
+ *Buf++ = *C;
+
+ if (*C == 0)
+ return false;
+
+ continue;
+ }
+
+ if (C != String && isspace(*C) != 0 && isspace(C[-1]) != 0)
+ continue;
+ if (isspace(*C) == 0)
+ return false;
+ *Buf++ = ' ';
+ }
+ *Buf = 0;
+ Res = Buffer;
+ String = C;
+ return true;
+}
+ /*}}}*/
+// QuoteString - Convert a string into quoted from /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string QuoteString(const string &Str, const char *Bad)
+{
+ std::stringstream Res;
+ for (string::const_iterator I = Str.begin(); I != Str.end(); ++I)
+ {
+ if (strchr(Bad,*I) != 0 || isprint(*I) == 0 ||
+ *I == 0x25 || // percent '%' char
+ *I <= 0x20 || *I >= 0x7F) // control chars
+ {
+ ioprintf(Res, "%%%02hhx", *I);
+ }
+ else
+ Res << *I;
+ }
+ return Res.str();
+}
+ /*}}}*/
+// DeQuoteString - Convert a string from quoted from /*{{{*/
+// ---------------------------------------------------------------------
+/* This undoes QuoteString */
+string DeQuoteString(const string &Str)
+{
+ return DeQuoteString(Str.begin(),Str.end());
+}
+string DeQuoteString(string::const_iterator const &begin,
+ string::const_iterator const &end)
+{
+ string Res;
+ for (string::const_iterator I = begin; I != end; ++I)
+ {
+ if (*I == '%' && I + 2 < end &&
+ isxdigit(I[1]) && isxdigit(I[2]))
+ {
+ char Tmp[3];
+ Tmp[0] = I[1];
+ Tmp[1] = I[2];
+ Tmp[2] = 0;
+ Res += (char)strtol(Tmp,0,16);
+ I += 2;
+ continue;
+ }
+ else
+ Res += *I;
+ }
+ return Res;
+}
+
+ /*}}}*/
+// SizeToStr - Convert a long into a human readable size /*{{{*/
+// ---------------------------------------------------------------------
+/* A max of 4 digits are shown before conversion to the next highest unit.
+ The max length of the string will be 5 chars unless the size is > 10
+ YottaBytes (E24) */
+string SizeToStr(double Size)
+{
+ double ASize;
+ if (Size >= 0)
+ ASize = Size;
+ else
+ ASize = -1*Size;
+
+ /* bytes, KiloBytes, MegaBytes, GigaBytes, TeraBytes, PetaBytes,
+ ExaBytes, ZettaBytes, YottaBytes */
+ char Ext[] = {'\0','k','M','G','T','P','E','Z','Y'};
+ int I = 0;
+ while (I <= 8)
+ {
+ if (ASize < 100 && I != 0)
+ {
+ std::string S;
+ strprintf(S, "%'.1f %c", ASize, Ext[I]);
+ return S;
+ }
+
+ if (ASize < 10000)
+ {
+ std::string S;
+ strprintf(S, "%'.0f %c", ASize, Ext[I]);
+ return S;
+ }
+ ASize /= 1000.0;
+ I++;
+ }
+ return "";
+}
+ /*}}}*/
+// TimeToStr - Convert the time into a string /*{{{*/
+// ---------------------------------------------------------------------
+/* Converts a number of seconds to a hms format */
+string TimeToStr(unsigned long Sec)
+{
+ std::string S;
+ if (Sec > 60*60*24)
+ {
+ //TRANSLATOR: d means days, h means hours, min means minutes, s means seconds
+ strprintf(S,_("%lid %lih %limin %lis"),Sec/60/60/24,(Sec/60/60) % 24,(Sec/60) % 60,Sec % 60);
+ }
+ else if (Sec > 60*60)
+ {
+ //TRANSLATOR: h means hours, min means minutes, s means seconds
+ strprintf(S,_("%lih %limin %lis"),Sec/60/60,(Sec/60) % 60,Sec % 60);
+ }
+ else if (Sec > 60)
+ {
+ //TRANSLATOR: min means minutes, s means seconds
+ strprintf(S,_("%limin %lis"),Sec/60,Sec % 60);
+ }
+ else
+ {
+ //TRANSLATOR: s means seconds
+ strprintf(S,_("%lis"),Sec);
+ }
+ return S;
+}
+ /*}}}*/
+// SubstVar - Substitute a string for another string /*{{{*/
+// ---------------------------------------------------------------------
+/* This replaces all occurrences of Subst with Contents in Str. */
+string SubstVar(const string &Str,const string &Subst,const string &Contents)
+{
+ if (Subst.empty() == true)
+ return Str;
+
+ string::size_type Pos = 0;
+ string::size_type OldPos = 0;
+ string Temp;
+
+ while (OldPos < Str.length() &&
+ (Pos = Str.find(Subst,OldPos)) != string::npos)
+ {
+ if (OldPos != Pos)
+ Temp.append(Str, OldPos, Pos - OldPos);
+ if (Contents.empty() == false)
+ Temp.append(Contents);
+ OldPos = Pos + Subst.length();
+ }
+
+ if (OldPos == 0)
+ return Str;
+
+ if (OldPos >= Str.length())
+ return Temp;
+
+ Temp.append(Str, OldPos, string::npos);
+ return Temp;
+}
+string SubstVar(string Str,const struct SubstVar *Vars)
+{
+ for (; Vars->Subst != 0; Vars++)
+ Str = SubstVar(Str,Vars->Subst,*Vars->Contents);
+ return Str;
+}
+ /*}}}*/
+// OutputInDepth - return a string with separator multiplied with depth /*{{{*/
+// ---------------------------------------------------------------------
+/* Returns a string with the supplied separator depth + 1 times in it */
+std::string OutputInDepth(const unsigned long Depth, const char* Separator)
+{
+ std::string output = "";
+ for(unsigned long d=Depth+1; d > 0; d--)
+ output.append(Separator);
+ return output;
+}
+ /*}}}*/
+// URItoFileName - Convert the uri into a unique file name /*{{{*/
+// ---------------------------------------------------------------------
+/* This converts a URI into a safe filename. It quotes all unsafe characters
+ and converts / to _ and removes the scheme identifier. The resulting
+ file name should be unique and never occur again for a different file */
+string URItoFileName(const string &URI)
+{
+ // Nuke 'sensitive' items
+ ::URI U(URI);
+ U.User.clear();
+ U.Password.clear();
+ U.Access.clear();
+
+ // "\x00-\x20{}|\\\\^\\[\\]<>\"\x7F-\xFF";
+ string NewURI = QuoteString(U,"\\|{}[]<>\"^~_=!@#$%^&*");
+ replace(NewURI.begin(),NewURI.end(),'/','_');
+ return NewURI;
+}
+ /*}}}*/
+// Base64Encode - Base64 Encoding routine for short strings /*{{{*/
+// ---------------------------------------------------------------------
+/* This routine performs a base64 transformation on a string. It was ripped
+ from wget and then patched and bug fixed.
+
+ This spec can be found in rfc2045 */
+string Base64Encode(const string &S)
+{
+ // Conversion table.
+ static char tbl[64] = {'A','B','C','D','E','F','G','H',
+ 'I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X',
+ 'Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n',
+ 'o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3',
+ '4','5','6','7','8','9','+','/'};
+
+ // Pre-allocate some space
+ string Final;
+ Final.reserve((4*S.length() + 2)/3 + 2);
+
+ /* Transform the 3x8 bits to 4x6 bits, as required by
+ base64. */
+ for (string::const_iterator I = S.begin(); I < S.end(); I += 3)
+ {
+ char Bits[3] = {0,0,0};
+ Bits[0] = I[0];
+ if (I + 1 < S.end())
+ Bits[1] = I[1];
+ if (I + 2 < S.end())
+ Bits[2] = I[2];
+
+ Final += tbl[Bits[0] >> 2];
+ Final += tbl[((Bits[0] & 3) << 4) + (Bits[1] >> 4)];
+
+ if (I + 1 >= S.end())
+ break;
+
+ Final += tbl[((Bits[1] & 0xf) << 2) + (Bits[2] >> 6)];
+
+ if (I + 2 >= S.end())
+ break;
+
+ Final += tbl[Bits[2] & 0x3f];
+ }
+
+ /* Apply the padding elements, this tells how many bytes the remote
+ end should discard */
+ if (S.length() % 3 == 2)
+ Final += '=';
+ if (S.length() % 3 == 1)
+ Final += "==";
+
+ return Final;
+}
+ /*}}}*/
+// stringcmp - Arbitrary string compare /*{{{*/
+// ---------------------------------------------------------------------
+/* This safely compares two non-null terminated strings of arbitrary
+ length */
+int stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (*A != *B)
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (*A < *B)
+ return -1;
+ return 1;
+}
+
+#if __GNUC__ >= 3
+int stringcmp(string::const_iterator A,string::const_iterator AEnd,
+ const char *B,const char *BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (*A != *B)
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (*A < *B)
+ return -1;
+ return 1;
+}
+int stringcmp(string::const_iterator A,string::const_iterator AEnd,
+ string::const_iterator B,string::const_iterator BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (*A != *B)
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (*A < *B)
+ return -1;
+ return 1;
+}
+#endif
+ /*}}}*/
+// stringcasecmp - Arbitrary case insensitive string compare /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (tolower_ascii(*A) != tolower_ascii(*B))
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (tolower_ascii(*A) < tolower_ascii(*B))
+ return -1;
+ return 1;
+}
+#if __GNUC__ >= 3
+int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
+ const char *B,const char *BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (tolower_ascii(*A) != tolower_ascii(*B))
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (tolower_ascii(*A) < tolower_ascii(*B))
+ return -1;
+ return 1;
+}
+int stringcasecmp(string::const_iterator A,string::const_iterator AEnd,
+ string::const_iterator B,string::const_iterator BEnd)
+{
+ for (; A != AEnd && B != BEnd; A++, B++)
+ if (tolower_ascii(*A) != tolower_ascii(*B))
+ break;
+
+ if (A == AEnd && B == BEnd)
+ return 0;
+ if (A == AEnd)
+ return 1;
+ if (B == BEnd)
+ return -1;
+ if (tolower_ascii(*A) < tolower_ascii(*B))
+ return -1;
+ return 1;
+}
+#endif
+ /*}}}*/
+// LookupTag - Lookup the value of a tag in a tagged string /*{{{*/
+// ---------------------------------------------------------------------
+/* The format is like those used in package files and the method
+ communication system */
+std::string LookupTag(const std::string &Message, const char *TagC, const char *Default)
+{
+ std::string tag = std::string("\n") + TagC + ":";
+ if (Default == nullptr)
+ Default = "";
+ if (Message.length() < tag.length())
+ return Default;
+ std::transform(tag.begin(), tag.end(), tag.begin(), tolower_ascii);
+ auto valuestart = Message.cbegin();
+ // maybe the message starts directly with tag
+ if (Message[tag.length() - 2] == ':')
+ {
+ std::string lowstart = std::string("\n") + Message.substr(0, tag.length() - 1);
+ std::transform(lowstart.begin(), lowstart.end(), lowstart.begin(), tolower_ascii);
+ if (lowstart == tag)
+ valuestart = std::next(valuestart, tag.length() - 1);
+ }
+ // the tag is somewhere in the message
+ if (valuestart == Message.cbegin())
+ {
+ auto const tagbegin = std::search(Message.cbegin(), Message.cend(), tag.cbegin(), tag.cend(),
+ [](char const a, char const b) { return tolower_ascii(a) == b; });
+ if (tagbegin == Message.cend())
+ return Default;
+ valuestart = std::next(tagbegin, tag.length());
+ }
+ auto const is_whitespace = [](char const c) { return isspace_ascii(c) != 0 && c != '\n'; };
+ auto const is_newline = [](char const c) { return c == '\n'; };
+ std::string result;
+ valuestart = std::find_if_not(valuestart, Message.cend(), is_whitespace);
+ // is the first line of the value empty?
+ if (valuestart != Message.cend() && *valuestart == '\n')
+ {
+ valuestart = std::next(valuestart);
+ if (valuestart != Message.cend() && *valuestart == ' ')
+ valuestart = std::next(valuestart);
+ }
+ // extract the value over multiple lines removing trailing whitespace
+ while (valuestart < Message.cend())
+ {
+ auto const linebreak = std::find_if(valuestart, Message.cend(), is_newline);
+ auto valueend = std::prev(linebreak);
+ // skip spaces at the end of the line
+ while (valueend > valuestart && is_whitespace(*valueend))
+ valueend = std::prev(valueend);
+ // append found line to result
+ {
+ std::string tmp(valuestart, std::next(valueend));
+ if (tmp != ".")
+ {
+ if (result.empty())
+ result.assign(std::move(tmp));
+ else
+ result.append(tmp);
+ }
+ }
+ // see if the value is multiline
+ if (linebreak == Message.cend())
+ break;
+ valuestart = std::next(linebreak);
+ if (valuestart == Message.cend() || *valuestart != ' ')
+ break;
+ result.append("\n");
+ // skip the space leading a multiline (Keep all other whitespaces in the value)
+ valuestart = std::next(valuestart);
+ }
+ auto const valueend = result.find_last_not_of("\n");
+ if (valueend == std::string::npos)
+ result.clear();
+ else
+ result.erase(valueend + 1);
+ return result;
+}
+ /*}}}*/
+// StringToBool - Converts a string into a boolean /*{{{*/
+// ---------------------------------------------------------------------
+/* This inspects the string to see if it is true or if it is false and
+ then returns the result. Several variants on true/false are checked. */
+int StringToBool(const string &Text,int Default)
+{
+ char *ParseEnd;
+ int Res = strtol(Text.c_str(),&ParseEnd,0);
+ // ensure that the entire string was converted by strtol to avoid
+ // failures on "apt-cache show -a 0ad" where the "0" is converted
+ const char *TextEnd = Text.c_str()+Text.size();
+ if (ParseEnd == TextEnd && Res >= 0 && Res <= 1)
+ return Res;
+
+ // Check for positives
+ if (strcasecmp(Text.c_str(),"no") == 0 ||
+ strcasecmp(Text.c_str(),"false") == 0 ||
+ strcasecmp(Text.c_str(),"without") == 0 ||
+ strcasecmp(Text.c_str(),"off") == 0 ||
+ strcasecmp(Text.c_str(),"disable") == 0)
+ return 0;
+
+ // Check for negatives
+ if (strcasecmp(Text.c_str(),"yes") == 0 ||
+ strcasecmp(Text.c_str(),"true") == 0 ||
+ strcasecmp(Text.c_str(),"with") == 0 ||
+ strcasecmp(Text.c_str(),"on") == 0 ||
+ strcasecmp(Text.c_str(),"enable") == 0)
+ return 1;
+
+ return Default;
+}
+ /*}}}*/
+// TimeRFC1123 - Convert a time_t into RFC1123 format /*{{{*/
+// ---------------------------------------------------------------------
+/* This converts a time_t into a string time representation that is
+ year 2000 compliant and timezone neutral */
+string TimeRFC1123(time_t Date)
+{
+ return TimeRFC1123(Date, false);
+}
+string TimeRFC1123(time_t Date, bool const NumericTimezone)
+{
+ struct tm Conv;
+ if (gmtime_r(&Date, &Conv) == NULL)
+ return "";
+
+ auto const posix = std::locale::classic();
+ std::ostringstream datestr;
+ datestr.imbue(posix);
+ APT::StringView const fmt("%a, %d %b %Y %H:%M:%S");
+ std::use_facet<std::time_put<char>>(posix).put(
+ std::ostreambuf_iterator<char>(datestr),
+ datestr, ' ', &Conv, fmt.data(), fmt.data() + fmt.size());
+ if (NumericTimezone)
+ datestr << " +0000";
+ else
+ datestr << " GMT";
+ return datestr.str();
+}
+ /*}}}*/
+// ReadMessages - Read messages from the FD /*{{{*/
+// ---------------------------------------------------------------------
+/* This pulls full messages from the input FD into the message buffer.
+ It assumes that messages will not pause during transit so no
+ fancy buffering is used.
+
+ In particular: this reads blocks from the input until it believes
+ that it's run out of input text. Each block is terminated by a
+ double newline ('\n' followed by '\n').
+ */
+bool ReadMessages(int Fd, vector<string> &List)
+{
+ char Buffer[64000];
+ // Represents any left-over from the previous iteration of the
+ // parse loop. (i.e., if a message is split across the end
+ // of the buffer, it goes here)
+ string PartialMessage;
+
+ do {
+ int const Res = read(Fd, Buffer, sizeof(Buffer));
+ if (Res < 0 && errno == EINTR)
+ continue;
+
+ // process we read from has died
+ if (Res == 0)
+ return false;
+
+ // No data
+#if EAGAIN != EWOULDBLOCK
+ if (Res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+#else
+ if (Res < 0 && errno == EAGAIN)
+#endif
+ return true;
+ if (Res < 0)
+ return false;
+
+ // extract the message(s) from the buffer
+ char const *Start = Buffer;
+ char const * const End = Buffer + Res;
+
+ char const * NL = (char const *) memchr(Start, '\n', End - Start);
+ if (NL == NULL)
+ {
+ // end of buffer: store what we have so far and read new data in
+ PartialMessage.append(Start, End - Start);
+ Start = End;
+ }
+ else
+ ++NL;
+
+ if (PartialMessage.empty() == false && Start < End)
+ {
+ // if we start with a new line, see if the partial message we have ended with one
+ // so that we properly detect records ending between two read() runs
+ // cases are: \n|\n , \r\n|\r\n and \r\n\r|\n
+ // the case \r|\n\r\n is handled by the usual double-newline handling
+ if ((NL - Start) == 1 || ((NL - Start) == 2 && *Start == '\r'))
+ {
+ if (APT::String::Endswith(PartialMessage, "\n") || APT::String::Endswith(PartialMessage, "\r\n\r"))
+ {
+ PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
+ List.push_back(PartialMessage);
+ PartialMessage.clear();
+ while (NL < End && (*NL == '\n' || *NL == '\r')) ++NL;
+ Start = NL;
+ }
+ }
+ }
+
+ while (Start < End) {
+ char const * NL2 = (char const *) memchr(NL, '\n', End - NL);
+ if (NL2 == NULL)
+ {
+ // end of buffer: store what we have so far and read new data in
+ PartialMessage.append(Start, End - Start);
+ break;
+ }
+ ++NL2;
+
+ // did we find a double newline?
+ if ((NL2 - NL) == 1 || ((NL2 - NL) == 2 && *NL == '\r'))
+ {
+ PartialMessage.append(Start, NL2 - Start);
+ PartialMessage.erase(PartialMessage.find_last_not_of("\r\n") + 1);
+ List.push_back(PartialMessage);
+ PartialMessage.clear();
+ while (NL2 < End && (*NL2 == '\n' || *NL2 == '\r')) ++NL2;
+ Start = NL2;
+ }
+ NL = NL2;
+ }
+
+ // we have read at least one complete message and nothing left
+ if (PartialMessage.empty() == true)
+ return true;
+
+ if (WaitFd(Fd) == false)
+ return false;
+ } while (true);
+}
+ /*}}}*/
+// MonthConv - Converts a month string into a number /*{{{*/
+// ---------------------------------------------------------------------
+/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
+ Made it a bit more robust with a few tolower_ascii though. */
+static int MonthConv(char const * const Month)
+{
+ switch (tolower_ascii(*Month))
+ {
+ case 'a':
+ return tolower_ascii(Month[1]) == 'p'?3:7;
+ case 'd':
+ return 11;
+ case 'f':
+ return 1;
+ case 'j':
+ if (tolower_ascii(Month[1]) == 'a')
+ return 0;
+ return tolower_ascii(Month[2]) == 'n'?5:6;
+ case 'm':
+ return tolower_ascii(Month[2]) == 'r'?2:4;
+ case 'n':
+ return 10;
+ case 'o':
+ return 9;
+ case 's':
+ return 8;
+
+ // Pretend it is January..
+ default:
+ return 0;
+ }
+}
+ /*}}}*/
+// timegm - Internal timegm if the gnu version is not available /*{{{*/
+// ---------------------------------------------------------------------
+/* Converts struct tm to time_t, assuming the data in tm is UTC rather
+ than local timezone (mktime assumes the latter).
+
+ This function is a nonstandard GNU extension that is also present on
+ the BSDs and maybe other systems. For others we follow the advice of
+ the manpage of timegm and use his portable replacement. */
+#ifndef HAVE_TIMEGM
+static time_t timegm(struct tm *t)
+{
+ char *tz = getenv("TZ");
+ setenv("TZ", "", 1);
+ tzset();
+ time_t ret = mktime(t);
+ if (tz)
+ setenv("TZ", tz, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+#endif
+ /*}}}*/
+// RFC1123StrToTime - Converts an HTTP1.1 full date strings into a time_t /*{{{*/
+// ---------------------------------------------------------------------
+/* tries to parses a full date as specified in RFC7231 §7.1.1.1
+ with one exception: HTTP/1.1 valid dates need to have GMT as timezone.
+ As we encounter dates from UTC or with a numeric timezone in other places,
+ we allow them here to to be able to reuse the method. Either way, a date
+ must be in UTC or parsing will fail. Previous implementations of this
+ method used to ignore the timezone and assume always UTC. */
+bool RFC1123StrToTime(const char* const str,time_t &time)
+{
+ unsigned short day = 0;
+ signed int year = 0; // yes, Y23K problem – we going to worry then…
+ std::string weekday, month, datespec, timespec, zone;
+ std::istringstream ss(str);
+ auto const &posix = std::locale::classic();
+ ss.imbue(posix);
+ ss >> weekday;
+ // we only superficially check weekday, mostly to avoid accepting localized
+ // weekdays here and take only its length to decide which datetime format we
+ // encounter here. The date isn't stored.
+ std::transform(weekday.begin(), weekday.end(), weekday.begin(), ::tolower);
+ std::array<char const * const, 7> c_weekdays = {{ "sun", "mon", "tue", "wed", "thu", "fri", "sat" }};
+ if (std::find(c_weekdays.begin(), c_weekdays.end(), weekday.substr(0,3)) == c_weekdays.end())
+ return false;
+
+ switch (weekday.length())
+ {
+ case 4:
+ // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ if (weekday[3] != ',')
+ return false;
+ ss >> day >> month >> year >> timespec >> zone;
+ break;
+ case 3:
+ // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ ss >> month >> day >> timespec >> year;
+ zone = "UTC";
+ break;
+ case 0:
+ case 1:
+ case 2:
+ return false;
+ default:
+ // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ if (weekday[weekday.length() - 1] != ',')
+ return false;
+ ss >> datespec >> timespec >> zone;
+ auto const expldate = VectorizeString(datespec, '-');
+ if (expldate.size() != 3)
+ return false;
+ try {
+ size_t pos;
+ day = std::stoi(expldate[0], &pos);
+ if (pos != expldate[0].length())
+ return false;
+ year = 1900 + std::stoi(expldate[2], &pos);
+ if (pos != expldate[2].length())
+ return false;
+ strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(expldate[1].c_str()) + 1, day);
+ } catch (...) {
+ return false;
+ }
+ break;
+ }
+
+ if (ss.fail() || ss.bad() || !ss.eof())
+ return false;
+
+ if (zone != "GMT" && zone != "UTC" && zone != "Z") // RFC 822
+ {
+ // numeric timezones as a should of RFC 1123 and generally preferred
+ try {
+ size_t pos;
+ auto const z = std::stoi(zone, &pos);
+ if (z != 0 || pos != zone.length())
+ return false;
+ } catch (...) {
+ return false;
+ }
+ }
+
+ if (datespec.empty())
+ {
+ if (month.empty())
+ return false;
+ strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(month.c_str()) + 1, day);
+ }
+
+ std::string const datetime = datespec + ' ' + timespec;
+ struct tm Tm;
+ if (strptime(datetime.c_str(), "%Y-%m-%d %H:%M:%S", &Tm) == nullptr)
+ return false;
+ time = timegm(&Tm);
+ return true;
+}
+ /*}}}*/
+// FTPMDTMStrToTime - Converts a ftp modification date into a time_t /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool FTPMDTMStrToTime(const char* const str,time_t &time)
+{
+ struct tm Tm;
+ // MDTM includes no whitespaces but recommend and ignored by strptime
+ if (strptime(str, "%Y %m %d %H %M %S", &Tm) == NULL)
+ return false;
+
+ time = timegm(&Tm);
+ return true;
+}
+ /*}}}*/
+// StrToTime - Converts a string into a time_t /*{{{*/
+// ---------------------------------------------------------------------
+/* This handles all 3 popular time formats including RFC 1123, RFC 1036
+ and the C library asctime format. It requires the GNU library function
+ 'timegm' to convert a struct tm in UTC to a time_t. For some bizzar
+ reason the C library does not provide any such function :< This also
+ handles the weird, but unambiguous FTP time format*/
+bool StrToTime(const string &Val,time_t &Result)
+{
+ struct tm Tm;
+ char Month[10];
+
+ // Skip the day of the week
+ const char *I = strchr(Val.c_str(), ' ');
+
+ // Handle RFC 1123 time
+ Month[0] = 0;
+ if (sscanf(I," %2d %3s %4d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,&Tm.tm_year,
+ &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
+ {
+ // Handle RFC 1036 time
+ if (sscanf(I," %2d-%3s-%3d %2d:%2d:%2d GMT",&Tm.tm_mday,Month,
+ &Tm.tm_year,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) == 6)
+ Tm.tm_year += 1900;
+ else
+ {
+ // asctime format
+ if (sscanf(I," %3s %2d %2d:%2d:%2d %4d",Month,&Tm.tm_mday,
+ &Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec,&Tm.tm_year) != 6)
+ {
+ // 'ftp' time
+ if (sscanf(Val.c_str(),"%4d%2d%2d%2d%2d%2d",&Tm.tm_year,&Tm.tm_mon,
+ &Tm.tm_mday,&Tm.tm_hour,&Tm.tm_min,&Tm.tm_sec) != 6)
+ return false;
+ Tm.tm_mon--;
+ }
+ }
+ }
+
+ Tm.tm_isdst = 0;
+ if (Month[0] != 0)
+ Tm.tm_mon = MonthConv(Month);
+ else
+ Tm.tm_mon = 0; // we don't have a month, so pick something
+ Tm.tm_year -= 1900;
+
+ // Convert to local time and then to GMT
+ Result = timegm(&Tm);
+ return true;
+}
+ /*}}}*/
+// StrToNum - Convert a fixed length string to a number /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used in decoding the crazy fixed length string headers in
+ tar and ar files. */
+bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base)
+{
+ char S[30];
+ if (Len >= sizeof(S))
+ return false;
+ memcpy(S,Str,Len);
+ S[Len] = 0;
+
+ // All spaces is a zero
+ Res = 0;
+ unsigned I;
+ for (I = 0; S[I] == ' '; I++);
+ if (S[I] == 0)
+ return true;
+
+ char *End;
+ Res = strtoul(S,&End,Base);
+ if (End == S)
+ return false;
+
+ return true;
+}
+ /*}}}*/
+// StrToNum - Convert a fixed length string to a number /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used in decoding the crazy fixed length string headers in
+ tar and ar files. */
+bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base)
+{
+ char S[30];
+ if (Len >= sizeof(S))
+ return false;
+ memcpy(S,Str,Len);
+ S[Len] = 0;
+
+ // All spaces is a zero
+ Res = 0;
+ unsigned I;
+ for (I = 0; S[I] == ' '; I++);
+ if (S[I] == 0)
+ return true;
+
+ char *End;
+ Res = strtoull(S,&End,Base);
+ if (End == S)
+ return false;
+
+ return true;
+}
+ /*}}}*/
+
+// Base256ToNum - Convert a fixed length binary to a number /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used in decoding the 256bit encoded fixed length fields in
+ tar files */
+bool Base256ToNum(const char *Str,unsigned long long &Res,unsigned int Len)
+{
+ if ((Str[0] & 0x80) == 0)
+ return false;
+ else
+ {
+ Res = Str[0] & 0x7F;
+ for(unsigned int i = 1; i < Len; ++i)
+ Res = (Res<<8) + Str[i];
+ return true;
+ }
+}
+ /*}}}*/
+// Base256ToNum - Convert a fixed length binary to a number /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used in decoding the 256bit encoded fixed length fields in
+ tar files */
+bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len)
+{
+ unsigned long long Num = 0;
+ bool rc;
+
+ rc = Base256ToNum(Str, Num, Len);
+ // rudimentary check for overflow (Res = ulong, Num = ulonglong)
+ Res = Num;
+ if (Res != Num)
+ return false;
+
+ return rc;
+}
+ /*}}}*/
+// HexDigit - Convert a hex character into an integer /*{{{*/
+// ---------------------------------------------------------------------
+/* Helper for Hex2Num */
+static int HexDigit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+ /*}}}*/
+// Hex2Num - Convert a long hex number into a buffer /*{{{*/
+// ---------------------------------------------------------------------
+/* The length of the buffer must be exactly 1/2 the length of the string. */
+bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
+{
+ return Hex2Num(APT::StringView(Str), Num, Length);
+}
+
+bool Hex2Num(const APT::StringView Str,unsigned char *Num,unsigned int Length)
+{
+ if (Str.length() != Length*2)
+ return false;
+
+ // Convert each digit. We store it in the same order as the string
+ int J = 0;
+ for (auto I = Str.begin(); I != Str.end();J++, I += 2)
+ {
+ int first_half = HexDigit(I[0]);
+ int second_half;
+ if (first_half < 0)
+ return false;
+
+ second_half = HexDigit(I[1]);
+ if (second_half < 0)
+ return false;
+ Num[J] = first_half << 4;
+ Num[J] += second_half;
+ }
+
+ return true;
+}
+ /*}}}*/
+// TokSplitString - Split a string up by a given token /*{{{*/
+// ---------------------------------------------------------------------
+/* This is intended to be a faster splitter, it does not use dynamic
+ memories. Input is changed to insert nulls at each token location. */
+bool TokSplitString(char Tok,char *Input,char **List,
+ unsigned long ListMax)
+{
+ // Strip any leading spaces
+ char *Start = Input;
+ char *Stop = Start + strlen(Start);
+ for (; *Start != 0 && isspace(*Start) != 0; Start++);
+
+ unsigned long Count = 0;
+ char *Pos = Start;
+ while (Pos != Stop)
+ {
+ // Skip to the next Token
+ for (; Pos != Stop && *Pos != Tok; Pos++);
+
+ // Back remove spaces
+ char *End = Pos;
+ for (; End > Start && (End[-1] == Tok || isspace(End[-1]) != 0); End--);
+ *End = 0;
+
+ List[Count++] = Start;
+ if (Count >= ListMax)
+ {
+ List[Count-1] = 0;
+ return false;
+ }
+
+ // Advance pos
+ for (; Pos != Stop && (*Pos == Tok || isspace(*Pos) != 0 || *Pos == 0); Pos++);
+ Start = Pos;
+ }
+
+ List[Count] = 0;
+ return true;
+}
+ /*}}}*/
+// VectorizeString - Split a string up into a vector of strings /*{{{*/
+// ---------------------------------------------------------------------
+/* This can be used to split a given string up into a vector, so the
+ propose is the same as in the method above and this one is a bit slower
+ also, but the advantage is that we have an iteratable vector */
+vector<string> VectorizeString(string const &haystack, char const &split)
+{
+ vector<string> exploded;
+ if (haystack.empty() == true)
+ return exploded;
+ string::const_iterator start = haystack.begin();
+ string::const_iterator end = start;
+ do {
+ for (; end != haystack.end() && *end != split; ++end);
+ exploded.push_back(string(start, end));
+ start = end + 1;
+ } while (end != haystack.end() && (++end) != haystack.end());
+ return exploded;
+}
+ /*}}}*/
+// StringSplit - split a string into a string vector by token /*{{{*/
+// ---------------------------------------------------------------------
+/* See header for details.
+ */
+vector<string> StringSplit(std::string const &s, std::string const &sep,
+ unsigned int maxsplit)
+{
+ vector<string> split;
+ size_t start, pos;
+
+ // no separator given, this is bogus
+ if(sep.size() == 0)
+ return split;
+
+ start = pos = 0;
+ while (pos != string::npos)
+ {
+ pos = s.find(sep, start);
+ split.push_back(s.substr(start, pos-start));
+
+ // if maxsplit is reached, the remaining string is the last item
+ if(split.size() >= maxsplit)
+ {
+ split[split.size()-1] = s.substr(start);
+ break;
+ }
+ start = pos+sep.size();
+ }
+ return split;
+}
+ /*}}}*/
+// RegexChoice - Simple regex list/list matcher /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
+ const char **ListEnd)
+{
+ for (RxChoiceList *R = Rxs; R->Str != 0; R++)
+ R->Hit = false;
+
+ unsigned long Hits = 0;
+ for (; ListBegin < ListEnd; ++ListBegin)
+ {
+ // Check if the name is a regex
+ const char *I;
+ bool Regex = true;
+ for (I = *ListBegin; *I != 0; I++)
+ if (*I == '.' || *I == '?' || *I == '*' || *I == '|')
+ break;
+ if (*I == 0)
+ Regex = false;
+
+ // Compile the regex pattern
+ regex_t Pattern;
+ if (Regex == true)
+ if (regcomp(&Pattern,*ListBegin,REG_EXTENDED | REG_ICASE |
+ REG_NOSUB) != 0)
+ Regex = false;
+
+ // Search the list
+ bool Done = false;
+ for (RxChoiceList *R = Rxs; R->Str != 0; R++)
+ {
+ if (R->Str[0] == 0)
+ continue;
+
+ if (strcasecmp(R->Str,*ListBegin) != 0)
+ {
+ if (Regex == false)
+ continue;
+ if (regexec(&Pattern,R->Str,0,0,0) != 0)
+ continue;
+ }
+ Done = true;
+
+ if (R->Hit == false)
+ Hits++;
+
+ R->Hit = true;
+ }
+
+ if (Regex == true)
+ regfree(&Pattern);
+
+ if (Done == false)
+ _error->Warning(_("Selection %s not found"),*ListBegin);
+ }
+
+ return Hits;
+}
+ /*}}}*/
+// {str,io}printf - C format string outputter to C++ strings/iostreams /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to make the internationalization strings easier to translate
+ and to allow reordering of parameters */
+static bool iovprintf(ostream &out, const char *format,
+ va_list &args, ssize_t &size) {
+ char *S = (char*)malloc(size);
+ ssize_t const n = vsnprintf(S, size, format, args);
+ if (n > -1 && n < size) {
+ out << S;
+ free(S);
+ return true;
+ } else {
+ if (n > -1)
+ size = n + 1;
+ else
+ size *= 2;
+ }
+ free(S);
+ return false;
+}
+void ioprintf(ostream &out,const char *format,...)
+{
+ va_list args;
+ ssize_t size = 400;
+ while (true) {
+ bool ret;
+ va_start(args,format);
+ ret = iovprintf(out, format, args, size);
+ va_end(args);
+ if (ret == true)
+ return;
+ }
+}
+void strprintf(string &out,const char *format,...)
+{
+ va_list args;
+ ssize_t size = 400;
+ std::ostringstream outstr;
+ while (true) {
+ bool ret;
+ va_start(args,format);
+ ret = iovprintf(outstr, format, args, size);
+ va_end(args);
+ if (ret == true)
+ break;
+ }
+ out = outstr.str();
+}
+ /*}}}*/
+// safe_snprintf - Safer snprintf /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a snprintf that will never (ever) go past 'End' and returns a
+ pointer to the end of the new string. The returned string is always null
+ terminated unless Buffer == end. This is a better alterantive to using
+ consecutive snprintfs. */
+char *safe_snprintf(char *Buffer,char *End,const char *Format,...)
+{
+ va_list args;
+ int Did;
+
+ if (End <= Buffer)
+ return End;
+ va_start(args,Format);
+ Did = vsnprintf(Buffer,End - Buffer,Format,args);
+ va_end(args);
+
+ if (Did < 0 || Buffer + Did > End)
+ return End;
+ return Buffer + Did;
+}
+ /*}}}*/
+// StripEpoch - Remove the version "epoch" from a version string /*{{{*/
+// ---------------------------------------------------------------------
+string StripEpoch(const string &VerStr)
+{
+ size_t i = VerStr.find(":");
+ if (i == string::npos)
+ return VerStr;
+ return VerStr.substr(i+1);
+}
+ /*}}}*/
+
+// tolower_ascii - tolower() function that ignores the locale /*{{{*/
+// ---------------------------------------------------------------------
+/* This little function is the most called method we have and tries
+ therefore to do the absolute minimum - and is notable faster than
+ standard tolower/toupper and as a bonus avoids problems with different
+ locales - we only operate on ascii chars anyway. */
+#undef tolower_ascii
+int tolower_ascii(int const c) APT_PURE APT_COLD;
+int tolower_ascii(int const c)
+{
+ return tolower_ascii_inline(c);
+}
+ /*}}}*/
+
+// isspace_ascii - isspace() function that ignores the locale /*{{{*/
+// ---------------------------------------------------------------------
+/* This little function is one of the most called methods we have and tries
+ therefore to do the absolute minimum - and is notable faster than
+ standard isspace() and as a bonus avoids problems with different
+ locales - we only operate on ascii chars anyway. */
+#undef isspace_ascii
+int isspace_ascii(int const c) APT_PURE APT_COLD;
+int isspace_ascii(int const c)
+{
+ return isspace_ascii_inline(c);
+}
+ /*}}}*/
+
+// CheckDomainList - See if Host is in a , separate list /*{{{*/
+// ---------------------------------------------------------------------
+/* The domain list is a comma separate list of domains that are suffix
+ matched against the argument */
+bool CheckDomainList(const string &Host,const string &List)
+{
+ string::const_iterator Start = List.begin();
+ for (string::const_iterator Cur = List.begin(); Cur <= List.end(); ++Cur)
+ {
+ if (Cur < List.end() && *Cur != ',')
+ continue;
+
+ // Match the end of the string..
+ if ((Host.size() >= (unsigned)(Cur - Start)) &&
+ Cur - Start != 0 &&
+ stringcasecmp(Host.end() - (Cur - Start),Host.end(),Start,Cur) == 0)
+ return true;
+
+ Start = Cur + 1;
+ }
+ return false;
+}
+ /*}}}*/
+// strv_length - Return the length of a NULL-terminated string array /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+size_t strv_length(const char **str_array)
+{
+ size_t i;
+ for (i=0; str_array[i] != NULL; i++)
+ /* nothing */
+ ;
+ return i;
+}
+ /*}}}*/
+// DeEscapeString - unescape (\0XX and \xXX) from a string /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+string DeEscapeString(const string &input)
+{
+ char tmp[3];
+ string::const_iterator it;
+ string output;
+ for (it = input.begin(); it != input.end(); ++it)
+ {
+ // just copy non-escape chars
+ if (*it != '\\')
+ {
+ output += *it;
+ continue;
+ }
+
+ // deal with double escape
+ if (*it == '\\' &&
+ (it + 1 < input.end()) && it[1] == '\\')
+ {
+ // copy
+ output += *it;
+ // advance iterator one step further
+ ++it;
+ continue;
+ }
+
+ // ensure we have a char to read
+ if (it + 1 == input.end())
+ continue;
+
+ // read it
+ ++it;
+ switch (*it)
+ {
+ case '0':
+ if (it + 2 <= input.end()) {
+ tmp[0] = it[1];
+ tmp[1] = it[2];
+ tmp[2] = 0;
+ output += (char)strtol(tmp, 0, 8);
+ it += 2;
+ }
+ break;
+ case 'x':
+ if (it + 2 <= input.end()) {
+ tmp[0] = it[1];
+ tmp[1] = it[2];
+ tmp[2] = 0;
+ output += (char)strtol(tmp, 0, 16);
+ it += 2;
+ }
+ break;
+ default:
+ // FIXME: raise exception here?
+ break;
+ }
+ }
+ return output;
+}
+ /*}}}*/
+// URI::CopyFrom - Copy from an object /*{{{*/
+// ---------------------------------------------------------------------
+/* This parses the URI into all of its components */
+void URI::CopyFrom(const string &U)
+{
+ string::const_iterator I = U.begin();
+
+ // Locate the first colon, this separates the scheme
+ for (; I < U.end() && *I != ':' ; ++I);
+ string::const_iterator FirstColon = I;
+
+ /* Determine if this is a host type URI with a leading double //
+ and then search for the first single / */
+ string::const_iterator SingleSlash = I;
+ if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
+ SingleSlash += 3;
+
+ /* Find the / indicating the end of the hostname, ignoring /'s in the
+ square brackets */
+ bool InBracket = false;
+ for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); ++SingleSlash)
+ {
+ if (*SingleSlash == '[')
+ InBracket = true;
+ if (InBracket == true && *SingleSlash == ']')
+ InBracket = false;
+ }
+
+ if (SingleSlash > U.end())
+ SingleSlash = U.end();
+
+ // We can now write the access and path specifiers
+ Access.assign(U.begin(),FirstColon);
+ if (SingleSlash != U.end())
+ Path.assign(SingleSlash,U.end());
+ if (Path.empty() == true)
+ Path = "/";
+
+ // Now we attempt to locate a user:pass@host fragment
+ if (FirstColon + 2 <= U.end() && FirstColon[1] == '/' && FirstColon[2] == '/')
+ FirstColon += 3;
+ else
+ FirstColon += 1;
+ if (FirstColon >= U.end())
+ return;
+
+ if (FirstColon > SingleSlash)
+ FirstColon = SingleSlash;
+
+ // Find the colon...
+ I = FirstColon + 1;
+ if (I > SingleSlash)
+ I = SingleSlash;
+
+ // Search for the @ separating user:pass from host
+ auto const RevAt = std::find(
+ std::string::const_reverse_iterator(SingleSlash),
+ std::string::const_reverse_iterator(I), '@');
+ string::const_iterator const At = RevAt.base() == I ? SingleSlash : std::prev(RevAt.base());
+ // and then look for the colon between user and pass
+ string::const_iterator const SecondColon = std::find(I, At, ':');
+
+ // Now write the host and user/pass
+ if (At == SingleSlash)
+ {
+ if (FirstColon < SingleSlash)
+ Host.assign(FirstColon,SingleSlash);
+ }
+ else
+ {
+ Host.assign(At+1,SingleSlash);
+ // username and password must be encoded (RFC 3986)
+ User.assign(DeQuoteString(FirstColon,SecondColon));
+ if (SecondColon < At)
+ Password.assign(DeQuoteString(SecondColon+1,At));
+ }
+
+ // Now we parse the RFC 2732 [] hostnames.
+ unsigned long PortEnd = 0;
+ InBracket = false;
+ for (unsigned I = 0; I != Host.length();)
+ {
+ if (Host[I] == '[')
+ {
+ InBracket = true;
+ Host.erase(I,1);
+ continue;
+ }
+
+ if (InBracket == true && Host[I] == ']')
+ {
+ InBracket = false;
+ Host.erase(I,1);
+ PortEnd = I;
+ continue;
+ }
+ I++;
+ }
+
+ // Tsk, weird.
+ if (InBracket == true)
+ {
+ Host.clear();
+ return;
+ }
+
+ // Now we parse off a port number from the hostname
+ Port = 0;
+ string::size_type Pos = Host.rfind(':');
+ if (Pos == string::npos || Pos < PortEnd)
+ return;
+
+ Port = atoi(string(Host,Pos+1).c_str());
+ Host.assign(Host,0,Pos);
+}
+ /*}}}*/
+// URI::operator string - Convert the URI to a string /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+URI::operator string()
+{
+ std::stringstream Res;
+
+ if (Access.empty() == false)
+ Res << Access << ':';
+
+ if (Host.empty() == false)
+ {
+ if (Access.empty() == false)
+ Res << "//";
+
+ if (User.empty() == false)
+ {
+ // FIXME: Technically userinfo is permitted even less
+ // characters than these, but this is not conveniently
+ // expressed with a blacklist.
+ Res << QuoteString(User, ":/?#[]@");
+ if (Password.empty() == false)
+ Res << ":" << QuoteString(Password, ":/?#[]@");
+ Res << "@";
+ }
+
+ // Add RFC 2732 escaping characters
+ if (Access.empty() == false && Host.find_first_of("/:") != string::npos)
+ Res << '[' << Host << ']';
+ else
+ Res << Host;
+
+ if (Port != 0)
+ Res << ':' << std::to_string(Port);
+ }
+
+ if (Path.empty() == false)
+ {
+ if (Path[0] != '/')
+ Res << "/" << Path;
+ else
+ Res << Path;
+ }
+
+ return Res.str();
+}
+ /*}}}*/
+// URI::SiteOnly - Return the schema and site for the URI /*{{{*/
+string URI::SiteOnly(const string &URI)
+{
+ ::URI U(URI);
+ U.User.clear();
+ U.Password.clear();
+ U.Path.clear();
+ return U;
+}
+ /*}}}*/
+// URI::ArchiveOnly - Return the schema, site and cleaned path for the URI /*{{{*/
+string URI::ArchiveOnly(const string &URI)
+{
+ ::URI U(URI);
+ U.User.clear();
+ U.Password.clear();
+ if (U.Path.empty() == false && U.Path[U.Path.length() - 1] == '/')
+ U.Path.erase(U.Path.length() - 1);
+ return U;
+}
+ /*}}}*/
+// URI::NoUserPassword - Return the schema, site and path for the URI /*{{{*/
+string URI::NoUserPassword(const string &URI)
+{
+ ::URI U(URI);
+ U.User.clear();
+ U.Password.clear();
+ return U;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h
new file mode 100644
index 0000000..9f74f8c
--- /dev/null
+++ b/apt-pkg/contrib/strutl.h
@@ -0,0 +1,245 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ String Util - These are some useful string functions
+
+ _strstrip is a function to remove whitespace from the front and end
+ of a string.
+
+ This source is placed in the Public Domain, do with it what you will
+ It was originally written by Jason Gunthorpe <jgg@gpu.srv.ualberta.ca>
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef STRUTL_H
+#define STRUTL_H
+
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <vector>
+#ifdef APT_PKG_EXPOSE_STRING_VIEW
+#include <apt-pkg/string_view.h>
+#endif
+#include <stddef.h>
+#include <time.h>
+
+#include "macros.h"
+
+#ifndef APT_10_CLEANER_HEADERS
+#include <stdlib.h>
+#endif
+#ifndef APT_8_CLEANER_HEADERS
+using std::string;
+using std::vector;
+using std::ostream;
+#endif
+
+namespace APT {
+ namespace String {
+ std::string Strip(const std::string &s);
+ bool Endswith(const std::string &s, const std::string &ending);
+ bool Startswith(const std::string &s, const std::string &starting);
+ std::string Join(std::vector<std::string> list, const std::string &sep);
+
+ }
+}
+
+
+bool UTF8ToCodeset(const char *codeset, const std::string &orig, std::string *dest);
+char *_strstrip(char *String);
+char *_strrstrip(char *String); // right strip only
+char *_strtabexpand(char *String,size_t Len);
+bool ParseQuoteWord(const char *&String,std::string &Res);
+bool ParseCWord(const char *&String,std::string &Res);
+std::string QuoteString(const std::string &Str,const char *Bad);
+std::string DeQuoteString(const std::string &Str);
+std::string DeQuoteString(std::string::const_iterator const &begin, std::string::const_iterator const &end);
+
+// unescape (\0XX and \xXX) from a string
+std::string DeEscapeString(const std::string &input);
+
+std::string SizeToStr(double Bytes);
+std::string TimeToStr(unsigned long Sec);
+std::string Base64Encode(const std::string &Str);
+std::string OutputInDepth(const unsigned long Depth, const char* Separator=" ");
+std::string URItoFileName(const std::string &URI);
+APT_DEPRECATED_MSG("Specify if GMT is required or a numeric timezone can be used") std::string TimeRFC1123(time_t Date);
+/** returns a datetime string as needed by HTTP/1.1 and Debian files.
+ *
+ * Note: The date will always be represented in a UTC timezone
+ *
+ * @param Date to be represented as a string
+ * @param NumericTimezone is preferred in general, but HTTP/1.1 requires the use
+ * of GMT as timezone instead. \b true means that the timezone should be denoted
+ * as "+0000" while \b false uses "GMT".
+ */
+std::string TimeRFC1123(time_t Date, bool const NumericTimezone);
+/** parses time as needed by HTTP/1.1 and Debian files.
+ *
+ * HTTP/1.1 prefers dates in RFC1123 format (but the other two obsolete date formats
+ * are supported to) and e.g. Release files use the same format in Date & Valid-Until
+ * fields.
+ *
+ * Note: datetime strings need to be in UTC timezones (GMT, UTC, Z, +/-0000) to be
+ * parsed. Other timezones will be rejected as invalid. Previous implementations
+ * accepted other timezones, but treated them as UTC.
+ *
+ * @param str is the datetime string to parse
+ * @param[out] time will be the seconds since epoch of the given datetime if
+ * parsing is successful, undefined otherwise.
+ * @return \b true if parsing was successful, otherwise \b false.
+ */
+bool RFC1123StrToTime(const char* const str,time_t &time) APT_MUSTCHECK;
+bool FTPMDTMStrToTime(const char* const str,time_t &time) APT_MUSTCHECK;
+APT_DEPRECATED_MSG("Use RFC1123StrToTime or FTPMDTMStrToTime as needed instead") bool StrToTime(const std::string &Val,time_t &Result);
+std::string LookupTag(const std::string &Message,const char *Tag,const char *Default = 0);
+int StringToBool(const std::string &Text,int Default = -1);
+bool ReadMessages(int Fd, std::vector<std::string> &List);
+bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base = 0);
+bool StrToNum(const char *Str,unsigned long long &Res,unsigned Len,unsigned Base = 0);
+bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len);
+bool Base256ToNum(const char *Str,unsigned long long &Res,unsigned int Len);
+bool Hex2Num(const std::string &Str,unsigned char *Num,unsigned int Length);
+#ifdef APT_PKG_EXPOSE_STRING_VIEW
+APT_HIDDEN bool Hex2Num(const APT::StringView Str,unsigned char *Num,unsigned int Length);
+#endif
+// input changing string split
+bool TokSplitString(char Tok,char *Input,char **List,
+ unsigned long ListMax);
+
+// split a given string by a char
+std::vector<std::string> VectorizeString(std::string const &haystack, char const &split) APT_PURE;
+
+/* \brief Return a vector of strings from string "input" where "sep"
+ * is used as the delimiter string.
+ *
+ * \param input The input string.
+ *
+ * \param sep The separator to use.
+ *
+ * \param maxsplit (optional) The maximum amount of splitting that
+ * should be done .
+ *
+ * The optional "maxsplit" argument can be used to limit the splitting,
+ * if used the string is only split on maxsplit places and the last
+ * item in the vector contains the remainder string.
+ */
+std::vector<std::string> StringSplit(std::string const &input,
+ std::string const &sep,
+ unsigned int maxsplit=std::numeric_limits<unsigned int>::max()) APT_PURE;
+
+void ioprintf(std::ostream &out,const char *format,...) APT_PRINTF(2);
+void strprintf(std::string &out,const char *format,...) APT_PRINTF(2);
+char *safe_snprintf(char *Buffer,char *End,const char *Format,...) APT_PRINTF(3);
+bool CheckDomainList(const std::string &Host, const std::string &List);
+
+/* Do some compat mumbo jumbo */
+#define tolower_ascii tolower_ascii_inline
+#define isspace_ascii isspace_ascii_inline
+
+APT_PURE APT_HOT
+static inline int tolower_ascii_unsafe(int const c)
+{
+ return c | 0x20;
+}
+APT_PURE APT_HOT
+static inline int tolower_ascii_inline(int const c)
+{
+ return (c >= 'A' && c <= 'Z') ? c + 32 : c;
+}
+APT_PURE APT_HOT
+static inline int isspace_ascii_inline(int const c)
+{
+ // 9='\t',10='\n',11='\v',12='\f',13='\r',32=' '
+ return (c >= 9 && c <= 13) || c == ' ';
+}
+
+std::string StripEpoch(const std::string &VerStr);
+
+#define APT_MKSTRCMP(name,func) \
+inline APT_PURE int name(const char *A,const char *B) {return func(A,A+strlen(A),B,B+strlen(B));} \
+inline APT_PURE int name(const char *A,const char *AEnd,const char *B) {return func(A,AEnd,B,B+strlen(B));} \
+inline APT_PURE int name(const std::string& A,const char *B) {return func(A.c_str(),A.c_str()+A.length(),B,B+strlen(B));} \
+inline APT_PURE int name(const std::string& A,const std::string& B) {return func(A.c_str(),A.c_str()+A.length(),B.c_str(),B.c_str()+B.length());} \
+inline APT_PURE int name(const std::string& A,const char *B,const char *BEnd) {return func(A.c_str(),A.c_str()+A.length(),B,BEnd);}
+
+#define APT_MKSTRCMP2(name,func) \
+inline APT_PURE int name(const char *A,const char *AEnd,const char *B) {return func(A,AEnd,B,B+strlen(B));} \
+inline APT_PURE int name(const std::string& A,const char *B) {return func(A.begin(),A.end(),B,B+strlen(B));} \
+inline APT_PURE int name(const std::string& A,const std::string& B) {return func(A.begin(),A.end(),B.begin(),B.end());} \
+inline APT_PURE int name(const std::string& A,const char *B,const char *BEnd) {return func(A.begin(),A.end(),B,BEnd);}
+
+int APT_PURE stringcmp(const char *A,const char *AEnd,const char *B,const char *BEnd);
+int APT_PURE stringcasecmp(const char *A,const char *AEnd,const char *B,const char *BEnd);
+
+/* We assume that GCC 3 indicates that libstdc++3 is in use too. In that
+ case the definition of string::const_iterator is not the same as
+ const char * and we need these extra functions */
+#if __GNUC__ >= 3
+int APT_PURE stringcmp(std::string::const_iterator A,std::string::const_iterator AEnd,
+ const char *B,const char *BEnd);
+int APT_PURE stringcmp(std::string::const_iterator A,std::string::const_iterator AEnd,
+ std::string::const_iterator B,std::string::const_iterator BEnd);
+int APT_PURE stringcasecmp(std::string::const_iterator A,std::string::const_iterator AEnd,
+ const char *B,const char *BEnd);
+int APT_PURE stringcasecmp(std::string::const_iterator A,std::string::const_iterator AEnd,
+ std::string::const_iterator B,std::string::const_iterator BEnd);
+
+inline APT_PURE int stringcmp(std::string::const_iterator A,std::string::const_iterator Aend,const char *B) {return stringcmp(A,Aend,B,B+strlen(B));}
+inline APT_PURE int stringcasecmp(std::string::const_iterator A,std::string::const_iterator Aend,const char *B) {return stringcasecmp(A,Aend,B,B+strlen(B));}
+#endif
+
+APT_MKSTRCMP2(stringcmp,stringcmp)
+APT_MKSTRCMP2(stringcasecmp,stringcasecmp)
+
+// Return the length of a NULL-terminated string array
+size_t APT_PURE strv_length(const char **str_array);
+
+
+inline const char *DeNull(const char *s) {return (s == 0?"(null)":s);}
+
+class URI
+{
+ void CopyFrom(const std::string &From);
+
+ public:
+
+ std::string Access;
+ std::string User;
+ std::string Password;
+ std::string Host;
+ std::string Path;
+ unsigned int Port;
+
+ operator std::string();
+ inline void operator =(const std::string &From) {CopyFrom(From);}
+ inline bool empty() {return Access.empty();};
+ static std::string SiteOnly(const std::string &URI);
+ static std::string ArchiveOnly(const std::string &URI);
+ static std::string NoUserPassword(const std::string &URI);
+
+ URI(std::string Path) {CopyFrom(Path);}
+ URI() : Port(0) {}
+};
+
+struct SubstVar
+{
+ const char *Subst;
+ const std::string *Contents;
+};
+std::string SubstVar(std::string Str,const struct SubstVar *Vars);
+std::string SubstVar(const std::string &Str,const std::string &Subst,const std::string &Contents);
+
+struct RxChoiceList
+{
+ void *UserData;
+ const char *Str;
+ bool Hit;
+};
+unsigned long RegexChoice(RxChoiceList *Rxs,const char **ListBegin,
+ const char **ListEnd);
+
+#endif
diff --git a/apt-pkg/contrib/weakptr.h b/apt-pkg/contrib/weakptr.h
new file mode 100644
index 0000000..8de727d
--- /dev/null
+++ b/apt-pkg/contrib/weakptr.h
@@ -0,0 +1,64 @@
+/* weakptr.h - An object which supports weak pointers.
+ *
+ * Copyright (C) 2010 Julian Andres Klode <jak@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef WEAK_POINTER_H
+#define WEAK_POINTER_H
+
+#include <set>
+#include <stddef.h>
+
+/**
+ * Class for objects providing support for weak pointers.
+ *
+ * This class allows for the registration of certain pointers as weak,
+ * which will cause them to be set to NULL when the destructor of the
+ * object is called.
+ */
+class WeakPointable {
+private:
+ std::set<WeakPointable**> pointers;
+
+public:
+
+ /**
+ * Add a new weak pointer.
+ */
+ inline void AddWeakPointer(WeakPointable** weakptr) {
+ pointers.insert(weakptr);
+ }
+
+ /**
+ * Remove the weak pointer from the list of weak pointers.
+ */
+ inline void RemoveWeakPointer(WeakPointable **weakptr) {
+ pointers.erase(weakptr);
+ }
+
+ /**
+ * Deconstruct the object, set all weak pointers to NULL.
+ */
+ ~WeakPointable() {
+ std::set<WeakPointable**>::iterator iter = pointers.begin();
+ while (iter != pointers.end())
+ **(iter++) = NULL;
+ }
+};
+
+#endif // WEAK_POINTER_H