- validate the files on start()
[platform/upstream/libzypp.git] / zypp / Fetcher.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/Fetcher.cc
10  *
11 */
12 #include <iostream>
13 #include <list>
14
15 #include "zypp/base/Logger.h"
16 #include "zypp/base/DefaultIntegral.h"
17 #include "zypp/ZYppFactory.h"
18 #include "zypp/Fetcher.h"
19 #include "zypp/KeyRing.h"
20
21 using namespace std;
22
23 ///////////////////////////////////////////////////////////////////
24 namespace zypp
25 { /////////////////////////////////////////////////////////////////
26
27   Fetcher::ChecksumFileChecker::ChecksumFileChecker( const CheckSum &checksum )
28     : _checksum(checksum)
29   {
30   }
31
32   bool Fetcher::ChecksumFileChecker::operator()( const Pathname &file ) const
33   {
34     callback::SendReport<DigestReport> report;
35     CheckSum real_checksum( _checksum.type(), filesystem::checksum( file, _checksum.type() ));
36     
37     if ( _checksum.empty() )
38     {
39       MIL << "File " <<  file << " has no checksum available." << std::endl;
40       if ( report->askUserToAcceptNoDigest(file) )
41       {
42         MIL << "User accepted " <<  file << " with no checksum." << std::endl;
43         return true;
44       }
45       else
46       {
47         return false;
48       }
49     }
50     else
51     {
52       if ( (real_checksum == _checksum) )
53       {
54         if ( report->askUserToAcceptWrongDigest( file, _checksum.checksum(), real_checksum.checksum() ) )
55         {
56           WAR << "User accepted " <<  file << " with WRONG CHECKSUM." << std::endl;
57           return true;
58         }
59         else
60         {
61           return false;
62         }
63       }
64       else
65       {
66         return true;
67       }
68     }
69   }
70
71   bool Fetcher::NullFileChecker::operator()(const Pathname &file ) const
72   {
73     return true;
74   }
75
76   bool Fetcher::CompositeFileChecker::operator()(const Pathname &file ) const
77   {
78     bool result = true;
79     for ( list<Fetcher::FileChecker>::const_iterator it = _checkers.begin(); it != _checkers.end(); ++it )
80     {
81       result = result && (*it)(file);
82     }
83     return result;
84   }
85   
86   void Fetcher::CompositeFileChecker::add( const FileChecker &checker )
87   {
88     _checkers.push_back(checker);
89   }
90
91   Fetcher::SignatureFileChecker::SignatureFileChecker( const Pathname &signature )
92     : _signature(signature)
93   {
94   }
95   
96   Fetcher::SignatureFileChecker::SignatureFileChecker()
97   {
98   }
99   
100   void Fetcher::SignatureFileChecker::addPublicKey( const Pathname &publickey )
101   {
102     ZYpp::Ptr z = getZYpp();
103     z->keyRing()->importKey(publickey, false);
104   }
105   
106   bool Fetcher::SignatureFileChecker::operator()(const Pathname &file ) const
107   {
108     ZYpp::Ptr z = getZYpp();
109     MIL << "checking " << file << " file validity using digital signature.." << endl;
110     bool valid = z->keyRing()->verifyFileSignatureWorkflow( file, string(), _signature);
111     return valid;
112   }
113   
114   /**
115    * Class to encapsulate the \ref OnMediaLocation object
116    * and the \ref Fetcher::FileChcker together
117    */
118   struct FetcherJob
119   {
120     FetcherJob( const OnMediaLocation &loc )
121       : location(loc)
122     {
123
124     }
125
126     OnMediaLocation location;
127     Fetcher::CompositeFileChecker checkers;
128   };
129
130   ///////////////////////////////////////////////////////////////////
131   //
132   //    CLASS NAME : Fetcher::Impl
133   //
134   /** Fetcher implementation. */
135   struct Fetcher::Impl
136   {
137
138   public:
139
140     void enqueue( const OnMediaLocation &resource, const Fetcher::FileChecker &checker  );
141     void enqueueDigested( const OnMediaLocation &resource, const Fetcher::FileChecker &checker );
142     void addCachePath( const Pathname &cache_dir );
143     void reset();
144     void start( const Pathname &dest_dir, MediaSetAccess &media );
145
146     /** Offer default Impl. */
147     static shared_ptr<Impl> nullimpl()
148     {
149       static shared_ptr<Impl> _nullimpl( new Impl );
150       return _nullimpl;
151     }
152
153   private:
154     friend Impl * rwcowClone<Impl>( const Impl * rhs );
155     /** clone for RWCOW_pointer */
156     Impl * clone() const
157     { return new Impl( *this ); }
158
159     std::list<FetcherJob> _resources;
160     std::list<Pathname> _caches;
161   };
162   ///////////////////////////////////////////////////////////////////
163
164
165   void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
166   {
167     CompositeFileChecker composite;
168     composite.add(ChecksumFileChecker(resource.checksum()));
169     composite.add(checker);
170     enqueue(resource, composite);
171   }
172   
173   void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
174   {
175     FetcherJob job(resource);
176     job.checkers.add(checker);
177     _resources.push_back(resource);
178   }
179
180   void Fetcher::Impl::reset()
181   {
182     _resources.clear();
183     _caches.clear();
184   }
185   
186   void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
187   {
188     _caches.push_back(cache_dir);
189   }
190   
191   void Fetcher::Impl::start( const Pathname &dest_dir, MediaSetAccess &media )
192   {
193     for ( list<FetcherJob>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
194     {
195       bool got_from_cache = false;
196       for ( list<Pathname>::const_iterator it_cache = _caches.begin(); it_cache != _caches.end(); ++it_cache )
197       {
198         // Pathinfos could be cached to avoid too many stats?
199         PathInfo info(*it_cache);
200         if ( info.isDir() )
201         {
202           // does the current file exists in the current cache?
203           Pathname cached_file = *it_cache + (*it_res).location.filename();
204           if ( PathInfo( cached_file ).isExist() )
205           {
206             // check the checksum
207             if ( is_checksum( cached_file, (*it_res).location.checksum() ) )
208             {
209               // cached
210               MIL << "file " << (*it_res).location.filename() << " found in previous cache. Using cached copy." << endl;
211               // checksum is already checked.
212               // we could later implement double failover and try to download if file copy fails.
213   
214               // replicate the complete path in the target directory
215               Pathname dest_full_path = dest_dir + (*it_res).location.filename();
216               if ( assert_dir( dest_full_path.dirname() ) != 0 )
217                 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
218   
219               if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
220               { //copy_file2dir
221                 //ZYPP_THROW(SourceIOException("Can't copy " + cached_file.asString() + " to " + destination.asString()));
222                 ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
223                 // try next cache
224                 continue;
225               }
226   
227               got_from_cache = true;
228               break;
229             }
230           }
231           else
232           {
233             // File exists in cache but with a different checksum
234             // so just try next cache
235             continue;
236           }
237         }
238         else
239         {
240           // skip bad cache directory and try with next one
241           ERR << "Skipping cache : " << *it_cache << endl;
242           continue;
243         }
244       }
245   
246       if ( ! got_from_cache )
247       {
248         // try to get the file from the net
249         try
250         {
251           Pathname tmp_file = media.provideFile((*it_res).location);
252           Pathname dest_full_path = dest_dir + (*it_res).location.filename();
253           if ( assert_dir( dest_full_path.dirname() ) != 0 )
254                 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
255           if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
256           {
257             ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
258           }
259           
260           
261         }
262         catch (const Exception & excpt_r)
263         {
264           ZYPP_CAUGHT(excpt_r);
265           ZYPP_THROW(Exception("Can't provide " + (*it_res).location.filename().asString() + " : " + excpt_r.msg() ));
266         }
267       }
268       else
269       {
270         // We got the file from cache
271         // continue with next file
272         continue;
273       }
274       
275       // no matter where did we got the file, try to validate it:
276        Pathname localfile = dest_dir + (*it_res).location.filename();
277        // call the checker function
278        bool good = (*it_res).checkers(localfile);
279        if (!good)
280        {
281          //FIXME better message
282          ZYPP_THROW(Exception("File " + (*it_res).location.filename().asString() + " does not validate." ));
283        }
284       
285     } // for each job
286   }
287
288   /** \relates Fetcher::Impl Stream output */
289   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
290   {
291     return str << "Fetcher::Impl";
292   }
293
294   ///////////////////////////////////////////////////////////////////
295   //
296   //    CLASS NAME : Fetcher
297   //
298   ///////////////////////////////////////////////////////////////////
299
300   ///////////////////////////////////////////////////////////////////
301   //
302   //    METHOD NAME : Fetcher::Fetcher
303   //    METHOD TYPE : Ctor
304   //
305   Fetcher::Fetcher()
306   : _pimpl( Impl::nullimpl() )
307   {}
308
309   ///////////////////////////////////////////////////////////////////
310   //
311   //    METHOD NAME : Fetcher::~Fetcher
312   //    METHOD TYPE : Dtor
313   //
314   Fetcher::~Fetcher()
315   {}
316
317   void Fetcher::enqueueDigested( const OnMediaLocation &resource, const Fetcher::FileChecker &checker )
318   {
319     _pimpl->enqueue(resource, checker);
320   }
321   
322   void Fetcher::enqueue( const OnMediaLocation &resource, const Fetcher::FileChecker &checker  )
323   {
324     _pimpl->enqueue(resource, checker);
325   }
326   
327   void Fetcher::addCachePath( const Pathname &cache_dir )
328   {
329     _pimpl->addCachePath(cache_dir);
330   }
331   
332   void Fetcher::reset()
333   {
334     _pimpl->reset();
335   }
336
337   void Fetcher::start( const Pathname &dest_dir, MediaSetAccess &media )
338   {
339     _pimpl->start(dest_dir, media);
340   }
341
342
343   /******************************************************************
344   **
345   **    FUNCTION NAME : operator<<
346   **    FUNCTION TYPE : std::ostream &
347   */
348   std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
349   {
350     return str << *obj._pimpl;
351   }
352
353   /////////////////////////////////////////////////////////////////
354 } // namespace zypp
355 ///////////////////////////////////////////////////////////////////
356