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