Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / repo / RepoProvideFile.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/source/RepoProvideFile.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <set>
16
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/String.h"
20 #include "zypp/base/UserRequestException.h"
21 #include "zypp/repo/RepoProvideFile.h"
22 #include "zypp/ZYppCallbacks.h"
23 #include "zypp/MediaSetAccess.h"
24 #include "zypp/ZConfig.h"
25 #include "zypp/ZYppFactory.h"
26 #include "zypp/repo/SUSEMediaVerifier.h"
27 #include "zypp/repo/RepoException.h"
28
29 #include "zypp/repo/SUSEMediaVerifier.h"
30 #include "zypp/repo/RepoException.h"
31 #include "zypp/FileChecker.h"
32 #include "zypp/Fetcher.h"
33
34 using std::endl;
35 using std::set;
36
37 ///////////////////////////////////////////////////////////////////
38 namespace zypp
39 { /////////////////////////////////////////////////////////////////
40   ///////////////////////////////////////////////////////////////////
41   namespace repo
42   { /////////////////////////////////////////////////////////////////
43
44     ///////////////////////////////////////////////////////////////////
45     //
46     //  provideFile
47     //
48     ///////////////////////////////////////////////////////////////////
49
50     ///////////////////////////////////////////////////////////////////
51     namespace
52     { /////////////////////////////////////////////////////////////////
53
54       /** Hack to extract progress information from media::DownloadProgressReport.
55        * We redirect the static report triggered from RepoInfo::provideFile
56        * to feed the ProvideFilePolicy callbacks in addition to any connected
57        * media::DownloadProgressReport.
58       */
59       struct DownloadFileReportHack : public callback::ReceiveReport<media::DownloadProgressReport>
60       {
61         typedef callback::ReceiveReport<ReportType> BaseType;
62         typedef function<bool(int)>                 RedirectType;
63
64         DownloadFileReportHack( RedirectType redirect_r )
65         : _oldRec( Distributor::instance().getReceiver() )
66         , _redirect( redirect_r )
67         { connect(); }
68         ~DownloadFileReportHack()
69         { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
70
71         virtual void start( const Url & file, Pathname localfile )
72         {
73           if ( _oldRec )
74             _oldRec->start( file, localfile );
75           else
76             BaseType::start( file, localfile );
77         }
78
79         virtual bool progress( int value, const Url & file, double dbps_avg = -1, double dbps_current = -1 )
80         {
81           bool ret = true;
82           if ( _oldRec )
83             ret &= _oldRec->progress( value, file, dbps_avg, dbps_current );
84           if ( _redirect )
85             ret &= _redirect( value );
86           return ret;
87         }
88
89         virtual Action problem( const Url & file, Error error, const std::string & description )
90         {
91           if ( _oldRec )
92             return _oldRec->problem( file, error, description );
93           return BaseType::problem( file, error, description );
94         }
95         virtual void finish( const Url & file, Error error, const std::string & reason )
96         {
97           if ( _oldRec )
98             _oldRec->finish( file, error, reason );
99           else
100             BaseType::finish( file, error, reason );
101         }
102
103         private:
104           Receiver * _oldRec;
105           RedirectType _redirect;
106       };
107
108       /////////////////////////////////////////////////////////////////
109     } // namespace
110     ///////////////////////////////////////////////////////////////////
111
112     ManagedFile provideFile( RepoInfo repo_r,
113                              const OnMediaLocation & loc_r,
114                              const ProvideFilePolicy & policy_r )
115     {
116       RepoMediaAccess access;
117       return access.provideFile(repo_r, loc_r, policy_r );
118     }
119
120     ///////////////////////////////////////////////////////////////////
121     class RepoMediaAccess::Impl
122     {
123     public:
124       Impl( const ProvideFilePolicy & defaultPolicy_r )
125         : _defaultPolicy( defaultPolicy_r )
126       {}
127
128       ~Impl()
129       {
130         std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
131         for ( it = _medias.begin();
132               it != _medias.end();
133               ++it )
134         {
135           it->second->release();
136         }
137       }
138
139       /** Provide a MediaSetAccess for \c url with label and verifyer adjusted.
140        *
141        * As the same url (e.g. \c 'dvd:///' ) might be used for multiple repos
142        * we must always adjust the repo specific data (label,verifyer).
143        *
144        * \todo This mixture of media and repos specific data is fragile.
145       */
146       shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url, RepoInfo repo )
147       {
148         std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
149         it = _medias.find(url);
150         shared_ptr<MediaSetAccess> media;
151         if ( it != _medias.end() )
152         {
153           media = it->second;
154         }
155         else
156         {
157           media.reset( new MediaSetAccess(url) );
158           _medias[url] = media;
159         }
160         setVerifierForRepo( repo, media );
161         return media;
162       }
163
164       private:
165         void setVerifierForRepo( RepoInfo repo, shared_ptr<MediaSetAccess> media )
166         {
167           // Always set the MediaSetAccess label.
168           media->setLabel( repo.name() );
169
170           // set a verifier if the repository has it
171
172           Pathname mediafile = repo.metadataPath() + "/media.1/media";
173           if ( ! repo.metadataPath().empty() )
174           {
175             if ( PathInfo(mediafile).isExist() )
176             {
177               std::map<shared_ptr<MediaSetAccess>, RepoInfo>::const_iterator it;
178               it = _verifier.find(media);
179               if ( it != _verifier.end() )
180               {
181                 if ( it->second.alias() == repo.alias() )
182                 {
183                   // this media is already using this repo verifier
184                   return;
185                 }
186               }
187
188               std::ifstream str(mediafile.asString().c_str());
189               std::string vendor;
190               std::string mediaid;
191               std::string buffer;
192               if ( str )
193               {
194                 getline(str, vendor);
195                 getline(str, mediaid);
196                 getline(str, buffer);
197
198                 unsigned media_nr = str::strtonum<unsigned>(buffer);
199                 MIL << "Repository '" << repo.alias() << "' has " << media_nr << " medias"<< endl;
200
201                 for ( unsigned i=1; i <= media_nr; ++i )
202                 {
203                   media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
204
205                   media->setVerifier( i, verifier);
206                 }
207                 _verifier[media] = repo;
208               }
209               else
210               {
211                 ZYPP_THROW(RepoMetadataException(repo));
212               }
213             }
214             else
215             {
216               WAR << "No media verifier for repo '" << repo.alias() << "' media/media.1 does not exist in '" << repo.metadataPath() << "'" << endl;
217             }
218           }
219           else
220           {
221             WAR << "'" << repo.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
222           }
223         }
224
225       private:
226         std::map<shared_ptr<MediaSetAccess>, RepoInfo> _verifier;
227         std::map<Url, shared_ptr<MediaSetAccess> > _medias;
228
229       public:
230         ProvideFilePolicy _defaultPolicy;
231     };
232     ///////////////////////////////////////////////////////////////////
233
234
235     RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
236       : _impl( new Impl( defaultPolicy_r ) )
237     {}
238
239     RepoMediaAccess::~RepoMediaAccess()
240     {}
241
242     void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
243     { _impl->_defaultPolicy = policy_r; }
244
245     const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
246     { return _impl->_defaultPolicy; }
247
248     ManagedFile RepoMediaAccess::provideFile( RepoInfo repo_r,
249                                               const OnMediaLocation & loc_r,
250                                               const ProvideFilePolicy & policy_r )
251     {
252       MIL << loc_r << endl;
253       // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
254       // and redirect download progress triggers to call the ProvideFilePolicy
255       // callback.
256       DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
257
258       RepoException repo_excpt(repo_r,
259                                str::form(_("Can't provide file '%s' from repository '%s'"),
260                                loc_r.filename().c_str(),
261                                repo_r.alias().c_str() ) );
262
263       if ( repo_r.baseUrlsEmpty() )
264       {
265         repo_excpt.remember(RepoException(_("No url in repository.")));
266         ZYPP_THROW(repo_excpt);
267       }
268
269       Fetcher fetcher;
270       fetcher.addCachePath( repo_r.packagesPath() );
271       MIL << "Added cache path " << repo_r.packagesPath() << endl;
272
273       // Test whether download destination is writable, if not
274       // switch into the tmpspace (e.g. bnc#755239, download and
275       // install srpms as user).
276       Pathname destinationDir( repo_r.packagesPath() );
277
278       PathInfo pi( destinationDir );
279       if ( ! pi.isExist() )
280       {
281         // try to create it...
282         assert_dir( destinationDir );
283         pi();
284       }
285       if ( geteuid() != 0 && ! pi.userMayW() )
286       {
287         WAR << "Destination dir '" << destinationDir << "' is not user writable, using tmp space." << endl;
288         destinationDir = getZYpp()->tmpPath() / destinationDir;
289         assert_dir( destinationDir );
290         fetcher.addCachePath( destinationDir );
291         MIL << "Added cache path " << destinationDir << endl;
292       }
293
294       // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
295       media::ScopedDisableMediaChangeReport guard( repo_r.baseUrlsSize() > 1 );
296
297       for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
298             it != repo_r.baseUrlsEnd();
299             /* incremented in the loop */ )
300       {
301         Url url( *it );
302         ++it;
303         try
304         {
305           MIL << "Providing file of repo '" << repo_r.alias() << "' from " << url << endl;
306           shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
307
308           fetcher.enqueue( loc_r );
309           fetcher.start( destinationDir, *access );
310
311           // reached if no exception has been thrown, so this is the correct file
312           ManagedFile ret( destinationDir + loc_r.filename() );
313           if ( !repo_r.keepPackages() )
314           {
315             ret.setDispose( filesystem::unlink );
316           }
317
318           MIL << "provideFile at " << ret << endl;
319           return ret;
320         }
321         catch ( const UserRequestException & excpt )
322         {
323           ZYPP_CAUGHT( excpt );
324           ZYPP_RETHROW( excpt );
325         }
326         catch ( const FileCheckException & excpt )
327         {
328           ZYPP_CAUGHT( excpt );
329           ZYPP_RETHROW( excpt );
330         }
331         catch ( const Exception &e )
332         {
333           ZYPP_CAUGHT( e );
334
335           repo_excpt.remember(e);
336
337           WAR << "Trying next url" << endl;
338           continue;
339         }
340       } // iteration over urls
341
342       ZYPP_THROW(repo_excpt);
343       return ManagedFile(); // not reached
344     }
345
346     /////////////////////////////////////////////////////////////////
347   } // namespace repo
348   ///////////////////////////////////////////////////////////////////
349   /////////////////////////////////////////////////////////////////
350 } // namespace zypp
351 ///////////////////////////////////////////////////////////////////