merged lp:~mvo/apt/apt-get-changelog
[ntk/apt.git] / cmdline / apt-get.cc
index 72c1ef0..b645da8 100644 (file)
@@ -2165,6 +2165,60 @@ bool DoAutoClean(CommandLine &CmdL)
       Cleaner.Go(_config->FindDir("Dir::Cache::archives") + "partial/",*Cache);
 }
                                                                        /*}}}*/
+// DoDownload - download a binary                                      /*{{{*/
+// ---------------------------------------------------------------------
+bool DoDownload(CommandLine &CmdL)
+{
+   CacheFile Cache;
+   if (Cache.ReadOnlyOpen() == false)
+      return false;
+   
+   APT::CacheSetHelper helper(c0out);
+   APT::VersionSet verset = APT::VersionSet::FromCommandLine(Cache,
+               CmdL.FileList + 1, APT::VersionSet::CANDIDATE, helper);
+   pkgAcquire Fetcher;
+   AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet",0));
+   Fetcher.Setup(&Stat);
+
+   if (verset.empty() == true)
+      return false;
+
+   bool result = true;
+   pkgRecords Recs(Cache);
+   pkgSourceList *SrcList = Cache.GetSourceList();
+   for (APT::VersionSet::const_iterator Ver = verset.begin(); 
+        Ver != verset.end(); 
+        ++Ver) 
+   {
+      string descr;
+      // get the right version
+      pkgCache::PkgIterator Pkg = Ver.ParentPkg();
+      pkgRecords::Parser &rec=Recs.Lookup(Ver.FileList());
+      pkgCache::VerFileIterator Vf = Ver.FileList();
+      if (Vf.end() == true)
+         return _error->Error("Can not find VerFile");
+      pkgCache::PkgFileIterator F = Vf.File();
+      pkgIndexFile *index;
+      if(SrcList->FindIndex(F, index) == false)
+         return _error->Error("FindIndex failed");
+      string uri = index->ArchiveURI(rec.FileName());
+      strprintf(descr, _("Downloading %s %s"), Pkg.Name(), Ver.VerStr());
+      // get the most appropriate hash
+      HashString hash;
+      if (rec.SHA256Hash() != "")
+         hash = HashString("sha256", rec.SHA256Hash());
+      else if (rec.SHA1Hash() != "")
+         hash = HashString("sha1", rec.SHA1Hash());
+      else if (rec.MD5Hash() != "")
+         hash = HashString("md5", rec.MD5Hash());
+      // get the file
+      new pkgAcqFile(&Fetcher, uri, hash.toStr(), (*Ver)->Size, descr, Pkg.Name(), ".");
+      result &= (Fetcher.Run() == pkgAcquire::Continue);
+   }
+
+   return result;
+}
+                                                                       /*}}}*/
 // DoCheck - Perform the check operation                               /*{{{*/
 // ---------------------------------------------------------------------
 /* Opening automatically checks the system, this command is mostly used
@@ -2733,16 +2787,48 @@ bool DoBuildDep(CommandLine &CmdL)
    return true;
 }
                                                                        /*}}}*/
+// GuessThirdPartyChangelogUri - return url                            /*{{{*/
+// ---------------------------------------------------------------------
+bool GuessThirdPartyChangelogUri(CacheFile &Cache, 
+                                 pkgCache::PkgIterator Pkg,
+                                 pkgCache::VerIterator Ver,
+                                 string &out_uri)
+{
+   pkgRecords Recs(Cache);
+   pkgSourceList *SrcList = Cache.GetSourceList();
+
+   // get the binary deb server path
+   pkgRecords::Parser &rec=Recs.Lookup(Ver.FileList());
+   string srcpkg = rec.SourcePkg().empty() ? Pkg.Name() : rec.SourcePkg();
+   // FIXME: deal with cases like gcc-defaults (srcver != binver)
+   string srcver = StripEpoch(Ver.VerStr());
+
+   pkgCache::VerFileIterator Vf = Ver.FileList();
+   if (Vf.end() == true)
+      return false;
+   pkgCache::PkgFileIterator F = Vf.File();
+   pkgIndexFile *index;
+   if(SrcList->FindIndex(F, index) == false)
+      return false;
+   // get archive uri for the binary deb
+   string deb_uri = index->ArchiveURI(rec.FileName());
+
+   // now strip away the filename and add srcpkg_srcver.changelog
+   out_uri = flNotFile(deb_uri);
+   out_uri += srcpkg + "_" + srcver + ".changelog";
+   return true;
+}
 // DownloadChangelog - Download the changelog                          /*{{{*/
 // ---------------------------------------------------------------------
-string DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher, pkgCache::VerIterator V)
+bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher, pkgCache::VerIterator V, string targetfile)
 {
-   string uri;
    string srcpkg;
    string prefix;
    string descr;
    string src_section;
    string verstr;
+   string server;
+   string path;
 
    // data structures we need
    pkgRecords Recs(CacheFile);
@@ -2751,7 +2837,6 @@ string DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher, pkgCache::Ve
 
    // build uri
    srcpkg = rec.SourcePkg().empty() ? Pkg.Name() : rec.SourcePkg();
-   strprintf(descr, _("Changelog for %s"), srcpkg.c_str());
    // FIXME: we actually need the source section here
    src_section= Pkg.Section();
    if(src_section.find('/')!=src_section.npos)
@@ -2767,22 +2852,35 @@ string DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher, pkgCache::Ve
    if(verstr.find(':')!=verstr.npos)
       verstr=string(verstr, verstr.find(':')+1);
 
-   strprintf(uri, "http://packages.debian.org/changelogs/pool/%s/%s/%s/%s_%s/changelog", src_section.c_str(), prefix.c_str(), srcpkg.c_str(), srcpkg.c_str(), verstr.c_str());
-
-   AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet",0));
-   Fetcher.Setup(&Stat);
-
-   // temp file
-   string targetfile = tmpnam(strdup("apt-changelog-XXXXXX"));
-   new pkgAcqFile(&Fetcher, uri, "", 0, descr, srcpkg, "ignore", targetfile);
-
-   // get it
+   // make the server configurable
+   server = _config->Find("Apt::Changelogs::Server",
+                          "http://packages.debian.org/");
+   // ... but not the format string to avoid all possible attacks
+   strprintf(path, "/changelogs/pool/%s/%s/%s/%s_%s/changelog", src_section.c_str(), prefix.c_str(), srcpkg.c_str(), srcpkg.c_str(), verstr.c_str());
+   // queue it
+   string changelog_uri = server+path;
+   strprintf(descr, _("Changelog for %s (%s)"), srcpkg.c_str(), changelog_uri.c_str());
+   new pkgAcqFile(&Fetcher, uri, "", 0, descr, srcpkg, "ignored", targetfile);
+
+   // try downloading it, if that fails, they third-party-changelogs location
+   // FIXME: res is "Continue" even if I get a 404?!?
    int res = Fetcher.Run();
+   if (!FileExists(targetfile))
+   {
+      string third_party_uri;
+      if (GuessThirdPartyChangelogUri(CacheFile, Pkg, V, third_party_uri))
+      {
+         strprintf(descr, _("Changelog for %s (%s)"), srcpkg.c_str(), third_party_uri.c_str());
+         new pkgAcqFile(&Fetcher, third_party_uri, "", 0, descr, srcpkg, "ignored", targetfile);
+         res = Fetcher.Run();
+      }
+   }
+
    if (FileExists(targetfile))
-      return targetfile;
+      return true;
 
    // error
-   return "";
+   return _error->Error("changelog download failed");
 }
                                                                        /*}}}*/
 // DisplayFileInPager - Display File with pager                                /*{{{*/
@@ -2815,20 +2913,30 @@ bool DoChangelog(CommandLine &CmdL)
    APT::VersionSet verset = APT::VersionSet::FromCommandLine(Cache,
                CmdL.FileList + 1, APT::VersionSet::CANDIDATE, helper);
    pkgAcquire Fetcher;
+   AcqTextStatus Stat(ScreenWidth, _config->FindI("quiet",0));
+   Fetcher.Setup(&Stat);
 
    if (verset.empty() == true)
       return false;
+   char *tmpdir = mkdtemp(strdup("/tmp/apt-changelog-XXXXXX"));
+   if (tmpdir == NULL) {
+      return _error->Errno("mkdtemp", "mkdtemp failed");
+   }
+   
    for (APT::VersionSet::const_iterator Ver = verset.begin(); 
         Ver != verset.end(); 
         ++Ver) 
    {
-      string changelogfile = DownloadChangelog(Cache, Fetcher, Ver);
-      if (changelogfile.size() > 0)
-      {
+      string changelogfile = string(tmpdir) + "changelog";
+      if (DownloadChangelog(Cache, Fetcher, Ver, changelogfile))
          DisplayFileInPager(changelogfile);
-         unlink(changelogfile.c_str());
-      }
+      // cleanup temp file
+      unlink(changelogfile.c_str());
    }
+   // clenaup tmp dir
+   rmdir(tmpdir);
+   free(tmpdir);
+   return true;
 }
                                                                        /*}}}*/
 // DoMoo - Never Ask, Never Tell                                       /*{{{*/
@@ -3021,6 +3129,7 @@ int main(int argc,const char *argv[])                                     /*{{{*/
                                    {"autoclean",&DoAutoClean},
                                    {"check",&DoCheck},
                                   {"source",&DoSource},
+                                   {"download",&DoDownload},
                                    {"changelog",&DoChangelog},
                                   {"moo",&DoMoo},
                                   {"help",&ShowHelp},