Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / repo / PackageProvider.cc
index 6b3f02a..fd52e1e 100644 (file)
  *
 */
 #include <iostream>
+#include <fstream>
 #include <sstream>
 #include "zypp/repo/PackageDelta.h"
 #include "zypp/base/Logger.h"
 #include "zypp/base/Gettext.h"
 #include "zypp/base/UserRequestException.h"
+#include "zypp/base/NonCopyable.h"
 #include "zypp/repo/PackageProvider.h"
-#include "zypp/repo/RepoProvideFile.h"
 #include "zypp/repo/Applydeltarpm.h"
 #include "zypp/repo/PackageDelta.h"
 
 #include "zypp/TmpPath.h"
 #include "zypp/ZConfig.h"
 #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;
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
   ///////////////////////////////////////////////////////////////////
   namespace repo
-  { /////////////////////////////////////////////////////////////////
-
+  {
     ///////////////////////////////////////////////////////////////////
-    //
-    // CLASS NAME : PackageProviderPolicy
-    //
+    // class PackageProviderPolicy
     ///////////////////////////////////////////////////////////////////
 
     bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
@@ -48,100 +52,329 @@ namespace zypp
       return false;
     }
 
+
     ///////////////////////////////////////////////////////////////////
-    //
-    // CLASS NAME : PackageProvider
-    //
+    /// \class PackageProvider::Impl
+    /// \brief PackageProvider implementation.
     ///////////////////////////////////////////////////////////////////
+    class PackageProvider::Impl : private base::NonCopyable
+    {
+      typedef callback::UserData UserData;
+    public:
+      /** Ctor taking the Package to provide. */
+      Impl( RepoMediaAccess & access_r,
+           const Package::constPtr & package_r,
+           const DeltaCandidates & deltas_r,
+           const PackageProviderPolicy & policy_r )
+      : _policy( policy_r )
+      , _package( package_r )
+      , _deltas( deltas_r )
+      , _access( access_r )
+      , _retry(false)
+      {}
+
+      virtual ~Impl() {}
+
+      /** Factory method providing the appropriate implementation.
+       * Called by PackageProvider ctor. Returned pointer should be
+       * immediately wrapped into a smartpointer.
+       */
+      static Impl * factoryMake( RepoMediaAccess & access_r,
+                                const Package::constPtr & package_r,
+                                const DeltaCandidates & deltas_r,
+                                const PackageProviderPolicy & policy_r );
+
+    public:
+      /** Provide the package.
+       * The basic workflow.
+       * \throws Exception.
+       */
+      ManagedFile providePackage() const;
+
+      /** Provide the package if it is cached. */
+      ManagedFile providePackageFromCache() const
+      {
+       ManagedFile ret( doProvidePackageFromCache() );
+       if ( ! ( ret->empty() ||  _package->repoInfo().keepPackages() ) )
+         ret.setDispose( filesystem::unlink );
+       return ret;
+      }
 
-    ///////////////////////////////////////////////////////////////////
-    namespace
-    { /////////////////////////////////////////////////////////////////
+      /** Whether the package is cached. */
+      bool isCached() const
+      { return ! doProvidePackageFromCache()->empty(); }
+
+    protected:
+      typedef PackageProvider::Impl    Base;
+      typedef callback::SendReport<repo::DownloadResolvableReport>     Report;
+
+      /** Lookup the final rpm in cache.
+       *
+       * A non empty ManagedFile will be returned to the caller.
+       *
+       * \note File disposal depending on the repos keepPackages setting
+       * are not set here, but in \ref providePackage or \ref providePackageFromCache.
+       *
+       * \note The provoided default implementation returns an empty ManagedFile
+       * (cache miss).
+       */
+      virtual ManagedFile doProvidePackageFromCache() const = 0;
+
+      /** Actually provide the final rpm.
+       * Report start/problem/finish and retry loop are hadled by \ref providePackage.
+       * Here you trigger just progress and delta/plugin callbacks as needed.
+       *
+       * Proxy method for progressPackageDownload is provided here.
+       * \code
+       * ProvideFilePolicy policy;
+       * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
+       * return _access.provideFile( _package->repoInfo(), loc, policy );
+       * \endcode
+       *
+       * \note The provided default implementation retrieves the packages default
+       * location.
+       */
+      virtual ManagedFile doProvidePackage() const = 0;
+
+    protected:
+      /** Access to the DownloadResolvableReport */
+      Report & report() const
+      { return *_report; }
+
+      /** Redirect ProvideFilePolicy package download progress to this. */
+      bool progressPackageDownload( int value ) const
+      {        return report()->progress( value, _package ); }
+
+      typedef target::rpm::RpmDb RpmDb;
+
+      RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
+      {
+       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;
+      }
+
+      /** 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:
+           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 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:
+      PackageProviderPolicy    _policy;
+      Package::constPtr                _package;
+      DeltaCandidates          _deltas;
+      RepoMediaAccess &                _access;
 
-      inline std::string defRpmFileName( const Package::constPtr & package )
+    private:
+      typedef shared_ptr<void> ScopedGuard;
+
+      ScopedGuard newReport() const
       {
-        std::ostringstream ret;
-        ret << package->name() << '-' << package->edition() << '.' << package->arch() << ".rpm";
-        return ret.str();
+       _report.reset( new Report );
+       // Use a custom deleter calling _report.reset() when guard goes out of
+       // scope (cast required as reset is overloaded). We want report to end
+       // when leaving providePackage and not wait for *this going out of scope.
+       return shared_ptr<void>( static_cast<void*>(0),
+                                bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
+                                      ref(_report) ) );
       }
 
-      /////////////////////////////////////////////////////////////////
-    } // namespace source
+      mutable bool               _retry;
+      mutable shared_ptr<Report> _report;
+      mutable Target_Ptr         _target;
+    };
     ///////////////////////////////////////////////////////////////////
-    PackageProvider::PackageProvider(  RepoMediaAccess &access,
-                                      const Package::constPtr & package,
-                                      const DeltaCandidates & deltas,
-                                      const PackageProviderPolicy & policy_r )
-    : _policy( policy_r )
-    , _package( package )
-    , _deltas(deltas)
-    , _access(access)
-    {}
 
-    PackageProvider::~PackageProvider()
-    {}
+    /** Default implementation (cache miss). */
+    ManagedFile PackageProvider::Impl::doProvidePackageFromCache() const
+    { return ManagedFile(); }
 
-    ManagedFile PackageProvider::providePackage() const
+    /** Default implementation (provide full package) */
+    ManagedFile PackageProvider::Impl::doProvidePackage() const
     {
-      Url url;
+      ManagedFile ret;
+      OnMediaLocation loc = _package->location();
+
+      ProvideFilePolicy policy;
+      policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
+      return _access.provideFile( _package->repoInfo(), loc, policy );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    ManagedFile PackageProvider::Impl::providePackage() const
+    {
+      ScopedGuard guardReport( newReport() );
+
+      // check for cache hit:
+      ManagedFile ret( providePackageFromCache() );
+      if ( ! ret->empty() )
+      {
+       MIL << "provided Package from cache " << _package << " at " << ret << endl;
+       report()->infoInCache( _package, ret );
+       return ret; // <-- cache hit
+      }
+
+      // HERE: cache misss, check toplevel cache or do download:
       RepoInfo info = _package->repoInfo();
+
+      // Check toplevel cache
+      {
+       RepoManagerOptions topCache;
+       if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath )  // not using toplevel cache
+       {
+         const OnMediaLocation & loc( _package->location() );
+         if ( ! loc.checksum().empty() )       // no cache hit without checksum
+         {
+           PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
+           if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
+           {
+             report()->start( _package, pi.path().asFileUrl() );
+             const Pathname & dest( info.packagesPath() / loc.filename() );
+             if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
+             {
+               ret = ManagedFile( dest );
+               if ( ! info.keepPackages() )
+                 ret.setDispose( filesystem::unlink );
+
+               MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
+               report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
+               return ret; // <-- toplevel cache hit
+             }
+           }
+         }
+       }
+      }
+
       // FIXME we only support the first url for now.
       if ( info.baseUrlsEmpty() )
         ZYPP_THROW(Exception("No url in repository."));
-      else
-        url = * info.baseUrlsBegin();
-
-      { // check for cache hit:
-        OnMediaLocation loc( _package->location() );
-        PathInfo cachepath( info.packagesPath() / loc.filename() );
 
-        if ( cachepath.isFile() && ! loc.checksum().empty() ) // accept cache hit with matching checksum only!
-             // Tempting to do a quick check for matching .rpm-filesize before computing checksum,
-             // but real life shows that loc.downloadSize() and the .rpm-filesize frequently do not
-             // match, even if loc.checksum() and the .rpm-files checksum do. Blame the metadata generator(s).
-        {
-          CheckSum cachechecksum( loc.checksum().type(), filesystem::checksum( cachepath.path(), loc.checksum().type() ) );
-          USR << cachechecksum << endl;
-          if ( cachechecksum == loc.checksum() )
-          {
-            ManagedFile ret( cachepath.path() );
-            if ( ! info.keepPackages() )
-            {
-              ret.setDispose( filesystem::unlink );
-            }
-            MIL << "provided Package from cache " << _package << " at " << ret << endl;
-            return ret; // <-- cache hit
-          }
-        }
-      }
-
-      // HERE: cache misss, do download:
       MIL << "provide Package " << _package << endl;
-      ScopedGuard guardReport( newReport() );
-      ManagedFile ret;
+      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() ) );
@@ -153,10 +386,10 @@ namespace zypp
                         _retry = true;
                         break;
                       case repo::DownloadResolvableReport::IGNORE:
-                        ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
+                        ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
                         break;
                       case repo::DownloadResolvableReport::ABORT:
-                        ZYPP_THROW(AbortRequestException("User requested to abort"));
+                        ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
                         break;
                       default:
                         ZYPP_RETHROW( excpt );
@@ -171,7 +404,48 @@ namespace zypp
       return ret;
     }
 
-    ManagedFile PackageProvider::doProvidePackage() const
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class RpmPackageProvider
+    /// \brief RPM PackageProvider implementation.
+    ///////////////////////////////////////////////////////////////////
+    class RpmPackageProvider : public PackageProvider::Impl
+    {
+    public:
+      RpmPackageProvider( RepoMediaAccess & access_r,
+                         const Package::constPtr & package_r,
+                         const DeltaCandidates & deltas_r,
+                         const PackageProviderPolicy & policy_r )
+      : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
+      {}
+
+    protected:
+      virtual ManagedFile doProvidePackageFromCache() const;
+
+      virtual ManagedFile doProvidePackage() const;
+
+    private:
+      typedef packagedelta::DeltaRpm   DeltaRpm;
+
+      ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
+
+      bool progressDeltaDownload( int value ) const
+      { return report()->progressDeltaDownload( value ); }
+
+      void progressDeltaApply( int value ) const
+      { return report()->progressDeltaApply( value ); }
+
+      bool queryInstalled( const Edition & ed_r = Edition() ) const
+      { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    ManagedFile RpmPackageProvider::doProvidePackageFromCache() const
+    {
+      return ManagedFile( _package->cachedLocation() );
+    }
+
+    ManagedFile RpmPackageProvider::doProvidePackage() const
     {
       Url url;
       RepoInfo info = _package->repoInfo();
@@ -182,42 +456,29 @@ namespace zypp
         url = * info.baseUrlsBegin();
 
       // check whether to process patch/delta rpms
-      if ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() )
-        {
-          std::list<DeltaRpm> deltaRpms;
-          if ( ZConfig::instance().download_use_deltarpm() )
-          {
-            _deltas.deltaRpms( _package ).swap( deltaRpms );
-          }
-
-          if ( ! ( deltaRpms.empty() )
-               && queryInstalled() )
-            {
-              if ( ! deltaRpms.empty() && applydeltarpm::haveApplydeltarpm() )
-                {
-                  for( std::list<DeltaRpm>::const_iterator it = deltaRpms.begin();
-                       it != deltaRpms.end(); ++it )
-                    {
-                      DBG << "tryDelta " << *it << endl;
-                      ManagedFile ret( tryDelta( *it ) );
-                      if ( ! ret->empty() )
-                        return ret;
-                    }
-                }
-            }
-        }
+      if ( ZConfig::instance().download_use_deltarpm()
+       && ( url.schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
+      {
+       std::list<DeltaRpm> deltaRpms;
+       _deltas.deltaRpms( _package ).swap( deltaRpms );
+
+       if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
+       {
+         for_( it, deltaRpms.begin(), deltaRpms.end())
+         {
+           DBG << "tryDelta " << *it << endl;
+           ManagedFile ret( tryDelta( *it ) );
+           if ( ! ret->empty() )
+             return ret;
+         }
+       }
+      }
 
       // no patch/delta -> provide full package
-      ManagedFile ret;
-      OnMediaLocation loc = _package->location();
-
-      ProvideFilePolicy policy;
-      policy.progressCB( bind( &PackageProvider::progressPackageDownload, this, _1 ) );
-      policy.failOnChecksumErrorCB( bind( &PackageProvider::failOnChecksumError, this ) );
-      return _access.provideFile( _package->repoInfo(), loc, policy );
+      return Base::doProvidePackage();
     }
 
-    ManagedFile PackageProvider::tryDelta( const DeltaRpm & delta_r ) const
+    ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
     {
       if ( delta_r.baseversion().edition() != Edition::noedition
            && ! queryInstalled( delta_r.baseversion().edition() ) )
@@ -232,7 +493,7 @@ namespace zypp
       try
         {
           ProvideFilePolicy policy;
-          policy.progressCB( bind( &PackageProvider::progressDeltaDownload, this, _1 ) );
+          policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
           delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
         }
       catch ( const Exception & excpt )
@@ -249,18 +510,11 @@ namespace zypp
           return ManagedFile();
         }
 
-      Pathname destination( Pathname::dirname( delta ) / defRpmFileName( _package ) );
-
-      if ( ! delta.getDispose() )
-      {
-        // There is no cleanup method associated with the deta. Thus the
-        // delta is not a temporary file, and we don't want to write in
-        // the package into this directory.
-        destination = filesystem::TmpPath::defaultLocation() / defRpmFileName( _package );
-      }
+      // build the package and put it into the cache
+      Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
 
       if ( ! applydeltarpm::provide( delta, destination,
-                                     bind( &PackageProvider::progressDeltaApply, this, _1 ) ) )
+                                     bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
         {
           report()->problemDeltaApply( _("applydeltarpm failed.") );
           return ManagedFile();
@@ -270,57 +524,71 @@ namespace zypp
       return ManagedFile( destination, filesystem::unlink );
     }
 
-    PackageProvider::ScopedGuard PackageProvider::newReport() const
+#if 0
+    ///////////////////////////////////////////////////////////////////
+    /// \class PluginPackageProvider
+    /// \brief Plugin PackageProvider implementation.
+    ///
+    /// Basically downloads the default package and calls a
+    /// 'stem'2rpm plugin to cteate the final .rpm package.
+    ///////////////////////////////////////////////////////////////////
+    class PluginPackageProvider : public PackageProvider::Impl
     {
-      _report.reset( new Report );
-      return shared_ptr<void>( static_cast<void*>(0),
-                               // custom deleter calling _report.reset()
-                               // (cast required as reset is overloaded)
-                               bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
-                                     ref(_report) ) );
-    }
+    public:
+      PluginPackageProvider( const std::string & stem_r,
+                            RepoMediaAccess & access_r,
+                            const Package::constPtr & package_r,
+                            const DeltaCandidates & deltas_r,
+                            const PackageProviderPolicy & policy_r )
+      : Base( access_r, package_r, deltas_r, policy_r )
+      {}
+
+    protected:
+      virtual ManagedFile doProvidePackageFromCache() const
+      {
+       return Base::doProvidePackageFromCache();
+      }
 
-    PackageProvider::Report & PackageProvider::report() const
-    { return *_report; }
+      virtual ManagedFile doProvidePackage() const
+      {
+       return Base::doProvidePackage();
+      }
+    };
+    ///////////////////////////////////////////////////////////////////
+#endif
 
-    bool PackageProvider::progressDeltaDownload( int value ) const
-    { return report()->progressDeltaDownload( value ); }
+    ///////////////////////////////////////////////////////////////////
+    // class PackageProvider
+    ///////////////////////////////////////////////////////////////////
 
-    void PackageProvider::progressDeltaApply( int value ) const
-    { return report()->progressDeltaApply( value ); }
+    PackageProvider::Impl * PackageProvider::Impl::factoryMake( RepoMediaAccess & access_r,
+                                                               const Package::constPtr & package_r,
+                                                               const DeltaCandidates & deltas_r,
+                                                               const PackageProviderPolicy & policy_r )
+    {
+      return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
+    }
 
-    bool PackageProvider::progressPackageDownload( int value ) const
-    { return report()->progress( value, _package ); }
+    PackageProvider::PackageProvider( RepoMediaAccess & access_r,
+                                     const Package::constPtr & package_r,
+                                     const DeltaCandidates & deltas_r,
+                                     const PackageProviderPolicy & policy_r )
+    : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
+    {}
 
-    bool PackageProvider::failOnChecksumError() const
-    {
-      std::string package_str = _package->name() + "-" + _package->edition().asString();
+    PackageProvider::~PackageProvider()
+    {}
 
-      // 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;
-        }
-      return true; // anyway a failure
-    }
+    ManagedFile PackageProvider::providePackage() const
+    { return _pimpl->providePackage(); }
 
-    bool PackageProvider::queryInstalled( const Edition & ed_r ) const
-    { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
+    ManagedFile PackageProvider::providePackageFromCache() const
+    { return _pimpl->providePackageFromCache(); }
 
+    bool PackageProvider::isCached() const
+    { return _pimpl->isCached(); }
 
-    /////////////////////////////////////////////////////////////////
   } // namespace repo
   ///////////////////////////////////////////////////////////////////
-  /////////////////////////////////////////////////////////////////
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////