- Fixes for the checksum/signature subsystem (#302059)
[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         if ( PathInfo( cached_file ).isExist() )
143         {
144           // check the checksum
145           if ( is_checksum( cached_file, (*it_res)->location.checksum() ) && (! (*it_res)->location.checksum().empty() ) )
146           {
147             // cached
148             MIL << "file " << (*it_res)->location.filename() << " found in previous cache. Using cached copy." << endl;
149             // checksum is already checked.
150             // we could later implement double failover and try to download if file copy fails.
151
152             // replicate the complete path in the target directory
153             Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
154             if ( assert_dir( dest_full_path.dirname() ) != 0 )
155               ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
156
157             if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
158             { //copy_file2dir
159               //ZYPP_THROW(SourceIOException("Can't copy " + cached_file.asString() + " to " + destination.asString()));
160               ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
161               // try next cache
162               continue;
163             }
164
165             got_from_cache = true;
166             break;
167           }
168         }
169       }
170
171       if ( ! got_from_cache )
172       {
173         // try to get the file from the net
174         try
175         {
176           Pathname tmp_file = media.provideFile((*it_res)->location);
177           Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
178           if ( assert_dir( dest_full_path.dirname() ) != 0 )
179                 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
180           if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
181           {
182             ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
183           }
184
185
186         }
187         catch (const Exception & excpt_r)
188         {
189           ZYPP_CAUGHT(excpt_r);
190           Exception nexcpt("Can't provide " + (*it_res)->location.filename().asString() + " : " + excpt_r.msg());
191           nexcpt.remember(excpt_r);
192           ZYPP_THROW(nexcpt);
193         }
194       }
195       else
196       {
197         // We got the file from cache
198         // continue with next file
199         continue;
200       }
201
202       // no matter where did we got the file, try to validate it:
203       Pathname localfile = dest_dir + (*it_res)->location.filename();
204       // call the checker function
205       try {
206         MIL << "Checking job [" << localfile << "] (" << (*it_res)->checkers.size() << " checkers )" << endl;
207         for ( list<FileChecker>::const_iterator it = (*it_res)->checkers.begin();
208               it != (*it_res)->checkers.end();
209               ++it )
210         {
211           if (*it)
212           {
213             (*it)(localfile);
214           }
215           else
216           {
217             ERR << "Invalid checker for '" << localfile << "'" << endl;
218           }
219         }
220         
221       }
222       catch ( const FileCheckException &e )
223       {
224         ZYPP_RETHROW(e);
225       }
226       catch ( const Exception &e )
227       {
228         ZYPP_RETHROW(e);
229       }
230       catch (...)
231       {
232         ZYPP_THROW(Exception("Unknown error while validating " + (*it_res)->location.filename().asString()));
233       }
234
235       if ( ! progress.incr() )
236         ZYPP_THROW(AbortRequestException());
237     } // for each job
238   }
239
240   /** \relates Fetcher::Impl Stream output */
241   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
242   {
243     return str << "Fetcher::Impl";
244   }
245
246   ///////////////////////////////////////////////////////////////////
247   //
248   //    CLASS NAME : Fetcher
249   //
250   ///////////////////////////////////////////////////////////////////
251
252   ///////////////////////////////////////////////////////////////////
253   //
254   //    METHOD NAME : Fetcher::Fetcher
255   //    METHOD TYPE : Ctor
256   //
257   Fetcher::Fetcher()
258   : _pimpl( Impl::nullimpl() )
259   {}
260
261   ///////////////////////////////////////////////////////////////////
262   //
263   //    METHOD NAME : Fetcher::~Fetcher
264   //    METHOD TYPE : Dtor
265   //
266   Fetcher::~Fetcher()
267   {}
268
269   void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
270   {
271     _pimpl->enqueueDigested(resource, checker);
272   }
273
274   void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker  )
275   {
276     _pimpl->enqueue(resource, checker);
277   }
278
279   void Fetcher::addCachePath( const Pathname &cache_dir )
280   {
281     _pimpl->addCachePath(cache_dir);
282   }
283
284   void Fetcher::reset()
285   {
286     _pimpl->reset();
287   }
288
289   void Fetcher::start( const Pathname &dest_dir,
290                        MediaSetAccess &media,
291                        const ProgressData::ReceiverFnc & progress_receiver )
292   {
293     _pimpl->start(dest_dir, media, progress_receiver);
294   }
295
296
297   /******************************************************************
298   **
299   **    FUNCTION NAME : operator<<
300   **    FUNCTION TYPE : std::ostream &
301   */
302   std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
303   {
304     return str << *obj._pimpl;
305   }
306
307   /////////////////////////////////////////////////////////////////
308 } // namespace zypp
309 ///////////////////////////////////////////////////////////////////
310