Merge branch 'aria'
[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 <fstream>
14 #include <list>
15 #include <map>
16
17 #include "zypp/base/Easy.h"
18 #include "zypp/base/LogControl.h"
19 #include "zypp/base/LogTools.h"
20 #include "zypp/base/PtrTypes.h"
21 #include "zypp/base/DefaultIntegral.h"
22 #include "zypp/base/String.h"
23 #include "zypp/Fetcher.h"
24 #include "zypp/ZYppFactory.h"
25 #include "zypp/CheckSum.h"
26 #include "zypp/base/UserRequestException.h"
27 #include "zypp/parser/susetags/ContentFileReader.h"
28 #include "zypp/parser/susetags/RepoIndex.h"
29
30 using namespace std;
31
32 #undef ZYPP_BASE_LOGGER_LOGGROUP
33 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp:fetcher"
34
35 ///////////////////////////////////////////////////////////////////
36 namespace zypp
37 { /////////////////////////////////////////////////////////////////
38
39   /**
40    * class that represents indexes which add metadata
41    * to fetcher jobs and therefore need to be retrieved
42    * in advance.
43    */
44   struct FetcherIndex
45   {
46     FetcherIndex( const OnMediaLocation &loc )
47       : location(loc)
48     {}
49     /** Index localtion. */
50     OnMediaLocation             location;
51     /** Whether we read this index. */
52     DefaultIntegral<bool,false> read;
53   };
54
55   typedef shared_ptr<FetcherIndex> FetcherIndex_Ptr;
56
57   /** std::set ordering (less semantic) */
58   struct SameFetcherIndex
59   {
60     bool operator()( const FetcherIndex_Ptr & lhs, const FetcherIndex_Ptr & rhs )
61     {
62       if ( lhs == rhs )
63         return false; // incl. NULL == NULL
64       if ( ! lhs )
65         return true;  // NULL < nonNULL
66       if ( ! rhs )
67         return false; // nonNULL > NULL
68       // both nonNULL ==> compare medianr and path
69       if ( lhs->location.medianr() == rhs->location.medianr() )
70         return lhs->location.filename() < rhs->location.filename();
71       //else
72         return lhs->location.medianr() < rhs->location.medianr();
73     }
74   };
75
76   /**
77    * Class to encapsulate the \ref OnMediaLocation object
78    * and the \ref FileChecker together
79    */
80   struct FetcherJob
81   {
82     enum Flag
83     {
84         None      = 0x0000,
85         Directory = 0x0001,
86         Recursive = 0x0002,
87         RecursiveDirectory = Directory | Recursive,
88         // check checksums even if there is no such
89         // checksum (warns of no checksum)
90         AlwaysVerifyChecksum = 0x0004,
91     };
92     ZYPP_DECLARE_FLAGS(Flags, Flag);
93
94
95     FetcherJob( const OnMediaLocation &loc )
96       : location(loc)
97       , flags(None)
98     {
99       //MIL << location << endl;
100     }
101
102     ~FetcherJob()
103     {
104       //MIL << location << " | * " << checkers.size() << endl;
105     }
106
107     OnMediaLocation location;
108     //CompositeFileChecker checkers;
109     list<FileChecker> checkers;
110     Flags flags;
111   };
112
113   ZYPP_DECLARE_OPERATORS_FOR_FLAGS(FetcherJob::Flags);
114   typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
115
116   std::ostream & operator<<( std::ostream & str, const FetcherJob_Ptr & obj )
117   {
118     return str << obj->location;
119   }
120
121   ///////////////////////////////////////////////////////////////////
122   //
123   //    CLASS NAME : Fetcher::Impl
124   //
125   /** Fetcher implementation. */
126   class Fetcher::Impl
127   {
128     friend std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj );
129
130   public:
131     Impl();
132
133     ~Impl() {
134       MIL << endl;
135     }
136
137     void setOptions( Fetcher::Options options );
138     Fetcher::Options options() const;
139
140     void addIndex( const OnMediaLocation &resource );
141
142     void enqueueDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker = FileChecker() );
143     void enqueueDigestedDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker = FileChecker() );
144
145     void enqueue( const OnMediaLocation &resource, const FileChecker &checker = FileChecker()  );
146     void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker = FileChecker() );
147     void addCachePath( const Pathname &cache_dir );
148     void reset();
149     void start( const Pathname &dest_dir,
150                 MediaSetAccess &media,
151                 const ProgressData::ReceiverFnc & progress_receiver );
152
153     /** Offer default Impl. */
154     static shared_ptr<Impl> nullimpl()
155     {
156       static shared_ptr<Impl> _nullimpl( new Impl );
157       return _nullimpl;
158     }
159   private:
160       /**
161        * download the indexes and reads them
162        */
163       void downloadAndReadIndexList( MediaSetAccess &media, const Pathname &dest_dir);
164
165       /**
166        * download the indexes and reads them
167        */
168       void downloadIndex( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir);
169
170       /**
171        * reads a downloaded index file and updates internal
172        * attributes table
173        *
174        * The index lists files relative to a directory, which is
175        * normally the same as the index file is located.
176        */
177       void readIndex( const Pathname &index, const Pathname &basedir );
178
179       /** specific version of \ref readIndex for SHA1SUMS file */
180       void readSha1sumsIndex( const Pathname &index, const Pathname &basedir );
181
182       /** specific version of \ref readIndex for SHA1SUMS file */
183       void readContentFileIndex( const Pathname &index, const Pathname &basedir );
184
185       /** reads the content of a directory but keeps a cache **/
186       void getDirectoryContent( MediaSetAccess &media, const OnMediaLocation &resource, filesystem::DirContent &content );
187
188       /**
189        * tries to provide the file represented by job into dest_dir by
190        * looking at the cache. If success, returns true, and the desired
191        * file should be available on dest_dir
192        */
193       bool provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir );
194       /**
195        * Validates the job against is checkers, by using the file instance
196        * on dest_dir
197        * \throws Exception
198        */
199       void validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers );
200
201       /**
202        * scan the directory and adds the individual jobs
203        */
204        void addDirJobs( MediaSetAccess &media, const OnMediaLocation &resource,
205                         const Pathname &dest_dir, FetcherJob::Flags flags );
206
207       /**
208        * auto discovery and reading of indexes
209        */
210       void autoaddIndexes( const filesystem::DirContent &content,
211                            MediaSetAccess &media,
212                            const OnMediaLocation &resource,
213                            const Pathname &dest_dir );
214       /**
215        * Provide the resource to \ref dest_dir
216        */
217       void provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir );
218
219   private:
220     friend Impl * rwcowClone<Impl>( const Impl * rhs );
221     /** clone for RWCOW_pointer */
222     Impl * clone() const
223     { return new Impl( *this ); }
224
225     list<FetcherJob_Ptr>   _resources;
226     std::set<FetcherIndex_Ptr,SameFetcherIndex> _indexes;
227     std::set<Pathname> _caches;
228     // checksums read from the indexes
229     map<string, CheckSum> _checksums;
230     // cache of dir contents
231     map<string, filesystem::DirContent> _dircontent;
232
233     Fetcher::Options _options;
234   };
235   ///////////////////////////////////////////////////////////////////
236
237   void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
238   {
239     FetcherJob_Ptr job;
240     job.reset(new FetcherJob(resource));
241     job->flags |= FetcherJob:: AlwaysVerifyChecksum;
242     _resources.push_back(job);
243   }
244
245   Fetcher::Impl::Impl()
246       : _options(0)
247   {
248   }
249
250   void Fetcher::Impl::setOptions( Fetcher::Options options )
251   { _options = options; }
252
253   Fetcher::Options Fetcher::Impl::options() const
254   { return _options; }
255
256   void Fetcher::Impl::enqueueDir( const OnMediaLocation &resource,
257                                   bool recursive,
258                                   const FileChecker &checker )
259   {
260     FetcherJob_Ptr job;
261     job.reset(new FetcherJob(resource));
262     if ( checker )
263         job->checkers.push_back(checker);
264     if ( recursive )
265         job->flags |= FetcherJob::Recursive;
266     job->flags |= FetcherJob::Directory;
267
268     _resources.push_back(job);
269   }
270
271   void Fetcher::Impl::enqueueDigestedDir( const OnMediaLocation &resource,
272                                           bool recursive,
273                                           const FileChecker &checker )
274   {
275     FetcherJob_Ptr job;
276     job.reset(new FetcherJob(resource));
277     if ( checker )
278         job->checkers.push_back(checker);
279     if ( recursive )
280         job->flags |= FetcherJob::Recursive;
281     job->flags |= FetcherJob::Directory;
282     job->flags |= FetcherJob::AlwaysVerifyChecksum;
283
284     _resources.push_back(job);
285
286   }
287
288   void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
289   {
290     FetcherJob_Ptr job;
291     job.reset(new FetcherJob(resource));
292     if ( checker )
293       job->checkers.push_back(checker);
294     _resources.push_back(job);
295   }
296
297   void Fetcher::Impl::addIndex( const OnMediaLocation &resource )
298   {
299     MIL << "adding index " << resource << endl;
300     _indexes.insert(FetcherIndex_Ptr(new FetcherIndex(resource)));
301   }
302
303
304   void Fetcher::Impl::reset()
305   {
306     _resources.clear();
307     _indexes.clear();
308     _checksums.clear();
309     _dircontent.clear();
310   }
311
312   void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
313   {
314     PathInfo info(cache_dir);
315     if ( info.isExist() )
316     {
317       if ( info.isDir() )
318       {
319         DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
320         _caches.insert(cache_dir);
321       }
322       else
323       {
324         // don't add bad cache directory, just log the error
325         ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
326       }
327     }
328     else
329     {
330         ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
331     }
332
333   }
334
335   // tries to provide resource to dest_dir from any of the configured additional
336   // cache paths where the file may already be present. returns true if the
337   // file was provided from the cache.
338   bool Fetcher::Impl::provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir )
339   {
340     Pathname dest_full_path = dest_dir + resource.filename();
341
342     // first check in the destination directory
343     if ( PathInfo(dest_full_path).isExist() )
344     {
345       if ( is_checksum( dest_full_path, resource.checksum() )
346            && (! resource.checksum().empty() ) )
347           return true;
348     }
349
350     MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
351     for_ ( it_cache, _caches.begin(), _caches.end() )
352     {
353       // does the current file exists in the current cache?
354       Pathname cached_file = *it_cache + resource.filename();
355       if ( PathInfo( cached_file ).isExist() )
356       {
357         DBG << "File '" << cached_file << "' exist, testing checksum " << resource.checksum() << endl;
358          // check the checksum
359         if ( is_checksum( cached_file, resource.checksum() ) && (! resource.checksum().empty() ) )
360         {
361           // cached
362           MIL << "file " << resource.filename() << " found in previous cache. Using cached copy." << endl;
363           // checksum is already checked.
364           // we could later implement double failover and try to download if file copy fails.
365            // replicate the complete path in the target directory
366           if( dest_full_path != cached_file )
367           {
368             if ( assert_dir( dest_full_path.dirname() ) != 0 )
369               ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
370
371             if ( filesystem::hardlink(cached_file, dest_full_path ) != 0 )
372             {
373               WAR << "Can't hardlink '" << cached_file << "' to '" << dest_dir << "'. Trying copying." << endl;
374               if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
375               {
376                 ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
377                 // try next cache
378                 continue;
379               }
380             }
381           }
382           // found in cache
383           return true;
384         }
385       }
386     } // iterate over caches
387     return false;
388   }
389
390     void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
391   {
392     // no matter where did we got the file, try to validate it:
393     Pathname localfile = dest_dir + resource.filename();
394     // call the checker function
395     try
396     {
397       MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
398
399       for ( list<FileChecker>::const_iterator it = checkers.begin();
400             it != checkers.end();
401             ++it )
402       {
403         if (*it)
404         {
405           (*it)(localfile);
406         }
407         else
408         {
409           ERR << "Invalid checker for '" << localfile << "'" << endl;
410         }
411       }
412
413     }
414     catch ( const FileCheckException &e )
415     {
416       ZYPP_RETHROW(e);
417     }
418     catch ( const Exception &e )
419     {
420       ZYPP_RETHROW(e);
421     }
422     catch (...)
423     {
424       ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
425     }
426   }
427
428   void Fetcher::Impl::autoaddIndexes( const filesystem::DirContent &content,
429                                       MediaSetAccess &media,
430                                       const OnMediaLocation &resource,
431                                       const Pathname &dest_dir )
432   {
433       if ( _options & AutoAddSha1sumsIndexes )
434       {
435           // only try to add an index if it exists
436           filesystem::DirEntry shafile;
437           shafile.name = "SHA1SUMS"; shafile.type = filesystem::FT_FILE;
438           if ( find( content.begin(), content.end(), shafile ) != content.end() )
439           {
440               // add the index of this directory
441               OnMediaLocation indexloc(resource);
442               indexloc.changeFilename(resource.filename() + "SHA1SUMS");
443               addIndex(indexloc);
444               // we need to read it now
445               downloadAndReadIndexList(media, dest_dir);
446           }
447       }
448       if ( _options & AutoAddContentFileIndexes )
449       {
450           // only try to add an index if it exists
451           filesystem::DirEntry contentfile;
452           contentfile.name = "content"; contentfile.type = filesystem::FT_FILE;
453           if ( find( content.begin(), content.end(), contentfile ) != content.end() )
454           {
455               // add the index of this directory
456               OnMediaLocation indexloc(resource);
457               indexloc.changeFilename(resource.filename() + "content");
458               addIndex(indexloc);
459               // we need to read it now
460               downloadAndReadIndexList(media, dest_dir);
461           }
462       }
463   }
464
465   void Fetcher::Impl::getDirectoryContent( MediaSetAccess &media,
466                                            const OnMediaLocation &resource,
467                                            filesystem::DirContent &content )
468   {
469       if ( _dircontent.find(resource.filename().asString())
470            != _dircontent.end() )
471       {
472           filesystem::DirContent filled(_dircontent[resource.filename().asString()]);
473
474           std::copy(filled.begin(), filled.end(), std::back_inserter(content));
475       }
476       else
477       {
478           filesystem::DirContent tofill;
479           media.dirInfo( tofill,
480                          resource.filename(),
481                          false /* dots */,
482                          resource.medianr());
483           std::copy(tofill.begin(), tofill.end(), std::back_inserter(content));
484           _dircontent[resource.filename().asString()] = tofill;
485       }
486   }
487
488   void Fetcher::Impl::addDirJobs( MediaSetAccess &media,
489                                   const OnMediaLocation &resource,
490                                   const Pathname &dest_dir, FetcherJob::Flags flags  )
491   {
492       // first get the content of the directory so we can add
493       // individual transfer jobs
494       MIL << "Adding directory " << resource.filename() << endl;
495       filesystem::DirContent content;
496       getDirectoryContent(media, resource, content);
497
498       // this method test for the option flags so indexes are added
499       // only if the options are enabled
500       autoaddIndexes(content, media, resource, dest_dir);
501
502       for ( filesystem::DirContent::const_iterator it = content.begin();
503             it != content.end();
504             ++it )
505       {
506           // skip SHA1SUMS* as they were already retrieved
507           if ( str::hasPrefix(it->name, "SHA1SUMS") )
508               continue;
509
510           Pathname filename = resource.filename() + it->name;
511
512           switch ( it->type )
513           {
514           case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
515           case filesystem::FT_FILE:
516           {
517               CheckSum chksm(resource.checksum());
518               if ( _checksums.find(filename.asString()) != _checksums.end() )
519               {
520                   // the checksum can be replaced with the one in the index.
521                   chksm = _checksums[filename.asString()];
522                   //MIL << "resource " << filename << " has checksum in the index file." << endl;
523               }
524               else
525                   WAR << "Resource " << filename << " has no checksum in the index either." << endl;
526
527               if ( flags & FetcherJob::AlwaysVerifyChecksum )
528                   enqueueDigested(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
529               else
530                   enqueue(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
531               break;
532           }
533           case filesystem::FT_DIR: // newer directory.yast contain at least directory info
534               if ( flags & FetcherJob::Recursive )
535                   addDirJobs(media, filename, dest_dir, flags);
536               break;
537           default:
538               // don't provide devices, sockets, etc.
539               break;
540           }
541       }
542   }
543
544   void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir )
545   {
546     bool got_from_cache = false;
547
548     // start look in cache
549     got_from_cache = provideFromCache(resource, dest_dir);
550
551     if ( ! got_from_cache )
552     {
553       MIL << "Not found in cache, downloading" << endl;
554
555       // try to get the file from the net
556       try
557       {
558         Pathname tmp_file = media.provideFile(resource);
559
560         Pathname dest_full_path = dest_dir + resource.filename();
561
562         if ( assert_dir( dest_full_path.dirname() ) != 0 )
563               ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
564         if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
565         {
566           if ( ! PathInfo(tmp_file).isExist() )
567               ERR << tmp_file << " does not exist" << endl;
568           if ( ! PathInfo(dest_full_path.dirname()).isExist() )
569               ERR << dest_full_path.dirname() << " does not exist" << endl;
570
571           media.releaseFile(resource); //not needed anymore, only eat space
572           ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
573         }
574
575         media.releaseFile(resource); //not needed anymore, only eat space
576       }
577       catch (Exception & excpt_r)
578       {
579         ZYPP_CAUGHT(excpt_r);
580         excpt_r.remember("Can't provide " + resource.filename().asString() + " : " + excpt_r.msg());
581
582         if ( resource.optional() )
583         {
584             WAR << "optional resource " << resource << " could not be transfered" << endl;
585             return;
586         }
587         else
588         {
589             ZYPP_RETHROW(excpt_r);
590         }
591       }
592     }
593     else
594     {
595       // We got the file from cache
596       // continue with next file
597         return;
598     }
599   }
600
601   // helper class to consume a content file
602   struct ContentReaderHelper : public parser::susetags::ContentFileReader
603   {
604     ContentReaderHelper()
605     {
606       setRepoIndexConsumer( bind( &ContentReaderHelper::consumeIndex, this, _1 ) );
607     }
608
609     void consumeIndex( const parser::susetags::RepoIndex_Ptr & data_r )
610     { _repoindex = data_r; }
611
612     parser::susetags::RepoIndex_Ptr _repoindex;
613   };
614
615   // generic function for reading indexes
616   void Fetcher::Impl::readIndex( const Pathname &index, const Pathname &basedir )
617   {
618     if ( index.basename() == "SHA1SUMS" )
619       readSha1sumsIndex(index, basedir);
620     else if ( index.basename() == "content" )
621       readContentFileIndex(index, basedir);
622     else
623       WAR << index << ": index file format not known" << endl;
624   }
625
626   // reads a content file index
627   void Fetcher::Impl::readContentFileIndex( const Pathname &index, const Pathname &basedir )
628   {
629       ContentReaderHelper reader;
630       reader.parse(index);
631       MIL << index << " contains " << reader._repoindex->mediaFileChecksums.size() << " checksums." << endl;
632       for_( it, reader._repoindex->mediaFileChecksums.begin(), reader._repoindex->mediaFileChecksums.end() )
633       {
634           // content file entries don't start with /
635           _checksums[(basedir + it->first).asString()] = it->second;
636       }
637   }
638
639   // reads a SHA1SUMS file index
640   void Fetcher::Impl::readSha1sumsIndex( const Pathname &index, const Pathname &basedir )
641   {
642       std::ifstream in( index.c_str() );
643       string buffer;
644       if ( ! in.fail() )
645       {
646           while ( getline(in, buffer) )
647           {
648               vector<string> words;
649               str::split( buffer, back_inserter(words) );
650               if ( words.size() != 2 )
651                   ZYPP_THROW(Exception("Wrong format for SHA1SUMS file"));
652               //MIL << "check: '" << words[0] << "' | '" << words[1] << "'" << endl;
653               if ( ! words[1].empty() )
654                   _checksums[(basedir + words[1]).asString()] = CheckSum::sha1(words[0]);
655           }
656       }
657       else
658           ZYPP_THROW(Exception("Can't open SHA1SUMS file: " + index.asString()));
659   }
660
661   void Fetcher::Impl::downloadIndex( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir)
662   {
663     MIL << "downloading index " << resource << endl;
664     // create a new fetcher with a different state to transfer the
665     // file containing checksums and its signature
666     Fetcher fetcher;
667     // signature checker for index. We havent got the signature from
668     // the nextwork yet.
669     SignatureFileChecker sigchecker;
670
671     // build the name of the index and the signature
672     OnMediaLocation idxloc(resource);
673     OnMediaLocation sigloc(resource);
674     OnMediaLocation keyloc(resource);
675
676     // we should not fail the download if those don't exists
677     // the checking will warn later
678     sigloc.setOptional(true);
679     keyloc.setOptional(true);
680
681     // calculate signature and key name
682     sigloc.changeFilename( sigloc.filename().extend(".asc") );
683     keyloc.changeFilename( keyloc.filename().extend(".key") );
684
685     //assert_dir(dest_dir + idxloc.filename().dirname());
686
687     // transfer the signature
688     fetcher.enqueue(sigloc);
689     fetcher.start( dest_dir, media );
690     // if we get the signature, update the checker
691     if ( PathInfo(dest_dir + sigloc.filename()).isExist() )
692         sigchecker = SignatureFileChecker(dest_dir + sigloc.filename());
693
694     fetcher.reset();
695
696     // now the key
697     fetcher.enqueue(keyloc);
698     fetcher.start( dest_dir, media );
699     fetcher.reset();
700
701     // try to import the key
702     if ( PathInfo(dest_dir + keyloc.filename()).isExist() )
703         getZYpp()->keyRing()->importKey(PublicKey(dest_dir + keyloc.filename()), false);
704     else
705         WAR << "No public key specified by user for index '" << keyloc.filename() << "'"<< endl;
706     
707     // now the index itself
708     fetcher.enqueue( idxloc, FileChecker(sigchecker) );
709     fetcher.start( dest_dir, media );
710     fetcher.reset();
711  }
712
713   // this method takes all the user pointed indexes, gets them and also tries to
714   // download their signature, and verify them. After that, its parses each one
715   // to fill the checksum cache.
716   void Fetcher::Impl::downloadAndReadIndexList( MediaSetAccess &media, const Pathname &dest_dir)
717   {
718       // if there is no indexes, then just return to avoid
719       // the directory listing
720       if ( _indexes.empty() )
721       {
722           MIL << "No indexes to read." << endl;
723           return;
724       }
725
726       for_( it_idx, _indexes.begin(), _indexes.end() )
727       {
728         if ( (*it_idx)->read )
729         {
730           DBG << "Already read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
731         }
732         else
733         {
734           // base::LogControl::TmpLineWriter shutUp;
735           downloadIndex( media, (*it_idx)->location, dest_dir );
736           // now we have the indexes in dest_dir
737           readIndex( dest_dir + (*it_idx)->location.filename(), (*it_idx)->location.filename().dirname() );
738           // Take care we don't process it again
739           MIL << "Remember read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
740           (*it_idx)->read = true;
741         }
742       }
743       MIL << "done reading indexes" << endl;
744   }
745
746   // start processing all fetcher jobs.
747   // it processes any user pointed index first
748   void Fetcher::Impl::start( const Pathname &dest_dir,
749                              MediaSetAccess &media,
750                              const ProgressData::ReceiverFnc & progress_receiver )
751   {
752     ProgressData progress(_resources.size());
753     progress.sendTo(progress_receiver);
754
755     downloadAndReadIndexList(media, dest_dir);
756
757     for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
758     {
759
760       if ( (*it_res)->flags & FetcherJob::Directory )
761       {
762           const OnMediaLocation location((*it_res)->location);
763           addDirJobs(media, location, dest_dir, (*it_res)->flags);
764           continue;
765       }
766
767       // may be this code can be factored out
768       // together with the autodiscovery of indexes
769       // of addDirJobs
770       if ( ( _options & AutoAddSha1sumsIndexes ) ||
771            ( _options & AutoAddContentFileIndexes ) )
772       {
773           // if auto indexing is enabled, then we need to read the
774           // index for each file. We look only in the directory
775           // where the file is. this is expensive of course.
776           filesystem::DirContent content;
777           getDirectoryContent(media, (*it_res)->location.filename().dirname(), content);
778           // this method test for the option flags so indexes are added
779           // only if the options are enabled
780           MIL << "Autodiscovering signed indexes on '"
781               << (*it_res)->location.filename().dirname() << "' for '"
782               << (*it_res)->location.filename() << "'" << endl;
783
784           autoaddIndexes(content, media, (*it_res)->location.filename().dirname(), dest_dir);
785
786           // also look in the root of the media
787           content.clear();
788           getDirectoryContent(media, Pathname("/"), content);
789           // this method test for the option flags so indexes are added
790           // only if the options are enabled
791           MIL << "Autodiscovering signed indexes on '"
792               << "/" << "' for '"
793               << (*it_res)->location.filename() << "'" << endl;
794
795           autoaddIndexes(content, media, Pathname("/"), dest_dir);
796       }
797
798       provideToDest(media, (*it_res)->location, dest_dir);
799
800       // if the file was not transfered, and no exception, just
801       // return, as it was an optional file
802       if ( ! PathInfo(dest_dir + (*it_res)->location.filename()).isExist() )
803           return;
804
805       // if the checksum is empty, but the checksum is in one of the
806       // indexes checksum, then add a checker
807       if ( (*it_res)->location.checksum().empty() )
808       {
809           if ( _checksums.find((*it_res)->location.filename().asString())
810                != _checksums.end() )
811           {
812               CheckSum chksm = _checksums[(*it_res)->location.filename().asString()];
813               ChecksumFileChecker digest_check(chksm);
814               (*it_res)->checkers.push_back(digest_check);
815           }
816           else
817           {
818               // if the index checksum is empty too, we only add the checker
819               // if the  AlwaysVerifyChecksum option is set on
820               if ( (*it_res)->flags & FetcherJob::AlwaysVerifyChecksum )
821               {
822                   // add the checker with the empty checksum
823                   ChecksumFileChecker digest_check((*it_res)->location.checksum());
824                   (*it_res)->checkers.push_back(digest_check);
825               }
826           }
827       }
828       else
829       {
830           // checksum is not empty, so add a checksum checker
831           ChecksumFileChecker digest_check((*it_res)->location.checksum());
832           (*it_res)->checkers.push_back(digest_check);
833       }
834
835       // validate job, this throws if not valid
836       validate((*it_res)->location, dest_dir, (*it_res)->checkers);
837
838       if ( ! progress.incr() )
839         ZYPP_THROW(AbortRequestException());
840     } // for each job
841   }
842
843   /** \relates Fetcher::Impl Stream output */
844   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
845   {
846       for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
847       {
848           str << *it_res;
849       }
850       return str;
851   }
852
853   Fetcher::Fetcher()
854   : _pimpl( new Impl() )
855   {}
856
857   Fetcher::~Fetcher()
858   {}
859
860   void Fetcher::setOptions( Fetcher::Options options )
861   {
862     _pimpl->setOptions(options);
863   }
864
865   Fetcher::Options Fetcher::options() const
866   {
867     return _pimpl->options();
868   }
869
870   void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
871   {
872     _pimpl->enqueueDigested(resource, checker);
873   }
874
875   void Fetcher::enqueueDir( const OnMediaLocation &resource,
876                             bool recursive,
877                             const FileChecker &checker )
878   {
879       _pimpl->enqueueDir(resource, recursive, checker);
880   }
881
882   void Fetcher::enqueueDigestedDir( const OnMediaLocation &resource,
883                                     bool recursive,
884                                     const FileChecker &checker )
885   {
886       _pimpl->enqueueDigestedDir(resource, recursive, checker);
887   }
888
889
890   void Fetcher::addIndex( const OnMediaLocation &resource )
891   {
892     _pimpl->addIndex(resource);
893   }
894
895
896   void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker  )
897   {
898     _pimpl->enqueue(resource, checker);
899   }
900
901   void Fetcher::addCachePath( const Pathname &cache_dir )
902   {
903     _pimpl->addCachePath(cache_dir);
904   }
905
906   void Fetcher::reset()
907   {
908     _pimpl->reset();
909   }
910
911   void Fetcher::start( const Pathname &dest_dir,
912                        MediaSetAccess &media,
913                        const ProgressData::ReceiverFnc & progress_receiver )
914   {
915     _pimpl->start(dest_dir, media, progress_receiver);
916   }
917
918   std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
919   {
920     return str << *obj._pimpl;
921   }
922
923   /////////////////////////////////////////////////////////////////
924 } // namespace zypp
925 ///////////////////////////////////////////////////////////////////
926