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