]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/acquire-item.cc
* apt-pkg/deb/dpkgpm.cc:
[apt.git] / apt-pkg / acquire-item.cc
index 421288007181fe408787f7ada7fcfe663bcaeb33..b7b16b9a2301611b1cf57a4d5cb8acecd0196a2f 100644 (file)
@@ -13,9 +13,6 @@
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "apt-pkg/acquire-item.h"
-#endif
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/configuration.h>
 #include <apt-pkg/sourcelist.h>
@@ -24,6 +21,8 @@
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/tagfile.h>
 
 #include <apti18n.h>
     
@@ -31,6 +30,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string>
+#include <sstream>
 #include <stdio.h>
                                                                        /*}}}*/
 
@@ -63,6 +63,7 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
    Status = StatIdle;
    ErrorText = LookupTag(Message,"Message");
+   UsedMirror =  LookupTag(Message,"UsedMirror");
    if (QueueCounter <= 1)
    {
       /* This indicates that the file is not available right now but might
@@ -75,10 +76,17 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
         Dequeue();
         return;
       }
-      
+
       Status = StatError;
       Dequeue();
    }   
+   
+   // report mirror failure back to LP if we actually use a mirror
+   string FailReason = LookupTag(Message, "FailReason");
+   if(FailReason.size() != 0)
+      ReportMirrorFailure(FailReason);
+   else
+      ReportMirrorFailure(ErrorText);
 }
                                                                        /*}}}*/
 // Acquire::Item::Start - Item has begun to download                   /*{{{*/
@@ -100,7 +108,8 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
 {
    // We just downloaded something..
    string FileName = LookupTag(Message,"Filename");
-   if (Complete == false && FileName == DestFile)
+   UsedMirror =  LookupTag(Message,"UsedMirror");
+   if (Complete == false && !Local && FileName == DestFile)
    {
       if (Owner->Log != 0)
         Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
@@ -108,7 +117,6 @@ void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
 
    if (FileSize == 0)
       FileSize= Size;
-   
    Status = StatDone;
    ErrorText = string();
    Owner->Dequeue(this);
@@ -131,14 +139,475 @@ void pkgAcquire::Item::Rename(string From,string To)
 }
                                                                        /*}}}*/
 
+void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
+{
+   // we only act if a mirror was used at all
+   if(UsedMirror.empty())
+      return;
+#if 0
+   std::cerr << "\nReportMirrorFailure: " 
+            << UsedMirror
+            << " Uri: " << DescURI()
+            << " FailCode: " 
+            << FailCode << std::endl;
+#endif
+   const char *Args[40];
+   unsigned int i = 0;
+   string report = _config->Find("Methods::Mirror::ProblemReporting", 
+                                "/usr/lib/apt/apt-report-mirror-failure");
+   if(!FileExists(report))
+      return;
+   Args[i++] = report.c_str();
+   Args[i++] = UsedMirror.c_str();
+   Args[i++] = DescURI().c_str();
+   Args[i++] = FailCode.c_str();
+   Args[i++] = NULL;
+   pid_t pid = ExecFork();
+   if(pid < 0) 
+   {
+      _error->Error("ReportMirrorFailure Fork failed");
+      return;
+   }
+   else if(pid == 0) 
+   {
+      execvp(Args[0], (char**)Args);
+      std::cerr << "Could not exec " << Args[0] << std::endl;
+      _exit(100);
+   }
+   if(!ExecWait(pid, "report-mirror-failure")) 
+   {
+      _error->Warning("Couldn't report problem to '%s'",
+                     _config->Find("Methods::Mirror::ProblemReporting").c_str());
+   }
+}
+
+
+
+// AcqDiffIndex::AcqDiffIndex - Constructor                    
+// ---------------------------------------------------------------------
+/* Get the DiffIndex file first and see if there are patches availabe 
+ * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
+ * patches. If anything goes wrong in that process, it will fall back to
+ * the original packages file
+ */
+pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
+                                string URI,string URIDesc,string ShortDesc,
+                                string ExpectedMD5)
+   : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), Description(URIDesc)
+{
+   
+   Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+   Desc.Description = URIDesc + "/DiffIndex";
+   Desc.Owner = this;
+   Desc.ShortDesc = ShortDesc;
+   Desc.URI = URI + ".diff/Index";
+
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(URI) + string(".DiffIndex");
+
+   if(Debug)
+      std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
+
+   // look for the current package file
+   CurrentPackagesFile = _config->FindDir("Dir::State::lists");
+   CurrentPackagesFile += URItoFileName(RealURI);
+
+   // FIXME: this file:/ check is a hack to prevent fetching
+   //        from local sources. this is really silly, and
+   //        should be fixed cleanly as soon as possible
+   if(!FileExists(CurrentPackagesFile) || 
+      Desc.URI.substr(0,strlen("file:/")) == "file:/")
+   {
+      // we don't have a pkg file or we don't want to queue
+      if(Debug)
+        std::clog << "No index file, local or canceld by user" << std::endl;
+      Failed("", NULL);
+      return;
+   }
+
+   if(Debug) 
+      std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): " 
+               << CurrentPackagesFile << std::endl;
+   
+   QueueURI(Desc);
+
+}
+
+// AcqIndex::Custom600Headers - Insert custom request headers          /*{{{*/
+// ---------------------------------------------------------------------
+/* The only header we use is the last-modified header. */
+string pkgAcqDiffIndex::Custom600Headers()
+{
+   string Final = _config->FindDir("Dir::State::lists");
+   Final += URItoFileName(RealURI) + string(".IndexDiff");
+   
+   if(Debug)
+      std::clog << "Custom600Header-IMS: " << Final << std::endl;
+
+   struct stat Buf;
+   if (stat(Final.c_str(),&Buf) != 0)
+      return "\nIndex-File: true";
+   
+   return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+}
+
+
+bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)
+{
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile 
+               << std::endl;
+
+   pkgTagSection Tags;
+   string ServerSha1;
+   vector<DiffInfo> available_patches;
+   
+   FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
+   pkgTagFile TF(&Fd);
+   if (_error->PendingError() == true)
+      return false;
+
+   if(TF.Step(Tags) == true)
+   {
+      string local_sha1;
+      bool found = false;
+      DiffInfo d;
+      string size;
+
+      string tmp = Tags.FindS("SHA1-Current");
+      std::stringstream ss(tmp);
+      ss >> ServerSha1;
+
+      FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
+      SHA1Summation SHA1;
+      SHA1.AddFD(fd.Fd(), fd.Size());
+      local_sha1 = string(SHA1.Result());
+
+      if(local_sha1 == ServerSha1) 
+      {
+        // we have the same sha1 as the server
+        if(Debug)
+           std::clog << "Package file is up-to-date" << std::endl;
+        // set found to true, this will queue a pkgAcqIndexDiffs with
+        // a empty availabe_patches
+        found = true;
+      } 
+      else 
+      {
+        if(Debug)
+           std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
+
+        // check the historie and see what patches we need
+        string history = Tags.FindS("SHA1-History");     
+        std::stringstream hist(history);
+        while(hist >> d.sha1 >> size >> d.file) 
+        {
+           d.size = atoi(size.c_str());
+           // read until the first match is found
+           if(d.sha1 == local_sha1) 
+              found=true;
+           // from that point on, we probably need all diffs
+           if(found) 
+           {
+              if(Debug)
+                 std::clog << "Need to get diff: " << d.file << std::endl;
+              available_patches.push_back(d);
+           }
+        }
+      }
+
+      // we have something, queue the next diff
+      if(found) 
+      {
+        // queue the diffs
+        int last_space = Description.rfind(" ");
+        if(last_space != string::npos)
+           Description.erase(last_space, Description.size()-last_space);
+        new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
+                             ExpectedMD5, available_patches);
+        Complete = false;
+        Status = StatDone;
+        Dequeue();
+        return true;
+      }
+   }
+   
+   // Nothing found, report and return false
+   // Failing here is ok, if we return false later, the full
+   // IndexFile is queued
+   if(Debug)
+      std::clog << "Can't find a patch in the index file" << std::endl;
+   return false;
+}
+
+void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+{
+   if(Debug)
+      std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl
+               << "Falling back to normal index file aquire" << std::endl;
+
+   new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc, 
+                  ExpectedMD5);
+
+   Complete = false;
+   Status = StatDone;
+   Dequeue();
+}
+
+void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash,
+                          pkgAcquire::MethodConfig *Cnf)
+{
+   if(Debug)
+      std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
+
+   Item::Done(Message,Size,Md5Hash,Cnf);
+
+   string FinalFile;
+   FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
+
+   // sucess in downloading the index
+   // rename the index
+   FinalFile += string(".IndexDiff");
+   if(Debug)
+      std::clog << "Renaming: " << DestFile << " -> " << FinalFile 
+               << std::endl;
+   Rename(DestFile,FinalFile);
+   chmod(FinalFile.c_str(),0644);
+   DestFile = FinalFile;
+
+   if(!ParseDiffIndex(DestFile))
+      return Failed("", NULL);
+
+   Complete = true;
+   Status = StatDone;
+   Dequeue();
+   return;
+}
+
+
+
+// AcqIndexDiffs::AcqIndexDiffs - Constructor                  
+// ---------------------------------------------------------------------
+/* The package diff is added to the queue. one object is constructed
+ * for each diff and the index
+ */
+pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
+                                  string URI,string URIDesc,string ShortDesc,
+                                  string ExpectedMD5, vector<DiffInfo> diffs)
+   : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), 
+     available_patches(diffs)
+{
+   
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(URI);
+
+   Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+   Description = URIDesc;
+   Desc.Owner = this;
+   Desc.ShortDesc = ShortDesc;
+
+   if(available_patches.size() == 0) 
+   {
+      // we are done (yeah!)
+      Finish(true);
+   }
+   else
+   {
+      // get the next diff
+      State = StateFetchDiff;
+      QueueNextDiff();
+   }
+}
+
+
+void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+{
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
+               << "Falling back to normal index file aquire" << std::endl;
+   new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc, 
+                  ExpectedMD5);
+   Finish();
+}
+
+
+// helper that cleans the item out of the fetcher queue
+void pkgAcqIndexDiffs::Finish(bool allDone)
+{
+   // we restore the original name, this is required, otherwise
+   // the file will be cleaned
+   if(allDone) 
+   {
+      DestFile = _config->FindDir("Dir::State::lists");
+      DestFile += URItoFileName(RealURI);
+
+      // do the final md5sum checking
+      MD5Summation sum;
+      FileFd Fd(DestFile, FileFd::ReadOnly);
+      sum.AddFD(Fd.Fd(), Fd.Size());
+      Fd.Close();
+      string MD5 = (string)sum.Result();
+
+      if (!ExpectedMD5.empty() && MD5 != ExpectedMD5)
+      {
+        Status = StatAuthError;
+        ErrorText = _("MD5Sum mismatch");
+        Rename(DestFile,DestFile + ".FAILED");
+        Dequeue();
+        return;
+      }
+
+      // this is for the "real" finish
+      Complete = true;
+      Status = StatDone;
+      Dequeue();
+      if(Debug)
+        std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
+      return;
+   }
+
+   if(Debug)
+      std::clog << "Finishing: " << Desc.URI << std::endl;
+   Complete = false;
+   Status = StatDone;
+   Dequeue();
+   return;
+}
+
+
+
+bool pkgAcqIndexDiffs::QueueNextDiff()
+{
+
+   // calc sha1 of the just patched file
+   string FinalFile = _config->FindDir("Dir::State::lists");
+   FinalFile += URItoFileName(RealURI);
+
+   FileFd fd(FinalFile, FileFd::ReadOnly);
+   SHA1Summation SHA1;
+   SHA1.AddFD(fd.Fd(), fd.Size());
+   string local_sha1 = string(SHA1.Result());
+   if(Debug)
+      std::clog << "QueueNextDiff: " 
+               << FinalFile << " (" << local_sha1 << ")"<<std::endl;
+
+   // remove all patches until the next matching patch is found
+   // this requires the Index file to be ordered
+   for(vector<DiffInfo>::iterator I=available_patches.begin();
+       available_patches.size() > 0 && 
+         I != available_patches.end() &&
+         (*I).sha1 != local_sha1; 
+       I++) 
+   {
+      available_patches.erase(I);
+   }
+
+   // error checking and falling back if no patch was found
+   if(available_patches.size() == 0) 
+   { 
+      Failed("", NULL);
+      return false;
+   }
+
+   // queue the right diff
+   Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz";
+   Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
+   DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
+
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
+   
+   QueueURI(Desc);
+
+   return true;
+}
+
+
+
+void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash,
+                           pkgAcquire::MethodConfig *Cnf)
+{
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
+
+   Item::Done(Message,Size,Md5Hash,Cnf);
+
+   string FinalFile;
+   FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
+
+   // sucess in downloading a diff, enter ApplyDiff state
+   if(State == StateFetchDiff) 
+   {
+
+      if(Debug)
+        std::clog << "Sending to gzip method: " << FinalFile << std::endl;
+
+      string FileName = LookupTag(Message,"Filename");
+      State = StateUnzipDiff;
+      Local = true;
+      Desc.URI = "gzip:" + FileName;
+      DestFile += ".decomp";
+      QueueURI(Desc);
+      Mode = "gzip";
+      return;
+   } 
+
+   // sucess in downloading a diff, enter ApplyDiff state
+   if(State == StateUnzipDiff) 
+   {
+
+      // rred excepts the patch as $FinalFile.ed
+      Rename(DestFile,FinalFile+".ed");
+
+      if(Debug)
+        std::clog << "Sending to rred method: " << FinalFile << std::endl;
+
+      State = StateApplyDiff;
+      Local = true;
+      Desc.URI = "rred:" + FinalFile;
+      QueueURI(Desc);
+      Mode = "rred";
+      return;
+   } 
+
+
+   // success in download/apply a diff, queue next (if needed)
+   if(State == StateApplyDiff)
+   {
+      // remove the just applied patch
+      available_patches.erase(available_patches.begin());
+
+      // move into place
+      if(Debug) 
+      {
+        std::clog << "Moving patched file in place: " << std::endl
+                  << DestFile << " -> " << FinalFile << std::endl;
+      }
+      Rename(DestFile,FinalFile);
+      chmod(FinalFile.c_str(),0644);
+
+      // see if there is more to download
+      if(available_patches.size() > 0) {
+        new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
+                             ExpectedMD5, available_patches);
+        return Finish();
+      } else 
+        return Finish(true);
+   }
+}
+
+
 // AcqIndex::AcqIndex - Constructor                                    /*{{{*/
 // ---------------------------------------------------------------------
 /* The package file is added to the queue and a second class is 
    instantiated to fetch the revision file */   
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
                         string URI,string URIDesc,string ShortDesc,
-                        string ExpectedMD5, string comprExt) :
-   Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
+                        string ExpectedMD5, string comprExt)
+   Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
 {
    Decompression = false;
    Erase = false;
@@ -176,7 +645,6 @@ string pkgAcqIndex::Custom600Headers()
    struct stat Buf;
    if (stat(Final.c_str(),&Buf) != 0)
       return "\nIndex-File: true";
-   
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
                                                                        /*}}}*/
@@ -194,9 +662,15 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       Complete = false;
       Dequeue();
       return;
+   } 
+   
+   // on decompression failure, remove bad versions in partial/
+   if(Decompression && Erase) {
+      string s = _config->FindDir("Dir::State::lists") + "partial/";
+      s += URItoFileName(RealURI);
+      unlink(s.c_str());
    }
 
-   
    Item::Failed(Message,Cnf);
 }
 
@@ -235,6 +709,7 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
          Status = StatAuthError;
          ErrorText = _("MD5Sum mismatch");
          Rename(DestFile,DestFile + ".FAILED");
+        ReportMirrorFailure("HashChecksumFailure");
          return;
       }
       // Done, move it into position
@@ -308,6 +783,35 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
    Mode = decompProg;
 }
 
+// AcqIndexTrans::pkgAcqIndexTrans - Constructor                       /*{{{*/
+// ---------------------------------------------------------------------
+/* The Translation file is added to the queue */
+pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
+                           string URI,string URIDesc,string ShortDesc) :
+                      pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, "", "")
+{
+}
+
+                                                                       /*}}}*/
+// AcqIndexTrans::Failed - Silence failure messages for missing files  /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+{
+   if (Cnf->LocalOnly == true || 
+       StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
+   {      
+      // Ignore this
+      Status = StatDone;
+      Complete = false;
+      Dequeue();
+      return;
+   }
+   
+   Item::Failed(Message,Cnf);
+}
+                                                                       /*}}}*/
+
 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
                             string URI,string URIDesc,string ShortDesc,
                             string MetaIndexURI, string MetaIndexURIDesc,
@@ -321,8 +825,9 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
    DestFile += URItoFileName(URI);
 
-   // remove any partial downloaded sig-file. it may confuse proxies
-   // and is too small to warrant a partial download anyway
+   // remove any partial downloaded sig-file in partial/. 
+   // it may confuse proxies and is too small to warrant a 
+   // partial download anyway
    unlink(DestFile.c_str());
 
    // Create the item
@@ -389,17 +894,22 @@ void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
                                                                        /*}}}*/
 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
+   string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
 
    // if we get a network error we fail gracefully
-   if(LookupTag(Message,"FailReason") == "Timeout" || 
-      LookupTag(Message,"FailReason") == "TmpResolveFailure" ||
-      LookupTag(Message,"FailReason") == "ConnectionRefused") {
+   if(Status == StatTransientNetworkError)
+   {
       Item::Failed(Message,Cnf);
+      // move the sigfile back on transient network failures 
+      if(FileExists(DestFile))
+        Rename(DestFile,Final);
+
+      // set the status back to , Item::Failed likes to reset it
+      Status = pkgAcquire::Item::StatTransientNetworkError;
       return;
    }
 
    // Delete any existing sigfile when the acquire failed
-   string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
    unlink(Final.c_str());
 
    // queue a pkgAcqMetaIndex with no sigfile
@@ -532,6 +1042,7 @@ void pkgAcqMetaIndex::RetrievalDone(string Message)
       // Move it into position
       Rename(DestFile,FinalFile);
    }
+   chmod(FinalFile.c_str(),0644);
    DestFile = FinalFile;
 }
 
@@ -601,9 +1112,14 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)
          }
       }
       
-      // Queue Packages file
-      new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
-                      (*Target)->ShortDesc, ExpectedIndexMD5);
+      // Queue Packages file (either diff or full packages files, depending
+      // on the users option)
+      if(_config->FindB("Acquire::PDiffs",false) == true) 
+        new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
+                            (*Target)->ShortDesc, ExpectedIndexMD5);
+      else 
+        new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
+                           (*Target)->ShortDesc, ExpectedIndexMD5);
    }
 }
 
@@ -637,7 +1153,7 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message)
    // check for missing sigs (that where not fatal because otherwise we had
    // bombed earlier)
    string missingkeys;
-   string msg = _("There are no public key available for the "
+   string msg = _("There is no public key available for the "
                  "following key IDs:\n");
    pos = Message.find("NO_PUBKEY ");
    if (pos != std::string::npos)
@@ -722,6 +1238,7 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       }
 
       // gpgv method failed 
+      ReportMirrorFailure("GPGFailure");
       _error->Warning("GPG error: %s: %s",
                       Desc.Description.c_str(),
                       LookupTag(Message,"Message").c_str());