]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/acquire-item.cc
support multiple fingerprints in signed-by
[apt.git] / apt-pkg / acquire-item.cc
index b2e578629fd0b2fabaf658b458ce5e9a5276587d..fde45e1fe9a8a37b7ceb5c9e1a4a04b36ffb0841 100644 (file)
@@ -132,6 +132,49 @@ static std::string GetDiffIndexURI(IndexTarget const &Target)              /*{{{*/
 }
                                                                        /*}}}*/
 
+static void ReportMirrorFailureToCentral(pkgAcquire::Item const &I, std::string const &FailCode, std::string const &Details)/*{{{*/
+{
+   // we only act if a mirror was used at all
+   if(I.UsedMirror.empty())
+      return;
+#if 0
+   std::cerr << "\nReportMirrorFailure: "
+            << UsedMirror
+            << " Uri: " << DescURI()
+            << " FailCode: "
+            << FailCode << std::endl;
+#endif
+   string const report = _config->Find("Methods::Mirror::ProblemReporting",
+                                "/usr/lib/apt/apt-report-mirror-failure");
+   if(!FileExists(report))
+      return;
+
+   std::vector<char const*> const Args = {
+      report.c_str(),
+      I.UsedMirror.c_str(),
+      I.DescURI().c_str(),
+      FailCode.c_str(),
+      Details.c_str(),
+      NULL
+   };
+
+   pid_t pid = ExecFork();
+   if(pid < 0)
+   {
+      _error->Error("ReportMirrorFailure Fork failed");
+      return;
+   }
+   else if(pid == 0)
+   {
+      execvp(Args[0], (char**)Args.data());
+      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'", report.c_str());
+}
+                                                                       /*}}}*/
+
 static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/
 {
    if (isError)
@@ -295,6 +338,21 @@ bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
       Status = StatDone;
       return false;
    }
+   // If we got the InRelease file via a mirror, pick all indexes directly from this mirror, too
+   if (TransactionManager != nullptr && TransactionManager->BaseURI.empty() == false &&
+        URI::SiteOnly(Item.URI) != URI::SiteOnly(TransactionManager->BaseURI))
+   {
+      // this ensures we rewrite only once and only the first step
+      auto const OldBaseURI = Target.Option(IndexTarget::BASE_URI);
+      if (APT::String::Startswith(Item.URI, OldBaseURI))
+      {
+        auto const ExtraPath = Item.URI.substr(OldBaseURI.length());
+        Item.URI = flCombine(TransactionManager->BaseURI, ExtraPath);
+        UsedMirror = TransactionManager->UsedMirror;
+        if (Item.Description.find(" ") != string::npos)
+           Item.Description.replace(0, Item.Description.find(" "), UsedMirror);
+      }
+   }
    return pkgAcquire::Item::QueueURI(Item);
 }
 /* The transition manager InRelease itself (or its older sisters-in-law
@@ -437,7 +495,7 @@ bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
         } else {
            if(Debug == true)
               std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
-           if (RemoveFile("TransactionCommit", DestFile) == false)
+           if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
               return false;
         }
         break;
@@ -470,7 +528,7 @@ bool pkgAcqIndex::TransactionState(TransactionStates const state)
         break;
       case TransactionCommit:
         if (EraseFileName.empty() == false)
-           RemoveFile("TransactionCommit", EraseFileName);
+           RemoveFile("AcqIndex::TransactionCommit", EraseFileName);
         break;
    }
    return true;
@@ -519,6 +577,41 @@ class APT_HIDDEN NoActionItem : public pkgAcquire::Item                    /*{{{*/
    }
 };
                                                                        /*}}}*/
+class APT_HIDDEN CleanupItem : public pkgAcqTransactionItem            /*{{{*/
+/* This class ensures that a file which was configured but isn't downloaded
+   for various reasons isn't kept in an old version in the lists directory.
+   In a way its the reverse of NoActionItem as it helps with removing files
+   even if the lists-cleanup is deactivated. */
+{
+   public:
+   virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
+   virtual HashStringList GetExpectedHashes()  const APT_OVERRIDE {return HashStringList();};
+
+   CleanupItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) :
+      pkgAcqTransactionItem(Owner, TransactionManager, Target)
+   {
+      Status = StatDone;
+      DestFile = GetFinalFileNameFromURI(Target.URI);
+   }
+   bool TransactionState(TransactionStates const state) APT_OVERRIDE
+   {
+      switch (state)
+      {
+        case TransactionStarted:
+           break;
+        case TransactionAbort:
+           break;
+        case TransactionCommit:
+           if (_config->FindB("Debug::Acquire::Transaction", false) == true)
+              std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
+           if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
+              return false;
+           break;
+      }
+      return true;
+   }
+};
+                                                                       /*}}}*/
 
 // Acquire::Item::Item - Constructor                                   /*{{{*/
 APT_IGNORE_DEPRECATED_PUSH
@@ -572,8 +665,6 @@ APT_CONST bool pkgAcquire::Item::IsTrusted() const                  /*{{{*/
    fetch this object */
 void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
 {
-   if(ErrorText.empty())
-      ErrorText = LookupTag(Message,"Message");
    if (QueueCounter <= 1)
    {
       /* This indicates that the file is not available right now but might
@@ -604,16 +695,63 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con
    }
 
    string const FailReason = LookupTag(Message, "FailReason");
-   if (FailReason == "MaximumSizeExceeded")
-      RenameOnError(MaximumSizeExceeded);
+   enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, OTHER } failreason = OTHER;
+   if ( FailReason == "MaximumSizeExceeded")
+      failreason = MAXIMUM_SIZE_EXCEEDED;
    else if (Status == StatAuthError)
-      RenameOnError(HashSumMismatch);
+      failreason = HASHSUM_MISMATCH;
+
+   if(ErrorText.empty())
+   {
+      if (Status == StatAuthError)
+      {
+        std::ostringstream out;
+        switch (failreason)
+        {
+           case HASHSUM_MISMATCH:
+              out << _("Hash Sum mismatch") << std::endl;
+              break;
+           case MAXIMUM_SIZE_EXCEEDED:
+           case OTHER:
+              out << LookupTag(Message, "Message") << std::endl;
+              break;
+        }
+        auto const ExpectedHashes = GetExpectedHashes();
+        if (ExpectedHashes.empty() == false)
+        {
+           out << "Hashes of expected file:" << std::endl;
+           for (auto const &hs: ExpectedHashes)
+              out << " - " << hs.toStr() << std::endl;
+        }
+        if (failreason == HASHSUM_MISMATCH)
+        {
+           out << "Hashes of received file:" << std::endl;
+           for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+           {
+              std::string const tagname = std::string(*type) + "-Hash";
+              std::string const hashsum = LookupTag(Message, tagname.c_str());
+              if (hashsum.empty() == false)
+                 out << " - " << HashString(*type, hashsum).toStr() << std::endl;
+           }
+           out << "Last modification reported: " << LookupTag(Message, "Last-Modified", "<none>") << std::endl;
+        }
+        ErrorText = out.str();
+      }
+      else
+        ErrorText = LookupTag(Message,"Message");
+   }
+
+   switch (failreason)
+   {
+      case MAXIMUM_SIZE_EXCEEDED: RenameOnError(MaximumSizeExceeded); break;
+      case HASHSUM_MISMATCH: RenameOnError(HashSumMismatch); break;
+      case OTHER: break;
+   }
 
-   // report mirror failure back to LP if we actually use a mirror
    if (FailReason.empty() == false)
-      ReportMirrorFailure(FailReason);
+      ReportMirrorFailureToCentral(*this, FailReason, ErrorText);
    else
-      ReportMirrorFailure(ErrorText);
+      ReportMirrorFailureToCentral(*this, ErrorText, ErrorText);
 
    if (QueueCounter > 1)
       Status = StatIdle;
@@ -701,13 +839,10 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const
    {
       case HashSumMismatch:
         errtext = _("Hash Sum mismatch");
-        Status = StatAuthError;
-        ReportMirrorFailure("HashChecksumFailure");
         break;
       case SizeMismatch:
         errtext = _("Size mismatch");
         Status = StatAuthError;
-        ReportMirrorFailure("SizeFailure");
         break;
       case InvalidFormat:
         errtext = _("Invalid file format");
@@ -724,7 +859,6 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const
         break;
       case MaximumSizeExceeded:
         // the method is expected to report a good error for this
-        Status = StatError;
         break;
       case PDiffError:
         // no handling here, done by callers
@@ -742,47 +876,9 @@ void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
 }
                                                                        /*}}}*/
 // Acquire::Item::ReportMirrorFailure                                  /*{{{*/
-void pkgAcquire::Item::ReportMirrorFailure(string const &FailCode)
+void pkgAcquire::Item::ReportMirrorFailure(std::string const &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
-   string report = _config->Find("Methods::Mirror::ProblemReporting", 
-                                "/usr/lib/apt/apt-report-mirror-failure");
-   if(!FileExists(report))
-      return;
-
-   std::vector<char const*> Args;
-   Args.push_back(report.c_str());
-   Args.push_back(UsedMirror.c_str());
-   Args.push_back(DescURI().c_str());
-   Args.push_back(FailCode.c_str());
-   Args.push_back(NULL);
-
-   pid_t pid = ExecFork();
-   if(pid < 0)
-   {
-      _error->Error("ReportMirrorFailure Fork failed");
-      return;
-   }
-   else if(pid == 0)
-   {
-      execvp(Args[0], (char**)Args.data());
-      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());
-   }
+   ReportMirrorFailureToCentral(*this, FailCode, FailCode);
 }
                                                                        /*}}}*/
 std::string pkgAcquire::Item::HashSum() const                          /*{{{*/
@@ -939,37 +1035,36 @@ void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
 }
                                                                        /*}}}*/
 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error   /*{{{*/
+/* This method is called from ::Failed handlers. If it returns true,
+   no fallback to other files or modi is performed */
 bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
 {
-   // FIXME: this entire function can do now that we disallow going to
-   //        a unauthenticated state and can cleanly rollback
-
    string const Final = I->GetFinalFilename();
-   if(FileExists(Final))
+   std::string const GPGError = LookupTag(Message, "Message");
+   if (FileExists(Final))
    {
       I->Status = StatTransientNetworkError;
-      _error->Warning(_("An error occurred during the signature "
-                        "verification. The repository is not updated "
-                        "and the previous index files will be used. "
-                        "GPG error: %s: %s"),
-                      Desc.Description.c_str(),
-                      LookupTag(Message,"Message").c_str());
+      _error->Warning(_("An error occurred during the signature verification. "
+              "The repository is not updated and the previous index files will be used. "
+              "GPG error: %s: %s"),
+           Desc.Description.c_str(),
+           GPGError.c_str());
       RunScripts("APT::Update::Auth-Failure");
       return true;
    } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
       /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
       _error->Error(_("GPG error: %s: %s"),
-                    Desc.Description.c_str(),
-                    LookupTag(Message,"Message").c_str());
+           Desc.Description.c_str(),
+           GPGError.c_str());
       I->Status = StatAuthError;
       return true;
    } else {
       _error->Warning(_("GPG error: %s: %s"),
-                      Desc.Description.c_str(),
-                      LookupTag(Message,"Message").c_str());
+           Desc.Description.c_str(),
+           GPGError.c_str());
    }
    // gpgv method failed
-   ReportMirrorFailure("GPGFailure");
+   ReportMirrorFailureToCentral(*this, "GPGFailure", GPGError);
    return false;
 }
                                                                        /*}}}*/
@@ -1007,6 +1102,15 @@ bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const st
    // We have just finished downloading a Release file (it is not
    // verified yet)
 
+   // Save the final base URI we got this Release file from
+   if (I->UsedMirror.empty() == false && _config->FindB("Acquire::SameMirrorForAllIndexes", true))
+   {
+      if (APT::String::Endswith(I->Desc.URI, "InRelease"))
+        TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("InRelease"));
+      else
+        TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("Release"));
+   }
+
    std::string const FileName = LookupTag(Message,"Filename");
    if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
    {
@@ -1113,10 +1217,12 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                    /*{{{*/
       // than invent an entirely new flag we would need to carry for all of eternity.
       if (Target->Option(IndexTarget::ARCHITECTURE) == "all")
       {
-        if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false)
-           continue;
-        if (TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false)
+        if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false ||
+              TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false)
+        {
+           new CleanupItem(Owner, TransactionManager, *Target);
            continue;
+        }
       }
 
       bool trypdiff = Target->OptionBool(IndexTarget::PDIFFS);
@@ -1126,13 +1232,17 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                    /*{{{*/
         {
            // optional targets that we do not have in the Release file are skipped
            if (Target->IsOptional)
+           {
+              new CleanupItem(Owner, TransactionManager, *Target);
               continue;
+           }
 
            std::string const &arch = Target->Option(IndexTarget::ARCHITECTURE);
            if (arch.empty() == false)
            {
               if (TransactionManager->MetaIndexParser->IsArchitectureSupported(arch) == false)
               {
+                 new CleanupItem(Owner, TransactionManager, *Target);
                  _error->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"),
                        Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str(), arch.c_str());
                  continue;
@@ -1141,7 +1251,10 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
               // ignore silently as this is pretty much the same as just shipping an empty file.
               // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
               if (TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
+              {
+                 new CleanupItem(Owner, TransactionManager, *Target);
                  continue;
+              }
            }
 
            Status = StatAuthError;
@@ -1151,11 +1264,21 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                    /*{{{*/
         else
         {
            auto const hashes = GetExpectedHashesFor(Target->MetaKey);
-           if (hashes.usable() == false && hashes.empty() == false)
+           if (hashes.empty() == false)
            {
-              _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
+              if (hashes.usable() == false)
+              {
+                 new CleanupItem(Owner, TransactionManager, *Target);
+                 _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
                        Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str());
-              continue;
+                 continue;
+              }
+              // empty files are skipped as acquiring the very small compressed files is a waste of time
+              else if (hashes.FileSize() == 0)
+              {
+                 new CleanupItem(Owner, TransactionManager, *Target);
+                 continue;
+              }
            }
         }
 
@@ -1242,25 +1365,8 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
    }
 }
                                                                        /*}}}*/
-bool pkgAcqMetaBase::VerifyVendor(string const &Message)               /*{{{*/
+bool pkgAcqMetaBase::VerifyVendor(string const &)                      /*{{{*/
 {
-   string::size_type pos;
-
-   // check for missing sigs (that where not fatal because otherwise we had
-   // bombed earlier)
-   string missingkeys;
-   string msg = _("There is no public key available for the "
-                 "following key IDs:\n");
-   pos = Message.find("NO_PUBKEY ");
-   if (pos != std::string::npos)
-   {
-      string::size_type start = pos+strlen("NO_PUBKEY ");
-      string Fingerprint = Message.substr(start, Message.find("\n")-start);
-      missingkeys += (Fingerprint);
-   }
-   if(!missingkeys.empty())
-      _error->Warning("%s", (msg + missingkeys).c_str());
-
    string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
 
    if (Transformed == "../project/experimental")
@@ -1268,7 +1374,7 @@ bool pkgAcqMetaBase::VerifyVendor(string const &Message)          /*{{{*/
       Transformed = "experimental";
    }
 
-   pos = Transformed.rfind('/');
+   auto pos = Transformed.rfind('/');
    if (pos != string::npos)
    {
       Transformed = Transformed.substr(0, pos);
@@ -1709,6 +1815,22 @@ pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
       IndexTarget const &Target)
 : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
 {
+}
+                                                                       /*}}}*/
+void pkgAcqBaseIndex::Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+   pkgAcquire::Item::Failed(Message, Cnf);
+   if (TransactionManager == nullptr || TransactionManager->MetaIndexParser == nullptr ||
+        Status != StatAuthError)
+      return;
+
+   ErrorText.append("Release file created at: ");
+   auto const timespec = TransactionManager->MetaIndexParser->GetDate();
+   if (timespec == 0)
+      ErrorText.append("<unknown>");
+   else
+      ErrorText.append(TimeRFC1123(timespec));
+   ErrorText.append("\n");
 }
                                                                        /*}}}*/
 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
@@ -2150,7 +2272,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
                                                                        /*}}}*/
 void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
 {
-   Item::Failed(Message,Cnf);
+   pkgAcqBaseIndex::Failed(Message,Cnf);
    Status = StatDone;
 
    if(Debug)
@@ -2231,7 +2353,7 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
                                                                        /*}}}*/
 void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
 {
-   Item::Failed(Message,Cnf);
+   pkgAcqBaseIndex::Failed(Message,Cnf);
    Status = StatDone;
 
    DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
@@ -2436,7 +2558,7 @@ void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfi
    if(Debug)
       std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
 
-   Item::Failed(Message,Cnf);
+   pkgAcqBaseIndex::Failed(Message,Cnf);
    Status = StatDone;
 
    // check if we are the first to fail, otherwise we are done here
@@ -2674,7 +2796,7 @@ string pkgAcqIndex::Custom600Headers() const
 // AcqIndex::Failed - getting the indexfile failed                     /*{{{*/
 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
 {
-   Item::Failed(Message,Cnf);
+   pkgAcqBaseIndex::Failed(Message,Cnf);
 
    // authorisation matches will not be fixed by other compression types
    if (Status != StatAuthError)
@@ -2748,9 +2870,8 @@ void pkgAcqIndex::StageDownloadDone(string const &Message)
    // methods like file:// give us an alternative (uncompressed) file
    else if (Target.KeepCompressed == false && AltFilename.empty() == false)
    {
-      if (CurrentCompressionExtension != "uncompressed")
-        DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1));
       Filename = AltFilename;
+      EraseFileName.clear();
    }
    // Methods like e.g. "file:" will give us a (compressed) FileName that is
    // not the "DestFile" we set, in this case we uncompress from the local file
@@ -2780,7 +2901,7 @@ void pkgAcqIndex::StageDownloadDone(string const &Message)
       DestFile = "/dev/null";
    }
 
-   if (EraseFileName.empty())
+   if (EraseFileName.empty() && Filename != AltFilename)
       EraseFileName = Filename;
 
    // queue uri for the next stage