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