revert commit don't check for existence of keys,
[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/Easy.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/PtrTypes.h"
18 #include "zypp/base/DefaultIntegral.h"
19 #include "zypp/Fetcher.h"
20 #include "zypp/base/UserRequestException.h"
21
22 using namespace std;
23
24 ///////////////////////////////////////////////////////////////////
25 namespace zypp
26 { /////////////////////////////////////////////////////////////////
27
28   /**
29    * Class to encapsulate the \ref OnMediaLocation object
30    * and the \ref FileChecker together
31    */
32   struct FetcherJob
33   {
34     FetcherJob( const OnMediaLocation &loc )
35       : location(loc)
36     {
37       //MIL << location << endl;
38     }
39
40     ~FetcherJob()
41     {
42       //MIL << location << " | * " << checkers.size() << endl;
43     }
44
45     OnMediaLocation location;
46     //CompositeFileChecker checkers;
47     list<FileChecker> checkers;
48   };
49
50   typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
51
52   ///////////////////////////////////////////////////////////////////
53   //
54   //    CLASS NAME : Fetcher::Impl
55   //
56   /** Fetcher implementation. */
57   class Fetcher::Impl
58   {
59
60   public:
61     Impl() {}
62     ~Impl() {
63       MIL << endl;
64      }
65     
66     void enqueue( const OnMediaLocation &resource, const FileChecker &checker  );
67     void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker );
68     void addCachePath( const Pathname &cache_dir );
69     void reset();
70     void start( const Pathname &dest_dir,
71                 MediaSetAccess &media,
72                 const ProgressData::ReceiverFnc & progress_receiver );
73
74     /** Offer default Impl. */
75     static shared_ptr<Impl> nullimpl()
76     {
77       static shared_ptr<Impl> _nullimpl( new Impl );
78       return _nullimpl;
79     }
80
81   private:
82     friend Impl * rwcowClone<Impl>( const Impl * rhs );
83     /** clone for RWCOW_pointer */
84     Impl * clone() const
85     { return new Impl( *this ); }
86
87     std::list<FetcherJob_Ptr> _resources;
88     std::list<Pathname> _caches;
89   };
90   ///////////////////////////////////////////////////////////////////
91
92   void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
93   {
94     FetcherJob_Ptr job;
95     job.reset(new FetcherJob(resource));
96     ChecksumFileChecker digest_check(resource.checksum());
97     job->checkers.push_back(digest_check);
98     if ( checker )
99       job->checkers.push_back(checker);
100     _resources.push_back(job);
101   }
102
103   void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
104   {
105     FetcherJob_Ptr job;
106     job.reset(new FetcherJob(resource));
107     if ( checker )
108       job->checkers.push_back(checker);
109     _resources.push_back(job);
110   }
111
112   void Fetcher::Impl::reset()
113   {
114     _resources.clear();
115   }
116
117   void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
118   {
119     PathInfo info(cache_dir);
120     if ( info.isExist() )
121     {
122       if ( info.isDir() )
123       {
124         DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
125         _caches.push_back(cache_dir);
126       }
127       else
128       {
129         // don't add bad cache directory, just log the error
130         ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
131       }
132     }
133     else
134     {
135         ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
136     }
137     
138   }
139
140   void Fetcher::Impl::start( const Pathname &dest_dir,
141                              MediaSetAccess &media,
142                              const ProgressData::ReceiverFnc & progress_receiver )
143   {
144     ProgressData progress(_resources.size());
145     progress.sendTo(progress_receiver);
146
147     for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
148     {
149       bool got_from_cache = false;
150       
151       MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
152       for_ ( it_cache, _caches.begin(), _caches.end() )
153       {
154         // does the current file exists in the current cache?
155         Pathname cached_file = *it_cache + (*it_res)->location.filename();
156
157         if ( PathInfo( cached_file ).isExist() )
158         {
159           DBG << "File '" << cached_file << "' exist, testing checksum " << (*it_res)->location.checksum() << endl;
160
161           // check the checksum
162           if ( is_checksum( cached_file, (*it_res)->location.checksum() ) && (! (*it_res)->location.checksum().empty() ) )
163           {
164             // cached
165             MIL << "file " << (*it_res)->location.filename() << " found in previous cache. Using cached copy." << endl;
166             // checksum is already checked.
167             // we could later implement double failover and try to download if file copy fails.
168
169             // replicate the complete path in the target directory
170             Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
171
172             if( dest_full_path != cached_file )
173             {
174               if ( assert_dir( dest_full_path.dirname() ) != 0 )
175                 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
176
177               if ( filesystem::hardlink(cached_file, dest_full_path ) != 0 )
178               {
179                 WAR << "Can't hardlink '" << cached_file << "' to '" << dest_dir << "'. Trying copying." << endl;
180                 if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
181                 {
182                   ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
183                   // try next cache
184                   continue;
185                 }
186               }
187             }
188
189             got_from_cache = true;
190             break;
191           }
192         }
193       }
194
195       if ( ! got_from_cache )
196       {
197         MIL << "Not found in cache, downloading" << endl;
198         
199         // try to get the file from the net
200         try
201         {
202           Pathname tmp_file = media.provideFile((*it_res)->location);
203           Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
204           if ( assert_dir( dest_full_path.dirname() ) != 0 )
205                 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
206           if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
207           {
208             ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
209           }
210
211           media.releaseFile((*it_res)->location); //not needed anymore, only eat space
212         }
213         catch (Exception & excpt_r)
214         {
215           ZYPP_CAUGHT(excpt_r);
216           excpt_r.remember("Can't provide " + (*it_res)->location.filename().asString() + " : " + excpt_r.msg());
217           ZYPP_RETHROW(excpt_r);
218         }
219       }
220       else
221       {
222         // We got the file from cache
223         // continue with next file
224         continue;
225       }
226
227       // no matter where did we got the file, try to validate it:
228       Pathname localfile = dest_dir + (*it_res)->location.filename();
229       // call the checker function
230       try {
231         MIL << "Checking job [" << localfile << "] (" << (*it_res)->checkers.size() << " checkers )" << endl;
232         for ( list<FileChecker>::const_iterator it = (*it_res)->checkers.begin();
233               it != (*it_res)->checkers.end();
234               ++it )
235         {
236           if (*it)
237           {
238             (*it)(localfile);
239           }
240           else
241           {
242             ERR << "Invalid checker for '" << localfile << "'" << endl;
243           }
244         }
245         
246       }
247       catch ( const FileCheckException &e )
248       {
249         ZYPP_RETHROW(e);
250       }
251       catch ( const Exception &e )
252       {
253         ZYPP_RETHROW(e);
254       }
255       catch (...)
256       {
257         ZYPP_THROW(Exception("Unknown error while validating " + (*it_res)->location.filename().asString()));
258       }
259
260       if ( ! progress.incr() )
261         ZYPP_THROW(AbortRequestException());
262     } // for each job
263   }
264
265   /** \relates Fetcher::Impl Stream output */
266   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
267   {
268     return str << "Fetcher::Impl";
269   }
270
271   ///////////////////////////////////////////////////////////////////
272   //
273   //    CLASS NAME : Fetcher
274   //
275   ///////////////////////////////////////////////////////////////////
276
277   ///////////////////////////////////////////////////////////////////
278   //
279   //    METHOD NAME : Fetcher::Fetcher
280   //    METHOD TYPE : Ctor
281   //
282   Fetcher::Fetcher()
283   : _pimpl( new Impl() )
284   {}
285
286   ///////////////////////////////////////////////////////////////////
287   //
288   //    METHOD NAME : Fetcher::~Fetcher
289   //    METHOD TYPE : Dtor
290   //
291   Fetcher::~Fetcher()
292   {}
293
294   void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
295   {
296     _pimpl->enqueueDigested(resource, checker);
297   }
298
299   void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker  )
300   {
301     _pimpl->enqueue(resource, checker);
302   }
303
304   void Fetcher::addCachePath( const Pathname &cache_dir )
305   {
306     _pimpl->addCachePath(cache_dir);
307   }
308
309   void Fetcher::reset()
310   {
311     _pimpl->reset();
312   }
313
314   void Fetcher::start( const Pathname &dest_dir,
315                        MediaSetAccess &media,
316                        const ProgressData::ReceiverFnc & progress_receiver )
317   {
318     _pimpl->start(dest_dir, media, progress_receiver);
319   }
320
321
322   /******************************************************************
323   **
324   **    FUNCTION NAME : operator<<
325   **    FUNCTION TYPE : std::ostream &
326   */
327   std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
328   {
329     return str << *obj._pimpl;
330   }
331
332   /////////////////////////////////////////////////////////////////
333 } // namespace zypp
334 ///////////////////////////////////////////////////////////////////
335