Skip to content

Commit

Permalink
Detect trimmed changelogs and pick online instead
Browse files Browse the repository at this point in the history
We only check the start of these lines to avoid hard coding the exact
command and we pick 150 as maximum line length as the longest package
name on my system is apparently 75 characters long. We could choose
longer or shorter without much issue as over-length just means we
mishandle the rest of the line as a new line and it should be really
unlikely that a) lines are that long in this file and b) that such long
lines contain one of our trigger sequences – but even if, all we do is
start a download of an online file. Could be worse.

This auto-detection can be avoided by setting
Acquire::Changelogs::AlwaysOnline (or Origin specific sub options)
to "true" if you always want the changelog from an online source.
The reverse – setting it to "false" in the hope it would not get the
changelog from an online source – was not and is still not possible.

Closes: #1024457
  • Loading branch information
DonKult committed Mar 3, 2023
1 parent edcdc25 commit acbfdf0
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 11 deletions.
67 changes: 56 additions & 11 deletions apt-pkg/acquire-item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3710,17 +3710,62 @@ std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
{
std::string const root = _config->FindDir("Dir");
std::string const basename = root + std::string("usr/share/doc/") + Pkg.Name() + "/changelog";
std::string const debianname = basename + ".Debian";
if (FileExists(debianname))
return "copy:https://" + debianname;
else if (FileExists(debianname + ".gz"))
return "store:https://" + debianname + ".gz";
else if (FileExists(basename))
return "copy:https://" + basename;
else if (FileExists(basename + ".gz"))
return "store:https://" + basename + ".gz";
auto const LocalFile = [](pkgCache::PkgIterator const &Pkg) -> std::string {
std::string const root = _config->FindDir("Dir");
std::string const basename = root + std::string("usr/share/doc/") + Pkg.Name() + "/changelog";
std::string const debianname = basename + ".Debian";
auto const exts = APT::Configuration::getCompressorExtensions(); // likely we encounter only .gz
for (auto file : { debianname, basename })
{
if (FileExists(file))
return "copy:https://" + file;
for (auto const& ext : exts)
{
auto const compressedfile = file + ext;
if (FileExists(compressedfile))
return "store:https://" + compressedfile;
}
}
return "";
}(Pkg);
if (not LocalFile.empty())
{
_error->PushToStack();
FileFd trimmed;
if (APT::String::Startswith(LocalFile, "copy:https://"))
trimmed.Open(LocalFile.substr(7), FileFd::ReadOnly, FileFd::None);
else
trimmed.Open(LocalFile.substr(8), FileFd::ReadOnly, FileFd::Extension);

bool trimmedFile = false;
if (trimmed.IsOpen())
{
/* We want to look at the last line… in a (likely) compressed file,
which means we more or less have to uncompress the entire file.
So we skip ahead the filesize minus our choosen line size in
the hope that changelogs don't grow by being compressed to
avoid doing this costly dance on at least a bit of the file. */
char buffer[150];
if (auto const filesize = trimmed.FileSize(); filesize > sizeof(buffer))
trimmed.Skip(filesize - sizeof(buffer));
std::string_view giveaways[] = {
"# To read the complete changelog use", // Debian
"# For older changelog entries, run", // Ubuntu
};
while (trimmed.ReadLine(buffer, sizeof(buffer)) != nullptr)
{
std::string_view const line{buffer};
if (std::any_of(std::begin(giveaways), std::end(giveaways), [=](auto const gw) { return line.compare(0, gw.size(), gw) == 0; }))
{
trimmedFile = true;
break;
}
}
}
_error->RevertToStack();
if (not trimmedFile)
return LocalFile;
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions test/integration/test-apt-get-changelog
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ testsuccessequal "'https://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_4
testsuccessequal "'https://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false -o Acquire::Changelogs::AlwaysOnline::Origin::Ubuntu=true
testsuccessequal "'copy:https://${TMPWORKINGDIRECTORY}/rootdir/usr/share/doc/awesome/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false -o Acquire::Changelogs::AlwaysOnline::Origin::Debian=true

printf '\n# Older entries have been removed from this changelog.' >> rootdir/usr/share/doc/awesome/changelog
testsuccessequal "'copy:https://${TMPWORKINGDIRECTORY}/rootdir/usr/share/doc/awesome/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false
printf '\n# To read the complete changelog use `apt changelog awesome`.' >> rootdir/usr/share/doc/awesome/changelog
testsuccessequal "'https://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false
printf '\n# No guarantees the trigger is the last line' >> rootdir/usr/share/doc/awesome/changelog
testsuccessequal "'https://localhost:${APTHTTPPORT}/pool/main/a/awesome/awesome_42/changelog' awesome.changelog" apt changelog awesome --print-uris -o Acquire::Changelogs::AlwaysOnline=false

testsuccess apt changelog awesome -d
testfilestats 'awesome.changelog' '%U:%G:%a' '=' "${TEST_DEFAULT_USER}:${TEST_DEFAULT_GROUP}:644"
head -n 3 awesome.changelog > awesome.change
Expand Down

0 comments on commit acbfdf0

Please sign in to comment.