libzypp  16.6.1
PackageProvider.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
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"
19 #include "zypp/base/NonCopyable.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 
29 #include "zypp/ZYppFactory.h"
30 #include "zypp/Target.h"
31 #include "zypp/target/rpm/RpmDb.h"
32 #include "zypp/FileChecker.h"
33 
34 using std::endl;
35 
37 namespace zypp
38 {
40  namespace repo
41  {
43  // class PackageProviderPolicy
45 
46  bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
47  const Edition & ed_r,
48  const Arch & arch_r ) const
49  {
50  if ( _queryInstalledCB )
51  return _queryInstalledCB( name_r, ed_r, arch_r );
52  return false;
53  }
54 
55 
61  {
63  public:
65  Impl( RepoMediaAccess & access_r,
66  const Package::constPtr & package_r,
67  const DeltaCandidates & deltas_r,
68  const PackageProviderPolicy & policy_r )
69  : _policy( policy_r )
70  , _package( package_r )
71  , _deltas( deltas_r )
72  , _access( access_r )
73  , _retry(false)
74  {}
75 
76  virtual ~Impl() {}
77 
82  static Impl * factoryMake( RepoMediaAccess & access_r,
83  const Package::constPtr & package_r,
84  const DeltaCandidates & deltas_r,
85  const PackageProviderPolicy & policy_r );
86 
87  public:
92  ManagedFile providePackage() const;
93 
96  {
97  ManagedFile ret( doProvidePackageFromCache() );
98  if ( ! ( ret->empty() || _package->repoInfo().keepPackages() ) )
100  return ret;
101  }
102 
104  bool isCached() const
105  { return ! doProvidePackageFromCache()->empty(); }
106 
107  protected:
110 
121  virtual ManagedFile doProvidePackageFromCache() const = 0;
122 
137  virtual ManagedFile doProvidePackage() const = 0;
138 
139  protected:
141  Report & report() const
142  { return *_report; }
143 
145  bool progressPackageDownload( int value ) const
146  { return report()->progress( value, _package ); }
147 
149 
150  RpmDb::CheckPackageResult packageSigCheck( const Pathname & path_r, UserData & userData ) const
151  {
152  if ( !_target )
153  _target = getZYpp()->getTarget();
154 
155  RpmDb::CheckPackageResult ret = RpmDb::CHK_ERROR;
157  if ( _target )
158  ret = _target->rpmDb().checkPackage( path_r, detail );
159  else
160  detail.push_back( RpmDb::CheckPackageDetail::value_type( ret, "OOps. Target is not initialized!" ) );
161 
162  userData.set( "CheckPackageResult", ret );
163  userData.set( "CheckPackageDetail", std::move(detail) );
164  return ret;
165  }
166 
171  {
172  switch ( action_r )
173  {
175  _retry = true;
176  break;
178  WAR << _package->asUserString() << ": " << "User requested to accept insecure file" << endl;
179  break;
180  default:
182  ZYPP_THROW(AbortRequestException("User requested to abort"));
183  break;
184  }
185  }
186 
188  void defaultReportSignatureError( RpmDb::CheckPackageResult ret, const std::string & detail_r = std::string() ) const
189  {
190  str::Str msg;
191  msg << _package->asUserString() << ": " << _("Signature verification failed") << " " << ret;
192  if ( ! detail_r.empty() )
193  msg << "\n" << detail_r;
194  resolveSignatureErrorAction( report()->problem( _package, repo::DownloadResolvableReport::INVALID, msg.str() ) );
195  }
196 
197  protected:
202 
203  private:
204  typedef shared_ptr<void> ScopedGuard;
205 
206  ScopedGuard newReport() const
207  {
208  _report.reset( new Report );
209  // Use a custom deleter calling _report.reset() when guard goes out of
210  // scope (cast required as reset is overloaded). We want report to end
211  // when leaving providePackage and not wait for *this going out of scope.
212  return shared_ptr<void>( static_cast<void*>(0),
213  bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
214  ref(_report) ) );
215  }
216 
217  mutable bool _retry;
218  mutable shared_ptr<Report> _report;
219  mutable Target_Ptr _target;
220  };
222 
225  { return ManagedFile(); }
226 
229  {
230  ManagedFile ret;
231  OnMediaLocation loc = _package->location();
232 
233  ProvideFilePolicy policy;
234  policy.progressCB( bind( &Base::progressPackageDownload, this, _1 ) );
235  return _access.provideFile( _package->repoInfo(), loc, policy );
236  }
237 
239 
241  {
242  ScopedGuard guardReport( newReport() );
243 
244  // check for cache hit:
245  ManagedFile ret( providePackageFromCache() );
246  if ( ! ret->empty() )
247  {
248  MIL << "provided Package from cache " << _package << " at " << ret << endl;
249  report()->infoInCache( _package, ret );
250  return ret; // <-- cache hit
251  }
252 
253  // HERE: cache misss, check toplevel cache or do download:
254  RepoInfo info = _package->repoInfo();
255 
256  // Check toplevel cache
257  {
258  RepoManagerOptions topCache;
259  if ( info.packagesPath().dirname() != topCache.repoPackagesCachePath ) // not using toplevel cache
260  {
261  const OnMediaLocation & loc( _package->location() );
262  if ( ! loc.checksum().empty() ) // no cache hit without checksum
263  {
264  PathInfo pi( topCache.repoPackagesCachePath / info.packagesPath().basename() / loc.filename() );
265  if ( pi.isExist() && loc.checksum() == CheckSum( loc.checksum().type(), std::ifstream( pi.c_str() ) ) )
266  {
267  report()->start( _package, pi.path().asFileUrl() );
268  const Pathname & dest( info.packagesPath() / loc.filename() );
269  if ( filesystem::assert_dir( dest.dirname() ) == 0 && filesystem::hardlinkCopy( pi.path(), dest ) == 0 )
270  {
271  ret = ManagedFile( dest );
272  if ( ! info.keepPackages() )
274 
275  MIL << "provided Package from toplevel cache " << _package << " at " << ret << endl;
276  report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
277  return ret; // <-- toplevel cache hit
278  }
279  }
280  }
281  }
282  }
283 
284  // FIXME we only support the first url for now.
285  if ( info.baseUrlsEmpty() )
286  ZYPP_THROW(Exception("No url in repository."));
287 
288  MIL << "provide Package " << _package << endl;
289  Url url = * info.baseUrlsBegin();
290  do {
291  _retry = false;
292  if ( ! ret->empty() )
293  {
295  ret.reset();
296  }
297  report()->start( _package, url );
298  try
299  {
300  ret = doProvidePackage();
301 
302  if ( info.pkgGpgCheck() )
303  {
304  UserData userData( "pkgGpgCheck" );
305  userData.set( "Package", _package );
306  userData.set( "Localpath", ret.value() );
307  RpmDb::CheckPackageResult res = packageSigCheck( ret, userData );
308  // publish the checkresult, even if it is OK. Apps may want to report something...
309  report()->pkgGpgCheck( userData );
310 USR << "CHK: " << res << endl;
311  if ( res != RpmDb::CHK_OK )
312  {
313  if ( userData.hasvalue( "Action" ) ) // pkgGpgCheck report provided an user error action
314  {
315  resolveSignatureErrorAction( userData.get( "Action", repo::DownloadResolvableReport::ABORT ) );
316  }
317  else if ( userData.haskey( "Action" ) ) // pkgGpgCheck requests the default problem report (wo. details)
318  {
319  defaultReportSignatureError( res );
320  }
321  else // no advice from user => usedefaults
322  {
323  switch ( res )
324  {
325  case RpmDb::CHK_OK: // Signature is OK
326  break;
327 
328  case RpmDb::CHK_NOKEY: // Public key is unavailable
329  case RpmDb::CHK_NOTFOUND: // Signature is unknown type
330  case RpmDb::CHK_FAIL: // Signature does not verify
331  case RpmDb::CHK_NOTTRUSTED: // Signature is OK, but key is not trusted
332  case RpmDb::CHK_ERROR: // File does not exist or can't be opened
333  default:
334  // report problem (w. details), throw if to abort, else retry/ignore
335  defaultReportSignatureError( res, str::Str() << userData.get<RpmDb::CheckPackageDetail>( "CheckPackageDetail" ) );
336  break;
337  }
338  }
339  }
340  }
341  }
342  catch ( const UserRequestException & excpt )
343  {
344  ERR << "Failed to provide Package " << _package << endl;
345  if ( ! _retry )
346  ZYPP_RETHROW( excpt );
347  }
348  catch ( const FileCheckException & excpt )
349  {
350  ERR << "Failed to provide Package " << _package << endl;
351  if ( ! _retry )
352  {
353  const std::string & package_str = _package->asUserString();
354  // TranslatorExplanation %s = package being checked for integrity
355  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() ) ) )
356  {
358  _retry = true;
359  break;
361  ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
362  break;
364  ZYPP_THROW(AbortRequestException("User requested to abort"));
365  break;
366  default:
367  break;
368  }
369  }
370  }
371  catch ( const Exception & excpt )
372  {
373  ERR << "Failed to provide Package " << _package << endl;
374  if ( ! _retry )
375  {
376  // Aything else gets reported
377  const std::string & package_str = _package->asUserString();
378 
379  // TranslatorExplanation %s = name of the package being processed.
380  std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
381  detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
382 
383  switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
384  {
386  _retry = true;
387  break;
389  ZYPP_THROW(SkipRequestException("User requested skip of file", excpt));
390  break;
392  ZYPP_THROW(AbortRequestException("User requested to abort", excpt));
393  break;
394  default:
395  ZYPP_RETHROW( excpt );
396  break;
397  }
398  }
399  }
400  } while ( _retry );
401 
402  report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
403  MIL << "provided Package " << _package << " at " << ret << endl;
404  return ret;
405  }
406 
407 
413  {
414  public:
416  const Package::constPtr & package_r,
417  const DeltaCandidates & deltas_r,
418  const PackageProviderPolicy & policy_r )
419  : PackageProvider::Impl( access_r, package_r, deltas_r, policy_r )
420  {}
421 
422  protected:
423  virtual ManagedFile doProvidePackageFromCache() const;
424 
425  virtual ManagedFile doProvidePackage() const;
426 
427  private:
429 
430  ManagedFile tryDelta( const DeltaRpm & delta_r ) const;
431 
432  bool progressDeltaDownload( int value ) const
433  { return report()->progressDeltaDownload( value ); }
434 
435  void progressDeltaApply( int value ) const
436  { return report()->progressDeltaApply( value ); }
437 
438  bool queryInstalled( const Edition & ed_r = Edition() ) const
439  { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
440  };
442 
444  {
445  return ManagedFile( _package->cachedLocation() );
446  }
447 
449  {
450  Url url;
451  RepoInfo info = _package->repoInfo();
452  // FIXME we only support the first url for now.
453  if ( info.baseUrlsEmpty() )
454  ZYPP_THROW(Exception("No url in repository."));
455  else
456  url = * info.baseUrlsBegin();
457 
458  // check whether to process patch/delta rpms
459  if ( ZConfig::instance().download_use_deltarpm()
461  {
462  std::list<DeltaRpm> deltaRpms;
463  _deltas.deltaRpms( _package ).swap( deltaRpms );
464 
465  if ( ! deltaRpms.empty() && queryInstalled() && applydeltarpm::haveApplydeltarpm() )
466  {
467  for_( it, deltaRpms.begin(), deltaRpms.end())
468  {
469  DBG << "tryDelta " << *it << endl;
470  ManagedFile ret( tryDelta( *it ) );
471  if ( ! ret->empty() )
472  return ret;
473  }
474  }
475  }
476 
477  // no patch/delta -> provide full package
478  return Base::doProvidePackage();
479  }
480 
481  ManagedFile RpmPackageProvider::tryDelta( const DeltaRpm & delta_r ) const
482  {
483  if ( delta_r.baseversion().edition() != Edition::noedition
484  && ! queryInstalled( delta_r.baseversion().edition() ) )
485  return ManagedFile();
486 
487  if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
488  return ManagedFile();
489 
490  report()->startDeltaDownload( delta_r.location().filename(),
491  delta_r.location().downloadSize() );
492  ManagedFile delta;
493  try
494  {
495  ProvideFilePolicy policy;
496  policy.progressCB( bind( &RpmPackageProvider::progressDeltaDownload, this, _1 ) );
497  delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
498  }
499  catch ( const Exception & excpt )
500  {
501  report()->problemDeltaDownload( excpt.asUserHistory() );
502  return ManagedFile();
503  }
504  report()->finishDeltaDownload();
505 
506  report()->startDeltaApply( delta );
507  if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
508  {
509  report()->problemDeltaApply( _("applydeltarpm check failed.") );
510  return ManagedFile();
511  }
512 
513  // build the package and put it into the cache
514  Pathname destination( _package->repoInfo().packagesPath() / _package->location().filename() );
515 
516  if ( ! applydeltarpm::provide( delta, destination,
517  bind( &RpmPackageProvider::progressDeltaApply, this, _1 ) ) )
518  {
519  report()->problemDeltaApply( _("applydeltarpm failed.") );
520  return ManagedFile();
521  }
522  report()->finishDeltaApply();
523 
524  return ManagedFile( destination, filesystem::unlink );
525  }
526 
527 #if 0
528  class PluginPackageProvider : public PackageProvider::Impl
536  {
537  public:
538  PluginPackageProvider( const std::string & stem_r,
539  RepoMediaAccess & access_r,
540  const Package::constPtr & package_r,
541  const DeltaCandidates & deltas_r,
542  const PackageProviderPolicy & policy_r )
543  : Base( access_r, package_r, deltas_r, policy_r )
544  {}
545 
546  protected:
547  virtual ManagedFile doProvidePackageFromCache() const
548  {
549  return Base::doProvidePackageFromCache();
550  }
551 
552  virtual ManagedFile doProvidePackage() const
553  {
554  return Base::doProvidePackage();
555  }
556  };
558 #endif
559 
561  // class PackageProvider
563 
565  const Package::constPtr & package_r,
566  const DeltaCandidates & deltas_r,
567  const PackageProviderPolicy & policy_r )
568  {
569  return new RpmPackageProvider( access_r, package_r, deltas_r, policy_r );
570  }
571 
573  const Package::constPtr & package_r,
574  const DeltaCandidates & deltas_r,
575  const PackageProviderPolicy & policy_r )
576  : _pimpl( Impl::factoryMake( access_r, package_r, deltas_r, policy_r ) )
577  {}
578 
580  {}
581 
583  { return _pimpl->providePackage(); }
584 
586  { return _pimpl->providePackageFromCache(); }
587 
589  { return _pimpl->isCached(); }
590 
591  } // namespace repo
593 } // namespace zypp
Candidate delta and patches for a package.
const BaseVersion & baseversion() const
Definition: PackageDelta.h:69
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:320
Interface to gettext.
Interface to the rpm program.
Definition: RpmDb.h:47
#define MIL
Definition: Logger.h:64
void defaultReportSignatureError(RpmDb::CheckPackageResult ret, const std::string &detail_r=std::string()) const
Default signature verification error handling.
bool download_use_deltarpm_always() const
Whether to consider using a deltarpm even when rpm is local.
Definition: ZConfig.cc:969
std::string str() const
Definition: String.h:221
PackageProvider implementation.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:321
Describes a path on a certain media amongs as the information required to download it...
static ZConfig & instance()
Singleton ctor.
Definition: Resolver.cc:121
ManagedFile providePackage() const
Provide the package.
virtual ManagedFile doProvidePackageFromCache() const
Lookup the final rpm in cache.
Architecture.
Definition: Arch.h:36
Helper filtering the files offered by a RepomdFileReader.
void reset()
Reset to default Ctor values.
Definition: AutoDispose.h:145
Report & report() const
Access to the DownloadResolvableReport.
bool isCached() const
Whether the package is cached.
const ByteCount & downloadSize() const
The size of the resource on the server.
callback::SendReport< repo::DownloadResolvableReport > Report
Policies and options for PackageProvider.
bool isCached() const
Whether the package is cached.
ManagedFile tryDelta(const DeltaRpm &delta_r) const
bool haveApplydeltarpm()
Test whether an execuatble applydeltarpm program is available.
void progressDeltaApply(int value) const
ManagedFile providePackageFromCache() const
Provide the package if it is cached.
What is known about a repository.
Definition: RepoInfo.h:71
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
Url url
Definition: MediaCurl.cc:196
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
Edition represents [epoch:]version[-release]
Definition: Edition.h:60
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:124
ManagedFile providePackage() const
Provide the package.
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
#define ERR
Definition: Logger.h:66
urls_const_iterator baseUrlsBegin() const
iterator that points at begin of repository urls
Definition: RepoInfo.cc:461
Repo manager settings.
Definition: RepoManager.h:53
bool baseUrlsEmpty() const
whether repository urls are available
Definition: RepoInfo.cc:470
Policy for provideFile.
ManagedFile providePackageFromCache() const
Provide the package if it is cached.
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:329
RW_pointer< Impl > _pimpl
Implementation class.
PackageProvider(RepoMediaAccess &access, const Package::constPtr &package, const DeltaCandidates &deltas, const PackageProviderPolicy &policy_r=PackageProviderPolicy())
Ctor taking the Package to provide.
packagedelta::DeltaRpm DeltaRpm
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
bool keepPackages() const
Whether packages downloaded from this repository will be kept in local cache.
Definition: RepoInfo.cc:407
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:210
bool pkgGpgCheck() const
Whether the signature of rpm packages should be checked for this repo.
Definition: RepoInfo.cc:319
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:653
bool set(const std::string &key_r, AnyType val_r)
Set the value for key (nonconst version always returns true).
Definition: UserData.h:118
const OnMediaLocation & location() const
Definition: PackageDelta.h:68
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:75
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:120
#define USR
Definition: Logger.h:69
#define WAR
Definition: Logger.h:65
Detailed rpm signature check log messages A single multiline message if CHK_OK.
Definition: RpmDb.h:443
RepoInfo info() const
Return any associated RepoInfo.
Definition: Repository.cc:273
bool queryInstalled(const Edition &ed_r=Edition()) const
int hardlinkCopy(const Pathname &oldpath, const Pathname &newpath)
Create newpath as hardlink or copy of oldpath.
Definition: PathInfo.cc:808
RpmDb::CheckPackageResult packageSigCheck(const Pathname &path_r, UserData &userData) const
RpmPackageProvider(RepoMediaAccess &access_r, const Package::constPtr &package_r, const DeltaCandidates &deltas_r, const PackageProviderPolicy &policy_r)
#define _(MSG)
Definition: Gettext.h:29
void resolveSignatureErrorAction(repo::DownloadResolvableReport::Action action_r) const
React on signature verification error user action.
Provides files from different repos.
void setDispose(const Dispose &dispose_r)
Set a new dispose function.
Definition: AutoDispose.h:158
const Pathname & filename() const
The path to the resource relatve to the url and path.
virtual ManagedFile doProvidePackage() const
Actually provide the final rpm.
virtual ManagedFile doProvidePackageFromCache() const =0
Lookup the final rpm in cache.
Base class for Exception.
Definition: Exception.h:143
ProvideFilePolicy & progressCB(ProgressCB progressCB_r)
Set callback.
Provide a package from a Repo.
reference value() const
Reference to the Tp object.
Definition: AutoDispose.h:133
const Tp & get(const std::string &key_r) const
Pass back a const Tp & reference to key_r value.
Definition: UserData.h:175
Impl(RepoMediaAccess &access_r, const Package::constPtr &package_r, const DeltaCandidates &deltas_r, const PackageProviderPolicy &policy_r)
Ctor taking the Package to provide.
callback::SendReport< DownloadProgressReport > * report
Definition: MediaCurl.cc:199
Typesafe passing of user data via callbacks.
Definition: UserData.h:38
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:92
bool progressDeltaDownload(int value) const
shared_ptr< MediaSetAccess > _access
RPM PackageProvider implementation.
Pathname repoPackagesCachePath
Definition: RepoManager.h:82
Pathname packagesPath() const
Path where this repo packages are cached.
Definition: RepoInfo.cc:413
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
bool haskey(const std::string &key_r) const
Whether key_r is in data.
Definition: UserData.h:97
CheckPackageResult
checkPackage result
Definition: RpmDb.h:429
bool quickcheck(const std::string &sequenceinfo_r)
Quick via check sequence info.
Definition: Applydeltarpm.h:48
bool check(const std::string &sequenceinfo_r, bool quick_r)
Check via sequence info.
virtual ManagedFile doProvidePackage() const =0
Actually provide the final rpm.
bool queryInstalled(const std::string &name_r, const Edition &ed_r, const Arch &arch_r) const
Evaluate callback.
bool progressPackageDownload(int value) const
Redirect ProvideFilePolicy package download progress to this.
Base for exceptions caused by explicit user request.
static bool schemeIsDownloading(const std::string &scheme_r)
http https ftp sftp tftp
Definition: Url.cc:474
bool hasvalue(const std::string &key_r) const
Whether key_r is in data and value is not empty.
Definition: UserData.h:101
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
const std::string & sequenceinfo() const
Definition: PackageDelta.h:46
bool provide(const Pathname &delta_r, const Pathname &new_r, const Progress &report_r)
Apply a binary delta to on-disk data to re-create a new rpm.
Url manipulation class.
Definition: Url.h:87
TraitsType::constPtrType constPtr
Definition: Package.h:38
#define DBG
Definition: Logger.h:63
static const Edition noedition
Value representing noedition ("") This is in fact a valid Edition.
Definition: Edition.h:73
const Repository & repository() const
Definition: PackageDelta.h:70
static Impl * factoryMake(RepoMediaAccess &access_r, const Package::constPtr &package_r, const DeltaCandidates &deltas_r, const PackageProviderPolicy &policy_r)
Factory method providing the appropriate implementation.