- Add Exception::asUserHistory: Returns a single (multiline) string composed
[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 <sstream>
14 #include "zypp/repo/PackageDelta.h"
15 #include "zypp/base/Logger.h"
16 #include "zypp/base/Gettext.h"
17 #include "zypp/base/UserRequestException.h"
18 #include "zypp/repo/PackageProvider.h"
19 #include "zypp/repo/RepoProvideFile.h"
20 #include "zypp/repo/Applydeltarpm.h"
21 #include "zypp/repo/PackageDelta.h"
22
23 #include "zypp/TmpPath.h"
24 #include "zypp/ZConfig.h"
25 #include "zypp/RepoInfo.h"
26 #include "zypp/media/MediaManager.h"
27
28 using std::endl;
29 using zypp::media::MediaManager;
30
31 ///////////////////////////////////////////////////////////////////
32 namespace zypp
33 { /////////////////////////////////////////////////////////////////
34   ///////////////////////////////////////////////////////////////////
35   namespace repo
36   { /////////////////////////////////////////////////////////////////
37
38     ///////////////////////////////////////////////////////////////////
39     //
40     //  CLASS NAME : PackageProviderPolicy
41     //
42     ///////////////////////////////////////////////////////////////////
43
44     bool PackageProviderPolicy::queryInstalled( const std::string & name_r,
45                                                 const Edition &     ed_r,
46                                                 const Arch &        arch_r ) const
47     {
48       if ( _queryInstalledCB )
49         return _queryInstalledCB( name_r, ed_r, arch_r );
50       return false;
51     }
52
53     ///////////////////////////////////////////////////////////////////
54     //
55     //  CLASS NAME : PackageProvider
56     //
57     ///////////////////////////////////////////////////////////////////
58
59     ///////////////////////////////////////////////////////////////////
60     namespace
61     { /////////////////////////////////////////////////////////////////
62
63       inline std::string defRpmFileName( const Package::constPtr & package )
64       {
65         std::ostringstream ret;
66         ret << package->name() << '-' << package->edition() << '.' << package->arch() << ".rpm";
67         return ret.str();
68       }
69
70       /////////////////////////////////////////////////////////////////
71     } // namespace source
72     ///////////////////////////////////////////////////////////////////
73     PackageProvider::PackageProvider(  RepoMediaAccess &access,
74                                       const Package::constPtr & package,
75                                       const DeltaCandidates & deltas,
76                                       const PackageProviderPolicy & policy_r )
77     : _policy( policy_r )
78     , _package( package )
79     , _deltas(deltas)
80     , _access(access)
81     {}
82
83     PackageProvider::~PackageProvider()
84     {}
85
86     ManagedFile PackageProvider::providePackage() const
87     {
88       Url url;
89       RepoInfo info = _package->repoInfo();
90       // FIXME we only support the first url for now.
91       if ( info.baseUrlsEmpty() )
92         ZYPP_THROW(Exception("No url in repository."));
93       else
94         url = * info.baseUrlsBegin();
95
96       MIL << "provide Package " << _package << endl;
97       ScopedGuard guardReport( newReport() );
98       ManagedFile ret;
99       do {
100         _retry = false;
101         report()->start( _package, url );
102         try  // ELIMINATE try/catch by providing a log-guard
103           {
104             ret = doProvidePackage();
105           }
106         catch ( const UserRequestException & excpt )
107           {
108             // UserRequestException e.g. from failOnChecksumError was already reported.
109             ERR << "Failed to provide Package " << _package << endl;
110             if ( ! _retry )
111               {
112                 ZYPP_RETHROW( excpt );
113               }
114           }
115         catch ( const Exception & excpt )
116           {
117             ERR << "Failed to provide Package " << _package << endl;
118             if ( ! _retry )
119               {
120                 // Aything else gets reported
121                 std::string package_str = _package->name() + "-" + _package->edition().asString();
122
123                 // TranslatorExplanation %s = name of the package being processed.
124                 std::string detail_str( str::form(_("Failed to provide Package %s. Do you want to retry retrieval?"), package_str.c_str() ) );
125                 detail_str += str::form( "\n\n%s", excpt.asUserHistory().c_str() );
126
127                 switch ( report()->problem( _package, repo::DownloadResolvableReport::IO, detail_str.c_str() ) )
128                 {
129                       case repo::DownloadResolvableReport::RETRY:
130                         _retry = true;
131                         break;
132                       case repo::DownloadResolvableReport::IGNORE:
133                         ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
134                         break;
135                       case repo::DownloadResolvableReport::ABORT:
136                         ZYPP_THROW(AbortRequestException("User requested to abort"));
137                         break;
138                       default:
139                         ZYPP_RETHROW( excpt );
140                         break;
141                 }
142               }
143           }
144       } while ( _retry );
145
146       report()->finish( _package, repo::DownloadResolvableReport::NO_ERROR, std::string() );
147       MIL << "provided Package " << _package << " at " << ret << endl;
148       return ret;
149     }
150
151     ManagedFile PackageProvider::doProvidePackage() const
152     {
153       Url url;
154       RepoInfo info = _package->repoInfo();
155       // FIXME we only support the first url for now.
156       if ( info.baseUrlsEmpty() )
157         ZYPP_THROW(Exception("No url in repository."));
158       else
159         url = * info.baseUrlsBegin();
160
161       // check whether to process patch/delta rpms
162       if ( MediaManager::downloads(url) || ZConfig::instance().download_use_deltarpm_always() )
163         {
164           std::list<DeltaRpm> deltaRpms;
165           if ( ZConfig::instance().download_use_deltarpm() )
166           {
167             _deltas.deltaRpms( _package ).swap( deltaRpms );
168           }
169
170           if ( ! ( deltaRpms.empty() )
171                && queryInstalled() )
172             {
173               if ( ! deltaRpms.empty() && applydeltarpm::haveApplydeltarpm() )
174                 {
175                   for( std::list<DeltaRpm>::const_iterator it = deltaRpms.begin();
176                        it != deltaRpms.end(); ++it )
177                     {
178                       DBG << "tryDelta " << *it << endl;
179                       ManagedFile ret( tryDelta( *it ) );
180                       if ( ! ret->empty() )
181                         return ret;
182                     }
183                 }
184             }
185         }
186
187       // no patch/delta -> provide full package
188       ManagedFile ret;
189       OnMediaLocation loc = _package->location();
190
191       ProvideFilePolicy policy;
192       policy.progressCB( bind( &PackageProvider::progressPackageDownload, this, _1 ) );
193       policy.failOnChecksumErrorCB( bind( &PackageProvider::failOnChecksumError, this ) );
194       return _access.provideFile( _package->repoInfo(), loc, policy );
195     }
196
197     ManagedFile PackageProvider::tryDelta( const DeltaRpm & delta_r ) const
198     {
199       if ( delta_r.baseversion().edition() != Edition::noedition
200            && ! queryInstalled( delta_r.baseversion().edition() ) )
201         return ManagedFile();
202
203       if ( ! applydeltarpm::quickcheck( delta_r.baseversion().sequenceinfo() ) )
204         return ManagedFile();
205
206       report()->startDeltaDownload( delta_r.location().filename(),
207                                     delta_r.location().downloadSize() );
208       ManagedFile delta;
209       try
210         {
211           ProvideFilePolicy policy;
212           policy.progressCB( bind( &PackageProvider::progressDeltaDownload, this, _1 ) );
213           delta = _access.provideFile( delta_r.repository().info(), delta_r.location(), policy );
214         }
215       catch ( const Exception & excpt )
216         {
217           report()->problemDeltaDownload( excpt.asUserHistory() );
218           return ManagedFile();
219         }
220       report()->finishDeltaDownload();
221
222       report()->startDeltaApply( delta );
223       if ( ! applydeltarpm::check( delta_r.baseversion().sequenceinfo() ) )
224         {
225           report()->problemDeltaApply( _("applydeltarpm check failed.") );
226           return ManagedFile();
227         }
228
229       Pathname destination( Pathname::dirname( delta ) / defRpmFileName( _package ) );
230
231       if ( ! delta.getDispose() )
232       {
233         // There is no cleanup method associated with the deta. Thus the
234         // delta is not a temporary file, and we don't want to write in
235         // the package into this directory.
236         destination = filesystem::TmpPath::defaultLocation() / defRpmFileName( _package );
237       }
238
239       if ( ! applydeltarpm::provide( delta, destination,
240                                      bind( &PackageProvider::progressDeltaApply, this, _1 ) ) )
241         {
242           report()->problemDeltaApply( _("applydeltarpm failed.") );
243           return ManagedFile();
244         }
245       report()->finishDeltaApply();
246
247       return ManagedFile( destination, filesystem::unlink );
248     }
249
250     PackageProvider::ScopedGuard PackageProvider::newReport() const
251     {
252       _report.reset( new Report );
253       return shared_ptr<void>( static_cast<void*>(0),
254                                // custom deleter calling _report.reset()
255                                // (cast required as reset is overloaded)
256                                bind( mem_fun_ref( static_cast<void (shared_ptr<Report>::*)()>(&shared_ptr<Report>::reset) ),
257                                      ref(_report) ) );
258     }
259
260     PackageProvider::Report & PackageProvider::report() const
261     { return *_report; }
262
263     bool PackageProvider::progressDeltaDownload( int value ) const
264     { return report()->progressDeltaDownload( value ); }
265
266     void PackageProvider::progressDeltaApply( int value ) const
267     { return report()->progressDeltaApply( value ); }
268
269     bool PackageProvider::progressPackageDownload( int value ) const
270     { return report()->progress( value, _package ); }
271
272     bool PackageProvider::failOnChecksumError() const
273     {
274       std::string package_str = _package->name() + "-" + _package->edition().asString();
275
276       // TranslatorExplanation %s = package being checked for integrity
277       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() ) ) )
278         {
279         case repo::DownloadResolvableReport::RETRY:
280           _retry = true;
281           break;
282           case repo::DownloadResolvableReport::IGNORE:
283           ZYPP_THROW(SkipRequestException("User requested skip of corrupted file"));
284           break;
285           case repo::DownloadResolvableReport::ABORT:
286           ZYPP_THROW(AbortRequestException("User requested to abort"));
287           break;
288         default:
289           break;
290         }
291       return true; // anyway a failure
292     }
293
294     bool PackageProvider::queryInstalled( const Edition & ed_r ) const
295     { return _policy.queryInstalled( _package->name(), ed_r, _package->arch() ); }
296
297
298     /////////////////////////////////////////////////////////////////
299   } // namespace repo
300   ///////////////////////////////////////////////////////////////////
301   /////////////////////////////////////////////////////////////////
302 } // namespace zypp
303 ///////////////////////////////////////////////////////////////////