8bd760109929e2f7e8aced1d0578787cfdfc3f4b
[platform/upstream/libzypp.git] / zypp / repo / PackageProvider.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/repo/PackageProvider.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include "zypp/repo/PackageDelta.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/UserRequestException.h"
19 #include "zypp/base/NonCopyable.h"
20 #include "zypp/repo/PackageProvider.h"
21 #include "zypp/repo/Applydeltarpm.h"
22 #include "zypp/repo/PackageDelta.h"
23
24 #include "zypp/TmpPath.h"
25 #include "zypp/ZConfig.h"
26 #include "zypp/RepoInfo.h"
27 #include "zypp/RepoManager.h"
28 #include "zypp/SrcPackage.h"
29
30 #include "zypp/ZYppFactory.h"
31 #include "zypp/Target.h"
32 #include "zypp/target/rpm/RpmDb.h"
33 #include "zypp/FileChecker.h"
34
35 using std::endl;
36
37 ///////////////////////////////////////////////////////////////////
38 namespace zypp
39 {
40   ///////////////////////////////////////////////////////////////////
41   namespace repo
42   {
43     ///////////////////////////////////////////////////////////////////
44     /// \class RpmSigCheckException
45     /// \brief Exception thrown by \ref PackageProviderImpl::rpmSigFileChecker
46     ///////////////////////////////////////////////////////////////////
47     class RpmSigCheckException : public FileCheckException
48     {
49     public:
50       RpmSigCheckException( repo::DownloadResolvableReport::Action action_r, std::string msg_r = "RpmSigCheckException" )
51       : FileCheckException( std::move(msg_r) )
52       , _action( std::move(action_r) )
53       {}
54
55       /** Users final decision how to proceed */
56       const repo::DownloadResolvableReport::Action & action() const
57       { return _action; }
58
59     private:
60       repo::DownloadResolvableReport::Action _action;
61     };
62
63
64     ///////////////////////////////////////////////////////////////////
65     //  class PackageProviderPolicy
66     ///////////////////////////////////////////////////////////////////
67
68     bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
69                                                 const Edition &     ed_r,
70                                                 const Arch &        arch_r ) const
71     {
72       if ( _queryInstalledCB )
73         return _queryInstalledCB( name_r, ed_r, arch_r );
74       return false;
75     }
76
77     ///////////////////////////////////////////////////////////////////
78     /// \class PackageProvider::Impl
79     /// \brief PackageProvider implementation interface.
80     ///////////////////////////////////////////////////////////////////
81     struct PackageProvider::Impl : private base::NonCopyable
82     {
83       Impl() {}
84       virtual ~Impl() {}
85
86       /** Provide the package.
87        * The basic workflow.
88        * \throws Exception.
89        */
90       virtual ManagedFile providePackage() const = 0;
91
92       /** Provide the package if it is cached. */
93       virtual ManagedFile providePackageFromCache() const = 0;
94
95       /** Whether the package is cached. */
96       virtual bool isCached() const = 0;
97     };
98
99     ///////////////////////////////////////////////////////////////////
100     /// \class PackageProviderImpl<TPackage>
101     /// \brief PackageProvider implementation for \c Package and \c SrcPackage
102     ///////////////////////////////////////////////////////////////////
103     template <class TPackage>
104     class PackageProviderImpl : public PackageProvider::Impl
105     {
106       typedef typename TPackage::constPtr TPackagePtr;  // Package or SrcPackage
107       typedef callback::UserData UserData;
108     public:
109       /** Ctor taking the Package to provide. */
110       PackageProviderImpl( RepoMediaAccess & access_r, const TPackagePtr & package_r,
111                            const PackageProviderPolicy & policy_r )
112       : _policy( policy_r )
113       , _package( package_r )
114       , _access( access_r )
115       , _retry(false)
116       {}
117
118       virtual ~PackageProviderImpl() {}
119
120     public:
121       /** Provide the package.
122        * The basic workflow.
123        * \throws Exception.
124        */
125       virtual ManagedFile providePackage() const;
126
127       /** Provide the package if it is cached. */
128       virtual ManagedFile providePackageFromCache() const
129       {
130         ManagedFile ret( doProvidePackageFromCache() );
131         if ( ! ( ret->empty() ||  _package->repoInfo().keepPackages() ) )
132           ret.setDispose( filesystem::unlink );
133         return ret;
134       }
135
136       /** Whether the package is cached. */
137       virtual bool isCached() const
138       { return ! doProvidePackageFromCache()->empty(); }
139
140     protected:
141       typedef PackageProviderImpl<TPackage>     Base;
142       typedef callback::SendReport<repo::DownloadResolvableReport>      Report;
143
144       /** Lookup the final rpm in cache.
145        *
146        * A cache hit will return a non empty ManagedFile and an empty one on cache miss.
147        *
148        * \note File disposal depending on the repos keepPackages setting
149        * are not set here, but in \ref providePackage or \ref providePackageFromCache.
150        */
151       ManagedFile doProvidePackageFromCache() const
152       { return ManagedFile( _package->cachedLocation() ); }
153
154       /** Actually provide the final rpm.
155        * Report start/problem/finish and retry loop are hadled by \ref providePackage.
156        * Here you trigger just progress and delta/plugin callbacks as needed.
157        *
158        * Proxy method for progressPackageDownload is provided here.
159        * \code
160        * ProvideFilePolicy policy;
161        * policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
162        * return _access.provideFile( _package->repoInfo(), loc, policy );
163        * \endcode
164        *
165        * \note The provided default implementation retrieves the packages default
166        * location.
167        */
168       virtual ManagedFile doProvidePackage() const
169       {
170         ManagedFile ret;
171         OnMediaLocation loc = _package->location();
172
173         ProvideFilePolicy policy;
174         policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
175         policy.fileChecker( bind( &Base::rpmSigFileChecker, this, _1 ) );
176         return _access.provideFile( _package->repoInfo(), loc, policy );
177       }
178
179     protected:
180       /** Access to the DownloadResolvableReport */
181       Report & report() const
182       { return *_report; }
183
184       /** Redirect ProvideFilePolicy package download progress to this. */
185       bool progressPackageDownload( int value ) const
186       { return report()->progress( value, _package ); }
187
188
189       /** \name Validate a rpm packages signature.
190        *
191        * This is the \ref FileChecker passed down to the \ref Fetcher to validate
192        * a provided rpm package. This builtin checker includes the workflow
193        * communicating with the user in case of a problem with the package
194        * signature.
195        *
196        * \throws RpmSigCheckException if the package is not accepted, propagating
197        * the users decision how to proceed (\ref DownloadResolvableReport::Action).
198        *
199        * \note This check is also needed, if the the rpm is built locally by using
200        * delta rpms! \ref \see RpmPackageProvider
201        */
202       //@{
203       void rpmSigFileChecker( const Pathname & file_r ) const
204       {
205         const RepoInfo & info = _package->repoInfo();
206         if ( info.pkgGpgCheck() )
207         {
208           UserData userData( "pkgGpgCheck" );
209           ResObject::constPtr roptr( _package );        // gcc6 needs it more explcit. Has problem deducing
210           userData.set( "ResObject", roptr );           // a type for '_package->asKind<ResObject>()'...
211           /*legacy:*/userData.set( "Package", roptr->asKind<Package>() );
212           userData.set( "Localpath", file_r );
213           RpmDb::CheckPackageResult res = packageSigCheck( file_r, info.pkgGpgCheckIsMandatory(), userData );
214
215           // publish the checkresult, even if it is OK. Apps may want to report something...
216           report()->pkgGpgCheck( userData );
217
218           if ( res != RpmDb::CHK_OK )
219           {
220             if ( userData.hasvalue( "Action" ) )        // pkgGpgCheck report provided an user error action
221             {
222               resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
223             }
224             else if ( userData.haskey( "Action" ) )     // pkgGpgCheck requests the default problem report (wo. details)
225             {
226               defaultReportSignatureError( res );
227             }
228             else                                        // no advice from user => usedefaults
229             {
230               switch ( res )
231               {
232                 case RpmDb::CHK_OK:             // Signature is OK
233                   break;
234
235                 case RpmDb::CHK_NOKEY:          // Public key is unavailable
236                 case RpmDb::CHK_NOTFOUND:       // Signature is unknown type
237                 case RpmDb::CHK_FAIL:           // Signature does not verify
238                 case RpmDb::CHK_NOTTRUSTED:     // Signature is OK, but key is not trusted
239                 case RpmDb::CHK_ERROR:          // File does not exist or can't be opened
240                 case RpmDb::CHK_NOSIG:          // File is unsigned
241                 default:
242                   // report problem (w. details), throw if to abort, else retry/ignore
243                   defaultReportSignatureError( res, str::Str() << userData.get<RpmDb::CheckPackageDetail>( "CheckPackageDetail" ) );
244                   break;
245               }
246             }
247           }
248         }
249       }
250
251       typedef target::rpm::RpmDb RpmDb;
252
253       /** Actual rpm package signature check. */
254       RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, bool isMandatory_r, UserData & userData ) const
255       {
256         if ( !_target )
257           _target = getZYpp()->getTarget();
258
259         RpmDb::CheckPackageResult ret = RpmDb::CHK_ERROR;
260         RpmDb::CheckPackageDetail detail;
261         if ( _target )
262         {
263           ret = _target->rpmDb().checkPackageSignature( path_r, detail );
264           if ( ret == RpmDb::CHK_NOSIG && !isMandatory_r )
265           {
266             WAR << "Relax CHK_NOSIG: Config says unsigned packages are OK" << endl;
267             ret = RpmDb::CHK_OK;
268           }
269         }
270         else
271           detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
272
273         userData.set( "CheckPackageResult", ret );
274         userData.set( "CheckPackageDetail", std::move(detail) );
275         return ret;
276       }
277
278       /** React on signature verification error user action.
279        * \note: IGNORE == accept insecure file (no SkipRequestException!)
280        */
281       void resolveSignatureErrorAction( repo::DownloadResolvableReport::Action action_r ) const
282       {
283         switch ( action_r )
284         {
285           case repo::DownloadResolvableReport::IGNORE:
286             WAR << _package->asUserString() << ": " << "User requested to accept insecure file" << endl;
287             break;
288           default:
289           case repo::DownloadResolvableReport::RETRY:
290           case repo::DownloadResolvableReport::ABORT:
291             ZYPP_THROW(RpmSigCheckException(action_r,"Signature verification failed"));
292             break;
293         }
294       }
295
296       /** Default signature verification error handling. */
297       void defaultReportSignatureError( RpmDb::CheckPackageResult ret, const std::string & detail_r = std::string() ) const
298       {
299         str::Str msg;
300         msg << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret;
301         if ( ! detail_r.empty() )
302           msg << "\n" << detail_r;
303         resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg.str() ) );
304       }
305       //@}
306
307     protected:
308       PackageProviderPolicy     _policy;
309       TPackagePtr               _package;
310       RepoMediaAccess &         _access;
311
312     private:
313       typedef shared_ptr<void>  ScopedGuard;
314
315       ScopedGuard newReport() const
316       {
317         _report.reset( new Report );
318         // Use a custom deleter calling _report.reset() when guard goes out of
319         // scope (cast required as reset is overloaded). We want report to end
320         // when leaving providePackage and not wait for *this going out of scope.
321         return shared_ptr<void>( static_cast<void*>(0),
322                                  bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
323                                        ref(_report) ) );
324       }
325
326       mutable bool               _retry;
327       mutable shared_ptr<Report> _report;
328       mutable Target_Ptr         _target;
329     };
330     ///////////////////////////////////////////////////////////////////
331
332     template <class TPackage>
333     ManagedFile PackageProviderImpl<TPackage>::providePackage() const
334     {
335       ScopedGuard guardReport( newReport() );
336
337       // check for cache hit:
338       ManagedFile ret( providePackageFromCache() );
339       if ( ! ret->empty() )
340       {
341         MIL << "provided Package from cache " << _package << " at " << ret << endl;
342         report()->infoInCache( _package, ret );
343         return ret; // <-- cache hit
344       }
345
346       // HERE: cache misss, check toplevel cache or do download:
347       RepoInfo info = _package->repoInfo();
348
349       // Check toplevel cache
350       {
351         RepoManagerOptions topCache;
352         if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath )  // not using toplevel cache
353         {
354           const OnMediaLocation & loc( _package->location() );
355           if ( ! loc.checksum().empty() )       // no cache hit without checksum
356           {
357             PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
358             if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
359             {
360               report()->start( _package, pi.path().asFileUrl() );
361               const Pathname & dest( info.packagesPath() / loc.filename() );
362               if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
363               {
364                 ret = ManagedFile( dest );
365                 if ( ! info.keepPackages() )
366                   ret.setDispose( filesystem::unlink );
367
368                 MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
369                 report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
370                 return ret; // <-- toplevel cache hit
371               }
372             }
373           }
374         }
375       }
376
377       // FIXME we only support the first url for now.
378       if ( info.baseUrlsEmpty() )
379         ZYPP_THROW(Exception("No url in repository."));
380
381       MIL << "provide Package " << _package << endl;
382       Url url = * info.baseUrlsBegin();
383       try {
384       do {
385         _retry = false;
386         if ( ! ret->empty() )
387         {
388           ret.setDispose( filesystem::unlink );
389           ret.reset();
390         }
391         report()->start( _package, url );
392         try
393           {
394             ret = doProvidePackage();
395           }
396         catch ( const UserRequestException & excpt )
397           {
398             ERR << "Failed to provide Package " << _package << endl;
399             if ( ! _retry )
400               ZYPP_RETHROW( excpt );
401           }
402         catch ( const RpmSigCheckException & excpt )
403           {
404             ERR << "Failed to provide Package " << _package << endl;
405             if ( ! _retry )
406             {
407               // Signature verification error was already reported by the
408               // rpmSigFileChecker. Just handle the users action decision:
409               switch ( excpt.action() )
410               {
411                 case repo::DownloadResolvableReport::RETRY:
412                   _retry = true;
413                   break;
414                 case repo::DownloadResolvableReport::IGNORE:
415                   ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
416                   break;
417                 default:
418                 case repo::DownloadResolvableReport::ABORT:
419                   ZYPP_THROW(AbortRequestException("User requested to abort"));
420                   break;
421               }
422             }
423           }
424         catch ( const FileCheckException & excpt )
425           {
426             ERR << "Failed to provide Package " << _package << endl;
427             if ( ! _retry )
428             {
429               const std::string & package_str = _package->asUserString();
430               // TranslatorExplanation %s = package being checked for integrity
431               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() ) ) )
432               {
433                 case repo::DownloadResolvableReport::RETRY:
434                   _retry = true;
435                   break;
436                 case repo::DownloadResolvableReport::IGNORE:
437                   ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
438                   break;
439                 default:
440                 case repo::DownloadResolvableReport::ABORT:
441                   ZYPP_THROW(AbortRequestException("User requested to abort"));
442                   break;
443               }
444             }
445           }
446         catch ( const Exception & excpt )
447           {
448             ERR << "Failed to provide Package " << _package << endl;
449             if ( ! _retry )
450             {
451                 // Aything else gets reported
452                 const std::string & package_str = _package->asUserString();
453
454                 // TranslatorExplanation %s = name of the package being processed.
455                 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
456                 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
457
458                 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
459                 {
460                       case repo::DownloadResolvableReport::RETRY:
461                         _retry = true;
462                         break;
463                       case repo::DownloadResolvableReport::IGNORE:
464                         ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
465                         break;
466                       default:
467                       case repo::DownloadResolvableReport::ABORT:
468                         ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
469                         break;
470                 }
471               }
472           }
473       } while ( _retry );
474       } catch(...){
475         // bsc#1045735: Be sure no invalid files stay in the cache!
476         if ( ! ret->empty() )
477           ret.setDispose( filesystem::unlink );
478         throw;
479       }
480
481       report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
482       MIL << "provided Package " << _package << " at " << ret << endl;
483       return ret;
484     }
485
486
487     ///////////////////////////////////////////////////////////////////
488     /// \class RpmPackageProvider
489     /// \brief RPM PackageProvider implementation (with deltarpm processing).
490     ///////////////////////////////////////////////////////////////////
491     class RpmPackageProvider : public PackageProviderImpl<Package>
492     {
493     public:
494       RpmPackageProvider( RepoMediaAccess & access_r,
495                           const Package::constPtr & package_r,
496                           const DeltaCandidates & deltas_r,
497                           const PackageProviderPolicy & policy_r )
498       : PackageProviderImpl<Package>( access_r, package_r, policy_r )
499       , _deltas( deltas_r )
500       {}
501
502     protected:
503       virtual ManagedFile doProvidePackage() const;
504
505     private:
506       typedef packagedelta::DeltaRpm    DeltaRpm;
507
508       ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
509
510       bool progressDeltaDownload( int value ) const
511       { return report()->progressDeltaDownload( value ); }
512
513       void progressDeltaApply( int value ) const
514       { return report()->progressDeltaApply( value ); }
515
516       bool queryInstalled( const Edition & ed_r = Edition() ) const
517       { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
518
519     private:
520       DeltaCandidates           _deltas;
521     };
522     ///////////////////////////////////////////////////////////////////
523
524     ManagedFile RpmPackageProvider::doProvidePackage() const
525     {
526       // check whether to process patch/delta rpms
527       // FIXME we only check the first url for now.
528       if ( ZConfig::instance().download_use_deltarpm()
529         && ( _package->repoInfo().url().schemeIsDownloading() || ZConfig::instance().download_use_deltarpm_always() ) )
530       {
531         std::list<DeltaRpm> deltaRpms;
532         _deltas.deltaRpms( _package ).swap( deltaRpms );
533
534         if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
535         {
536           for_( it, deltaRpms.begin(), deltaRpms.end())
537           {
538             DBG << "tryDelta " << *it << endl;
539             ManagedFile ret( tryDelta( *it ) );
540             if ( ! ret->empty() )
541               return ret;
542           }
543         }
544       }
545
546       // no patch/delta -> provide full package
547       return Base::doProvidePackage();
548     }
549
550     ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
551     {
552       if ( delta_r.baseversion().edition() != Edition::noedition
553            && ! queryInstalled( delta_r.baseversion().edition() ) )
554         return ManagedFile();
555
556       if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
557         return ManagedFile();
558
559       report()->startDeltaDownload( delta_r.location().filename(),
560                                     delta_r.location().downloadSize() );
561       ManagedFile delta;
562       try
563         {
564           ProvideFilePolicy policy;
565           policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
566           delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
567         }
568       catch ( const Exception & excpt )
569         {
570           report()->problemDeltaDownload( excpt.asUserHistory() );
571           return ManagedFile();
572         }
573       report()->finishDeltaDownload();
574
575       report()->startDeltaApply( delta );
576       if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
577         {
578           report()->problemDeltaApply( _("applydeltarpm check failed.") );
579           return ManagedFile();
580         }
581
582       // Build the package
583       Pathname cachedest( _package->repoInfo().packagesPath() / _package->location().filename() );
584       Pathname builddest( cachedest.extend( ".drpm" ) );
585
586       if ( ! applydeltarpm::provide( delta, builddest,
587                                      bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
588         {
589           report()->problemDeltaApply( _("applydeltarpm failed.") );
590           return ManagedFile();
591         }
592       ManagedFile builddestCleanup( builddest, filesystem::unlink );
593       report()->finishDeltaApply();
594
595       // Check and move it into the cache
596       // Here the rpm itself is ready. If the packages sigcheck fails, it
597       // makes no sense to return a ManagedFile() and fallback to download the
598       // full rpm. It won't be different. So let the exceptions escape...
599       rpmSigFileChecker( builddest );
600       if ( filesystem::hardlinkCopy( builddest, cachedest ) != 0 )
601         ZYPP_THROW( Exception( str::Str() << "Can't hardlink/copy " << builddest << " to " << cachedest ) );
602
603       return ManagedFile( cachedest, filesystem::unlink );
604     }
605
606     ///////////////////////////////////////////////////////////////////
607     //  class PackageProvider
608     ///////////////////////////////////////////////////////////////////
609     namespace factory
610     {
611       inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const PoolItem & pi_r,
612                                            const DeltaCandidates & deltas_r,
613                                            const PackageProviderPolicy & policy_r )
614       {
615         if ( pi_r.satSolvable().isKind<Package>() )
616           return new RpmPackageProvider( access_r, pi_r->asKind<Package>(), deltas_r, policy_r );
617         else if ( pi_r.satSolvable().isKind<SrcPackage>() )
618           return new PackageProviderImpl<SrcPackage>( access_r, pi_r->asKind<SrcPackage>(), policy_r );
619         else
620           ZYPP_THROW( Exception( str::Str() << "Don't know how to cache non-package " << pi_r.satSolvable().asUserString() ) );
621       }
622
623       inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const PoolItem & pi_r,
624                                                   const PackageProviderPolicy & policy_r )
625       {
626         if ( pi_r.satSolvable().isKind<Package>() )
627           return new PackageProviderImpl<Package>( access_r, pi_r->asKind<Package>(), policy_r );
628         else if ( pi_r.satSolvable().isKind<SrcPackage>() )
629           return new PackageProviderImpl<SrcPackage>( access_r, pi_r->asKind<SrcPackage>(), policy_r );
630         else
631           ZYPP_THROW( Exception( str::Str() << "Don't know how to cache non-package " << pi_r.satSolvable().asUserString() ) );
632       }
633
634       inline PackageProvider::Impl * make( RepoMediaAccess & access_r, const Package::constPtr & package_r,
635                                            const DeltaCandidates & deltas_r,
636                                            const PackageProviderPolicy & policy_r )
637       { return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r ); }
638
639     } // namespace factory
640     ///////////////////////////////////////////////////////////////////
641
642     PackageProvider::PackageProvider( RepoMediaAccess & access_r, const PoolItem & pi_r,
643                                       const DeltaCandidates & deltas_r, const PackageProviderPolicy & policy_r )
644
645     : _pimpl( factory::make( access_r, pi_r, deltas_r, policy_r ) )
646     {}
647
648     PackageProvider::PackageProvider( RepoMediaAccess & access_r, const PoolItem & pi_r,
649                                       const PackageProviderPolicy & policy_r )
650     : _pimpl( factory::make( access_r, pi_r, policy_r ) )
651     {}
652
653     /* legacy */
654     PackageProvider::PackageProvider( RepoMediaAccess & access_r,
655                                       const Package::constPtr & package_r,
656                                       const DeltaCandidates & deltas_r,
657                                       const PackageProviderPolicy & policy_r )
658     : _pimpl( factory::make( access_r, package_r, deltas_r, policy_r ) )
659     {}
660
661     PackageProvider::~PackageProvider()
662     {}
663
664     ManagedFile PackageProvider::providePackage() const
665     { return _pimpl->providePackage(); }
666
667     ManagedFile PackageProvider::providePackageFromCache() const
668     { return _pimpl->providePackageFromCache(); }
669
670     bool PackageProvider::isCached() const
671     { return _pimpl->isCached(); }
672
673   } // namespace repo
674   ///////////////////////////////////////////////////////////////////
675 } // namespace zypp
676 ///////////////////////////////////////////////////////////////////