- merge Stano's patch to keep downloaded rpms
[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/repo/SUSEMediaVerifier.h"
26 #include "zypp/repo/RepoException.h"
27
28 #include "zypp/repo/SUSEMediaVerifier.h"
29 #include "zypp/repo/RepoException.h"
30 #include "zypp/FileChecker.h"
31 #include "zypp/Fetcher.h"
32
33 using std::endl;
34 using std::set;
35
36 ///////////////////////////////////////////////////////////////////
37 namespace zypp
38 { /////////////////////////////////////////////////////////////////
39   ///////////////////////////////////////////////////////////////////
40   namespace repo
41   { /////////////////////////////////////////////////////////////////
42
43     ///////////////////////////////////////////////////////////////////
44     //
45     //  provideFile
46     //
47     ///////////////////////////////////////////////////////////////////
48
49     ///////////////////////////////////////////////////////////////////
50     namespace
51     { /////////////////////////////////////////////////////////////////
52
53       /** Hack to extract progress information from source::DownloadFileReport.
54        * We redirect the static report triggered from Repository::provideFile
55        * to feed the ProvideFilePolicy callbacks.
56       */
57       struct DownloadFileReportHack : public callback::ReceiveReport<repo::RepoReport>
58       {
59         virtual bool progress( const ProgressData &progress )
60         {
61           if ( _redirect )
62             return _redirect( progress.val() );
63           return true;
64         }
65         function<bool ( int )> _redirect;
66       };
67
68       /////////////////////////////////////////////////////////////////
69     } // namespace
70     ///////////////////////////////////////////////////////////////////
71
72     ManagedFile provideFile( Repository repo_r,
73                              const OnMediaLocation & loc_r,
74                              const ProvideFilePolicy & policy_r )
75     {
76       RepoMediaAccess access;
77       return access.provideFile(repo_r, loc_r, policy_r );
78     }
79
80     class RepoMediaAccess::Impl
81     {
82     public:
83       Impl( const ProvideFilePolicy & defaultPolicy_r )
84         : _defaultPolicy( defaultPolicy_r )
85       {}
86
87       ~Impl()
88       {
89         std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
90         for ( it = _medias.begin();
91               it != _medias.end();
92               ++it )
93         {
94           it->second->release();
95         }
96       }
97
98       shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url )
99       {
100         std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
101         it = _medias.find(url);
102         shared_ptr<MediaSetAccess> media;
103         if ( it != _medias.end() )
104         {
105           media = it->second;
106         }
107         else
108         {
109           media.reset( new MediaSetAccess(url) );
110           _medias[url] = media;
111         }
112         return media;
113       }
114
115       void setVerifierForRepo( Repository repo, shared_ptr<MediaSetAccess> media )
116       {
117         RepoInfo info = repo.info();
118         // set a verifier if the repository has it
119          
120         Pathname mediafile = info.metadataPath() + "/media.1/media";
121         if ( ! info.metadataPath().empty() )
122         {
123           if ( PathInfo(mediafile).isExist() )
124           {
125             std::map<shared_ptr<MediaSetAccess>, Repository>::const_iterator it;
126             it = _verifier.find(media);
127             if ( it != _verifier.end() )
128             {
129               if ( it->second == repo )
130               {
131                 // this media is already using this repo verifier
132                 return;
133               }
134             }
135
136             std::ifstream str(mediafile.asString().c_str());
137             std::string vendor;
138             std::string mediaid;
139             std::string buffer;
140             if ( str )
141             {
142               getline(str, vendor);
143               getline(str, mediaid);
144               getline(str, buffer);
145
146               unsigned media_nr = str::strtonum<unsigned>(buffer);
147               MIL << "Repository '" << info.alias() << "' has " << media_nr << " medias"<< endl;
148
149               for ( unsigned i=1; i <= media_nr; ++i )
150               {
151                 media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
152
153                 media->setVerifier( i, verifier);
154               }
155               _verifier[media] = repo;
156             }
157             else
158             {
159               ZYPP_THROW(RepoMetadataException(info));
160             }
161           }
162           else
163           {
164             WAR << "No media verifier for repo '" << info.alias() << "' media/media.1 does not exist in '" << info.metadataPath() << "'" << endl;
165           }
166         }
167         else
168         {
169           WAR << "'" << info.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
170         }
171       }
172
173       std::map<shared_ptr<MediaSetAccess>, Repository> _verifier;
174       std::map<Url, shared_ptr<MediaSetAccess> > _medias;
175       ProvideFilePolicy _defaultPolicy;
176     };
177
178
179
180     RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
181       : _impl( new Impl( defaultPolicy_r ) )
182     {}
183
184     RepoMediaAccess::~RepoMediaAccess()
185     {}
186
187     void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
188     { _impl->_defaultPolicy = policy_r; }
189
190     const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
191     { return _impl->_defaultPolicy; }
192
193     ManagedFile RepoMediaAccess::provideFile( Repository repo_r,
194                                               const OnMediaLocation & loc_r,
195                                               const ProvideFilePolicy & policy_r )
196     {
197       MIL << loc_r << endl;
198       // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
199       // and redirect download progress triggers to call the ProvideFilePolicy
200       // callback.
201       DownloadFileReportHack dumb;
202       dumb._redirect = bind( mem_fun_ref( &ProvideFilePolicy::progress ),
203                              ref( policy_r ), _1 );
204       callback::TempConnect<repo::RepoReport> temp( dumb );
205
206       Url url;
207       RepoInfo info = repo_r.info();
208       
209       RepoException repo_excpt(str::form(_("Can't provide file '%s' from repository '%s'"),
210                                loc_r.filename().c_str(),
211                                info.alias().c_str() ) );
212       
213       if ( info.baseUrlsEmpty() )
214       {
215         repo_excpt.remember(RepoException(_("No url in repository.")));
216         ZYPP_THROW(repo_excpt);
217       }
218
219       Fetcher fetcher;
220       fetcher.addCachePath( info.packagesPath() );
221       
222       MIL << "Added cache path " << info.packagesPath() << endl;
223             
224       for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin();
225             it != info.baseUrlsEnd();
226             /* incremented in the loop */ )
227       {
228         url = *it;
229         ++it;
230         try
231         {
232           MIL << "Providing file of repo '" << info.alias()
233               << "' from " << url << endl;
234           shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl(url);
235           _impl->setVerifierForRepo(repo_r, access);
236           
237           fetcher.enqueue( loc_r );
238           
239           // FIXME: works for packages only
240           fetcher.start( info.packagesPath(), *access );
241
242           // reached if no exception has been thrown, so this is the correct file
243           ManagedFile ret( info.packagesPath() + loc_r.filename() );
244
245           std::string scheme( url.getScheme() );
246           if ( !info.keepPackages() )
247           {
248             ret.setDispose( filesystem::unlink );
249           }
250
251           if ( loc_r.checksum().empty() )
252           {
253             // no checksum in metadata
254             WAR << "No checksum in metadata " << loc_r << endl;
255           }
256           else
257           {
258             std::ifstream input( ret->asString().c_str() );
259             CheckSum retChecksum( loc_r.checksum().type(), input );
260             input.close();
261
262             if ( loc_r.checksum() != retChecksum )
263             {
264               // failed integity check
265               std::ostringstream err;
266               err << "File " << ret << " fails integrity check. Expected: [" << loc_r.checksum() << "] Got: [";
267               if ( retChecksum.empty() )
268                 err << "Failed to compute checksum";
269               else
270                 err << retChecksum;
271               err << "]";
272
273               if ( policy_r.failOnChecksumError() )
274                 ZYPP_THROW( FileCheckException( err.str() ) );
275               else
276                 WAR << "NO failOnChecksumError: " << err.str() << endl;
277             }
278           }
279
280           MIL << "provideFile at " << ret << endl;
281           return ret;
282         }
283         catch ( const SkipRequestException &e )
284         {
285           ZYPP_CAUGHT( e );
286           ZYPP_RETHROW(e);
287         }
288         catch ( const AbortRequestException &e )
289         {
290           ZYPP_CAUGHT( e );
291           ZYPP_RETHROW(e);
292         }
293         catch ( const Exception &e )
294         {
295           ZYPP_CAUGHT( e );
296           
297           repo_excpt.remember(e);
298           
299           WAR << "Trying next url" << endl;
300           continue;
301         }
302       } // iteration over urls
303
304       ZYPP_THROW(repo_excpt);
305       return ManagedFile(); // not reached
306     }
307
308     /////////////////////////////////////////////////////////////////
309   } // namespace repo
310   ///////////////////////////////////////////////////////////////////
311   /////////////////////////////////////////////////////////////////
312 } // namespace zypp
313 ///////////////////////////////////////////////////////////////////