Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / repo / PackageProvider.cc
index 0557284..fd52e1e 100644 (file)
 #include "zypp/RepoInfo.h"
 #include "zypp/RepoManager.h"
 
+#include "zypp/ZYppFactory.h"
+#include "zypp/Target.h"
+#include "zypp/target/rpm/RpmDb.h"
+#include "zypp/FileChecker.h"
+
 using std::endl;
 
 ///////////////////////////////////////////////////////////////////
@@ -54,6 +59,7 @@ namespace zypp
     ///////////////////////////////////////////////////////////////////
     class PackageProvider::Impl : private base::NonCopyable
     {
+      typedef callback::UserData UserData;
     public:
       /** Ctor taking the Package to provide. */
       Impl( RepoMediaAccess & access_r,
@@ -118,17 +124,14 @@ namespace zypp
        * Report start/problem/finish and retry loop are hadled by \ref providePackage.
        * Here you trigger just progress and delta/plugin callbacks as needed.
        *
-       * Proxy methods for progressPackageDownload and failOnChecksum are provided here.
-       * Create similar proxies for other progress callbacks in derived classes and link
-       * it to ProvideFilePolicy for download:
+       * Proxy method for progressPackageDownload is provided here.
        * \code
        * ProvideFilePolicy policy;
        * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
-       * policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
        * return _access.provideFile( _package->repoInfo(), loc, policy );
        * \endcode
        *
-       * \note The provoided default implementation retrieves the packages default
+       * \note The provided default implementation retrieves the packages default
        * location.
        */
       virtual ManagedFile doProvidePackage() const = 0;
@@ -142,27 +145,53 @@ namespace zypp
       bool progressPackageDownload( int value ) const
       {        return report()->progress( value, _package ); }
 
-      /** Redirect ProvideFilePolicy failOnChecksumError to this if needed. */
-      bool failOnChecksumError() const
+      typedef target::rpm::RpmDb RpmDb;
+
+      RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
       {
-       std::string package_str = _package->name() + "-" + _package->edition().asString();
+       if ( !_target )
+         _target = getZYpp()->getTarget();
+
+       RpmDb::CheckPackageResult ret = RpmDb::CHK_ERROR;
+       RpmDb::CheckPackageDetail detail;
+       if ( _target )
+         ret = _target->rpmDb().checkPackage( path_r, detail );
+       else
+         detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
+
+       userData.set( "CheckPackageResult", ret );
+       userData.set( "CheckPackageDetail", std::move(detail) );
+       return ret;
+      }
 
-       // TranslatorExplanation %s = package being checked for integrity
-       switch ( report()->problem( _package, repo::DownloadResolvableReport::INVALID, str::form(_("Package %s seems to be corrupted during transfer. Do you want to retry retrieval?"), package_str.c_str() ) ) )
+      /** React on signature verification error user action
+       * \note: IGNORE == accept insecure file (no SkipRequestException!)
+       */
+      void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
+      {
+       switch ( action_r )
        {
          case repo::DownloadResolvableReport::RETRY:
            _retry = true;
            break;
          case repo::DownloadResolvableReport::IGNORE:
-           ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
+           WAR << _package->asUserString() << ": " << "User requested to accept insecure file" << endl;
            break;
+         default:
          case repo::DownloadResolvableReport::ABORT:
            ZYPP_THROW(AbortRequestException("User requested to abort"));
            break;
-         default:
-           break;
        }
-       return true; // anyway a failure
+      }
+
+      /** Default signature verification error handling. */
+      void defaultReportSignatureError( RpmDb::CheckPackageResult ret, const std::string & detail_r = std::string() ) const
+      {
+       str::Str msg;
+       msg << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret;
+       if ( ! detail_r.empty() )
+         msg << "\n" << detail_r;
+       resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg.str() ) );
       }
 
     protected:
@@ -187,6 +216,7 @@ namespace zypp
 
       mutable bool               _retry;
       mutable shared_ptr<Report> _report;
+      mutable Target_Ptr         _target;
     };
     ///////////////////////////////////////////////////////////////////
 
@@ -202,7 +232,6 @@ namespace zypp
 
       ProvideFilePolicy policy;
       policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
-      policy.failOnChecksumErrorCB( bind( &Base::failOnChecksumError, this ) );
       return _access.provideFile( _package->repoInfo(), loc, policy );
     }
 
@@ -260,27 +289,92 @@ namespace zypp
       Url url = * info.baseUrlsBegin();
       do {
         _retry = false;
+       if ( ! ret->empty() )
+       {
+         ret.setDispose( filesystem::unlink );
+         ret.reset();
+       }
         report()->start( _package, url );
-        try  // ELIMINATE try/catch by providing a log-guard
+        try
           {
             ret = doProvidePackage();
+
+           if ( info.pkgGpgCheck() )
+           {
+             UserData userData( "pkgGpgCheck" );
+             userData.set( "Package", _package );
+             userData.set( "Localpath", ret.value() );
+             RpmDb::CheckPackageResult res = packageSigCheck( ret, userData );
+             // publish the checkresult, even if it is OK. Apps may want to report something...
+             report()->pkgGpgCheck( userData );
+USR << "CHK: " << res << endl;
+             if ( res != RpmDb::CHK_OK )
+             {
+               if ( userData.hasvalue( "Action" ) )    // pkgGpgCheck report provided an user error action
+               {
+                 resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
+               }
+               else if ( userData.haskey( "Action" ) ) // pkgGpgCheck requests the default problem report (wo. details)
+               {
+                 defaultReportSignatureError( res );
+               }
+               else                                    // no advice from user => usedefaults
+               {
+                 switch ( res )
+                 {
+                   case RpmDb::CHK_OK:         // Signature is OK
+                     break;
+
+                   case RpmDb::CHK_NOKEY:      // Public key is unavailable
+                   case RpmDb::CHK_NOTFOUND:   // Signature is unknown type
+                   case RpmDb::CHK_FAIL:       // Signature does not verify
+                   case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
+                   case RpmDb::CHK_ERROR:      // File does not exist or can't be opened
+                   default:
+                     // report problem (w. details), throw if to abort, else retry/ignore
+                     defaultReportSignatureError( res, str::Str() << userData.get<RpmDb::CheckPackageDetail>( "CheckPackageDetail" ) );
+                     break;
+                 }
+               }
+             }
+           }
           }
         catch ( const UserRequestException & excpt )
           {
-            // UserRequestException e.g. from failOnChecksumError was already reported.
             ERR << "Failed to provide Package " << _package << endl;
-            if ( ! _retry )
-              {
-                ZYPP_RETHROW( excpt );
-              }
+           if ( ! _retry )
+             ZYPP_RETHROW( excpt );
           }
+        catch ( const FileCheckException & excpt )
+          {
+           ERR << "Failed to provide Package " << _package << endl;
+           if ( ! _retry )
+           {
+             const std::string & package_str = _package->asUserString();
+             // TranslatorExplanation %s = package being checked for integrity
+             switch ( report()->problem( _package, repo::DownloadResolvableReport::INVALID, str::form(_("Package %s seems to be corrupted during transfer. Do you want to retry retrieval?"), package_str.c_str() ) ) )
+             {
+               case repo::DownloadResolvableReport::RETRY:
+                 _retry = true;
+                 break;
+               case repo::DownloadResolvableReport::IGNORE:
+                 ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
+                 break;
+               case repo::DownloadResolvableReport::ABORT:
+                 ZYPP_THROW(AbortRequestException("User requested to abort"));
+                 break;
+               default:
+                 break;
+             }
+           }
+         }
         catch ( const Exception & excpt )
           {
             ERR << "Failed to provide Package " << _package << endl;
             if ( ! _retry )
-              {
+           {
                 // Aything else gets reported
-                std::string package_str = _package->name() + "-" + _package->edition().asString();
+                const std::string & package_str = _package->asUserString();
 
                 // TranslatorExplanation %s = name of the package being processed.
                 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
@@ -292,7 +386,7 @@ namespace zypp
                         _retry = true;
                         break;
                       case repo::DownloadResolvableReport::IGNORE:
-                        ZYPP_THROW(SkipRequestException("User requested skip of corrupted file", excpt));
+                        ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
                         break;
                       case repo::DownloadResolvableReport::ABORT:
                         ZYPP_THROW(AbortRequestException("User requested to abort", excpt));