Enhance exception histories
[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, const Pathname dfile = Pathname())
96       : location(loc)
97       , deltafile(dfile)
98       , flags(None)
99     {
100       //MIL << location << endl;
101     }
102
103     ~FetcherJob()
104     {
105       //MIL << location << " | * " << checkers.size() << endl;
106     }
107
108     OnMediaLocation location;
109     Pathname deltafile;
110     //CompositeFileChecker checkers;
111     list<FileChecker> checkers;
112     Flags flags;
113   };
114
115   ZYPP_DECLARE_OPERATORS_FOR_FLAGS(FetcherJob::Flags);
116   typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
117
118   std::ostream & operator<<( std::ostream & str, const FetcherJob_Ptr & obj )
119   {
120     return str << obj->location;
121   }
122
123   ///////////////////////////////////////////////////////////////////
124   //
125   //    CLASS NAME : Fetcher::Impl
126   //
127   /** Fetcher implementation. */
128   class Fetcher::Impl
129   {
130     friend std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj );
131
132   public:
133     Impl();
134
135     ~Impl() {}
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(), const Pathname &deltafile = Pathname() );
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 , const Pathname &deltafile);
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, const Pathname &deltafile )
238   {
239     FetcherJob_Ptr job;
240     job.reset(new FetcherJob(resource, deltafile));
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::hardlinkCopy(cached_file, dest_full_path ) != 0 )
372             {
373               ERR << "Can't hardlink/copy " << cached_file + " to " + dest_dir << endl;
374               continue;
375             }
376           }
377           // found in cache
378           return true;
379         }
380       }
381     } // iterate over caches
382     return false;
383   }
384
385     void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
386   {
387     // no matter where did we got the file, try to validate it:
388     Pathname localfile = dest_dir + resource.filename();
389     // call the checker function
390     try
391     {
392       MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
393
394       for ( list<FileChecker>::const_iterator it = checkers.begin();
395             it != checkers.end();
396             ++it )
397       {
398         if (*it)
399         {
400           (*it)(localfile);
401         }
402         else
403         {
404           ERR << "Invalid checker for '" << localfile << "'" << endl;
405         }
406       }
407
408     }
409     catch ( const FileCheckException &e )
410     {
411       ZYPP_RETHROW(e);
412     }
413     catch ( const Exception &e )
414     {
415       ZYPP_RETHROW(e);
416     }
417     catch (...)
418     {
419       ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
420     }
421   }
422
423   void Fetcher::Impl::autoaddIndexes( const filesystem::DirContent &content,
424                                       MediaSetAccess &media,
425                                       const OnMediaLocation &resource,
426                                       const Pathname &dest_dir )
427   {
428       if ( _options & AutoAddSha1sumsIndexes )
429       {
430           // only try to add an index if it exists
431           filesystem::DirEntry shafile;
432           shafile.name = "SHA1SUMS"; shafile.type = filesystem::FT_FILE;
433           if ( find( content.begin(), content.end(), shafile ) != content.end() )
434           {
435               // add the index of this directory
436               OnMediaLocation indexloc(resource);
437               indexloc.changeFilename(resource.filename() + "SHA1SUMS");
438               addIndex(indexloc);
439               // we need to read it now
440               downloadAndReadIndexList(media, dest_dir);
441           }
442       }
443       if ( _options & AutoAddContentFileIndexes )
444       {
445           // only try to add an index if it exists
446           filesystem::DirEntry contentfile;
447           contentfile.name = "content"; contentfile.type = filesystem::FT_FILE;
448           if ( find( content.begin(), content.end(), contentfile ) != content.end() )
449           {
450               // add the index of this directory
451               OnMediaLocation indexloc(resource);
452               indexloc.changeFilename(resource.filename() + "content");
453               addIndex(indexloc);
454               // we need to read it now
455               downloadAndReadIndexList(media, dest_dir);
456           }
457       }
458   }
459
460   void Fetcher::Impl::getDirectoryContent( MediaSetAccess &media,
461                                            const OnMediaLocation &resource,
462                                            filesystem::DirContent &content )
463   {
464       if ( _dircontent.find(resource.filename().asString())
465            != _dircontent.end() )
466       {
467           filesystem::DirContent filled(_dircontent[resource.filename().asString()]);
468
469           std::copy(filled.begin(), filled.end(), std::back_inserter(content));
470       }
471       else
472       {
473           filesystem::DirContent tofill;
474           media.dirInfo( tofill,
475                          resource.filename(),
476                          false /* dots */,
477                          resource.medianr());
478           std::copy(tofill.begin(), tofill.end(), std::back_inserter(content));
479           _dircontent[resource.filename().asString()] = tofill;
480       }
481   }
482
483   void Fetcher::Impl::addDirJobs( MediaSetAccess &media,
484                                   const OnMediaLocation &resource,
485                                   const Pathname &dest_dir, FetcherJob::Flags flags  )
486   {
487       // first get the content of the directory so we can add
488       // individual transfer jobs
489       MIL << "Adding directory " << resource.filename() << endl;
490       filesystem::DirContent content;
491       getDirectoryContent(media, resource, content);
492
493       // this method test for the option flags so indexes are added
494       // only if the options are enabled
495       autoaddIndexes(content, media, resource, dest_dir);
496
497       for ( filesystem::DirContent::const_iterator it = content.begin();
498             it != content.end();
499             ++it )
500       {
501           // skip SHA1SUMS* as they were already retrieved
502           if ( str::hasPrefix(it->name, "SHA1SUMS") )
503               continue;
504
505           Pathname filename = resource.filename() + it->name;
506
507           switch ( it->type )
508           {
509           case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
510           case filesystem::FT_FILE:
511           {
512               CheckSum chksm(resource.checksum());
513               if ( _checksums.find(filename.asString()) != _checksums.end() )
514               {
515                   // the checksum can be replaced with the one in the index.
516                   chksm = _checksums[filename.asString()];
517                   //MIL << "resource " << filename << " has checksum in the index file." << endl;
518               }
519               else
520                   WAR << "Resource " << filename << " has no checksum in the index either." << endl;
521
522               if ( flags & FetcherJob::AlwaysVerifyChecksum )
523                   enqueueDigested(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
524               else
525                   enqueue(OnMediaLocation(filename, resource.medianr()).setChecksum(chksm));
526               break;
527           }
528           case filesystem::FT_DIR: // newer directory.yast contain at least directory info
529               if ( flags & FetcherJob::Recursive )
530                   addDirJobs(media, filename, dest_dir, flags);
531               break;
532           default:
533               // don't provide devices, sockets, etc.
534               break;
535           }
536       }
537   }
538
539   void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir, const Pathname &deltafile )
540   {
541     bool got_from_cache = false;
542
543     // start look in cache
544     got_from_cache = provideFromCache(resource, dest_dir);
545
546     if ( ! got_from_cache )
547     {
548       MIL << "Not found in cache, downloading" << endl;
549
550       // try to get the file from the net
551       try
552       {
553         Pathname tmp_file = media.provideFile(resource, resource.optional() ? MediaSetAccess::PROVIDE_NON_INTERACTIVE : MediaSetAccess::PROVIDE_DEFAULT, deltafile );
554
555         Pathname dest_full_path = dest_dir + resource.filename();
556
557         if ( assert_dir( dest_full_path.dirname() ) != 0 )
558               ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
559         if ( filesystem::hardlinkCopy( tmp_file, dest_full_path ) != 0 )
560         {
561           if ( ! PathInfo(tmp_file).isExist() )
562               ERR << tmp_file << " does not exist" << endl;
563           if ( ! PathInfo(dest_full_path.dirname()).isExist() )
564               ERR << dest_full_path.dirname() << " does not exist" << endl;
565
566           media.releaseFile(resource); //not needed anymore, only eat space
567           ZYPP_THROW( Exception("Can't hardlink/copy " + tmp_file.asString() + " to " + dest_dir.asString()));
568         }
569
570         media.releaseFile(resource); //not needed anymore, only eat space
571       }
572       catch (Exception & excpt_r)
573       {
574         if ( resource.optional() )
575         {
576             ZYPP_CAUGHT(excpt_r);
577             WAR << "optional resource " << resource << " could not be transfered" << endl;
578             return;
579         }
580         else
581         {
582             excpt_r.remember("Can't provide " + resource.filename().asString() );
583             ZYPP_RETHROW(excpt_r);
584         }
585       }
586     }
587     else
588     {
589       // We got the file from cache
590       // continue with next file
591         return;
592     }
593   }
594
595   // helper class to consume a content file
596   struct ContentReaderHelper : public parser::susetags::ContentFileReader
597   {
598     ContentReaderHelper()
599     {
600       setRepoIndexConsumer( bind( &ContentReaderHelper::consumeIndex, this, _1 ) );
601     }
602
603     void consumeIndex( const parser::susetags::RepoIndex_Ptr & data_r )
604     { _repoindex = data_r; }
605
606     parser::susetags::RepoIndex_Ptr _repoindex;
607   };
608
609   // generic function for reading indexes
610   void Fetcher::Impl::readIndex( const Pathname &index, const Pathname &basedir )
611   {
612     if ( index.basename() == "SHA1SUMS" )
613       readSha1sumsIndex(index, basedir);
614     else if ( index.basename() == "content" )
615       readContentFileIndex(index, basedir);
616     else
617       WAR << index << ": index file format not known" << endl;
618   }
619
620   // reads a content file index
621   void Fetcher::Impl::readContentFileIndex( const Pathname &index, const Pathname &basedir )
622   {
623       ContentReaderHelper reader;
624       reader.parse(index);
625       MIL << index << " contains " << reader._repoindex->mediaFileChecksums.size() << " checksums." << endl;
626       for_( it, reader._repoindex->mediaFileChecksums.begin(), reader._repoindex->mediaFileChecksums.end() )
627       {
628           // content file entries don't start with /
629           _checksums[(basedir + it->first).asString()] = it->second;
630       }
631   }
632
633   // reads a SHA1SUMS file index
634   void Fetcher::Impl::readSha1sumsIndex( const Pathname &index, const Pathname &basedir )
635   {
636       std::ifstream in( index.c_str() );
637       string buffer;
638       if ( ! in.fail() )
639       {
640           while ( getline(in, buffer) )
641           {
642               vector<string> words;
643               str::split( buffer, back_inserter(words) );
644               if ( words.size() != 2 )
645                   ZYPP_THROW(Exception("Wrong format for SHA1SUMS file"));
646               //MIL << "check: '" << words[0] << "' | '" << words[1] << "'" << endl;
647               if ( ! words[1].empty() )
648                   _checksums[(basedir + words[1]).asString()] = CheckSum::sha1(words[0]);
649           }
650       }
651       else
652           ZYPP_THROW(Exception("Can't open SHA1SUMS file: " + index.asString()));
653   }
654
655   void Fetcher::Impl::downloadIndex( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir)
656   {
657     MIL << "downloading index " << resource << endl;
658     // create a new fetcher with a different state to transfer the
659     // file containing checksums and its signature
660     Fetcher fetcher;
661     // signature checker for index. We havent got the signature from
662     // the nextwork yet.
663     SignatureFileChecker sigchecker;
664
665     // build the name of the index and the signature
666     OnMediaLocation idxloc(resource);
667     OnMediaLocation sigloc(resource);
668     OnMediaLocation keyloc(resource);
669
670     // we should not fail the download if those don't exists
671     // the checking will warn later
672     sigloc.setOptional(true);
673     keyloc.setOptional(true);
674
675     // calculate signature and key name
676     sigloc.changeFilename( sigloc.filename().extend(".asc") );
677     keyloc.changeFilename( keyloc.filename().extend(".key") );
678
679     //assert_dir(dest_dir + idxloc.filename().dirname());
680
681     // transfer the signature
682     fetcher.enqueue(sigloc);
683     fetcher.start( dest_dir, media );
684     // if we get the signature, update the checker
685     if ( PathInfo(dest_dir + sigloc.filename()).isExist() )
686         sigchecker = SignatureFileChecker(dest_dir + sigloc.filename());
687
688     fetcher.reset();
689
690     // now the key
691     fetcher.enqueue(keyloc);
692     fetcher.start( dest_dir, media );
693     fetcher.reset();
694
695     // try to import the key
696     if ( PathInfo(dest_dir + keyloc.filename()).isExist() )
697         getZYpp()->keyRing()->importKey(PublicKey(dest_dir + keyloc.filename()), false);
698     else
699         WAR << "No public key specified by user for index '" << keyloc.filename() << "'"<< endl;
700
701     // now the index itself
702     fetcher.enqueue( idxloc, FileChecker(sigchecker) );
703     fetcher.start( dest_dir, media );
704     fetcher.reset();
705  }
706
707   // this method takes all the user pointed indexes, gets them and also tries to
708   // download their signature, and verify them. After that, its parses each one
709   // to fill the checksum cache.
710   void Fetcher::Impl::downloadAndReadIndexList( MediaSetAccess &media, const Pathname &dest_dir)
711   {
712       // if there is no indexes, then just return to avoid
713       // the directory listing
714       if ( _indexes.empty() )
715       {
716           MIL << "No indexes to read." << endl;
717           return;
718       }
719
720       for_( it_idx, _indexes.begin(), _indexes.end() )
721       {
722         if ( (*it_idx)->read )
723         {
724           DBG << "Already read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
725         }
726         else
727         {
728           // base::LogControl::TmpLineWriter shutUp;
729           downloadIndex( media, (*it_idx)->location, dest_dir );
730           // now we have the indexes in dest_dir
731           readIndex( dest_dir + (*it_idx)->location.filename(), (*it_idx)->location.filename().dirname() );
732           // Take care we don't process it again
733           MIL << "Remember read index " << PathInfo(dest_dir + (*it_idx)->location.filename()) << endl;
734           (*it_idx)->read = true;
735         }
736       }
737       MIL << "done reading indexes" << endl;
738   }
739
740   // start processing all fetcher jobs.
741   // it processes any user pointed index first
742   void Fetcher::Impl::start( const Pathname &dest_dir,
743                              MediaSetAccess &media,
744                              const ProgressData::ReceiverFnc & progress_receiver )
745   {
746     ProgressData progress(_resources.size());
747     progress.sendTo(progress_receiver);
748
749     downloadAndReadIndexList(media, dest_dir);
750
751     for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
752     {
753
754       if ( (*it_res)->flags & FetcherJob::Directory )
755       {
756           const OnMediaLocation location((*it_res)->location);
757           addDirJobs(media, location, dest_dir, (*it_res)->flags);
758           continue;
759       }
760
761       // may be this code can be factored out
762       // together with the autodiscovery of indexes
763       // of addDirJobs
764       if ( ( _options & AutoAddSha1sumsIndexes ) ||
765            ( _options & AutoAddContentFileIndexes ) )
766       {
767           // if auto indexing is enabled, then we need to read the
768           // index for each file. We look only in the directory
769           // where the file is. this is expensive of course.
770           filesystem::DirContent content;
771           getDirectoryContent(media, (*it_res)->location.filename().dirname(), content);
772           // this method test for the option flags so indexes are added
773           // only if the options are enabled
774           MIL << "Autodiscovering signed indexes on '"
775               << (*it_res)->location.filename().dirname() << "' for '"
776               << (*it_res)->location.filename() << "'" << endl;
777
778           autoaddIndexes(content, media, (*it_res)->location.filename().dirname(), dest_dir);
779
780           // also look in the root of the media
781           content.clear();
782           getDirectoryContent(media, Pathname("/"), content);
783           // this method test for the option flags so indexes are added
784           // only if the options are enabled
785           MIL << "Autodiscovering signed indexes on '"
786               << "/" << "' for '"
787               << (*it_res)->location.filename() << "'" << endl;
788
789           autoaddIndexes(content, media, Pathname("/"), dest_dir);
790       }
791
792       provideToDest(media, (*it_res)->location, dest_dir, (*it_res)->deltafile);
793
794       // if the file was not transfered, and no exception, just
795       // return, as it was an optional file
796       if ( ! PathInfo(dest_dir + (*it_res)->location.filename()).isExist() )
797           return;
798
799       // if the checksum is empty, but the checksum is in one of the
800       // indexes checksum, then add a checker
801       if ( (*it_res)->location.checksum().empty() )
802       {
803           if ( _checksums.find((*it_res)->location.filename().asString())
804                != _checksums.end() )
805           {
806               CheckSum chksm = _checksums[(*it_res)->location.filename().asString()];
807               ChecksumFileChecker digest_check(chksm);
808               (*it_res)->checkers.push_back(digest_check);
809           }
810           else
811           {
812               // if the index checksum is empty too, we only add the checker
813               // if the  AlwaysVerifyChecksum option is set on
814               if ( (*it_res)->flags & FetcherJob::AlwaysVerifyChecksum )
815               {
816                   // add the checker with the empty checksum
817                   ChecksumFileChecker digest_check((*it_res)->location.checksum());
818                   (*it_res)->checkers.push_back(digest_check);
819               }
820           }
821       }
822       else
823       {
824           // checksum is not empty, so add a checksum checker
825           ChecksumFileChecker digest_check((*it_res)->location.checksum());
826           (*it_res)->checkers.push_back(digest_check);
827       }
828
829       // validate job, this throws if not valid
830       validate((*it_res)->location, dest_dir, (*it_res)->checkers);
831
832       if ( ! progress.incr() )
833         ZYPP_THROW(AbortRequestException());
834     } // for each job
835   }
836
837   /** \relates Fetcher::Impl Stream output */
838   inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
839   {
840       for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
841       {
842           str << *it_res;
843       }
844       return str;
845   }
846
847   Fetcher::Fetcher()
848   : _pimpl( new Impl() )
849   {}
850
851   Fetcher::~Fetcher()
852   {}
853
854   void Fetcher::setOptions( Fetcher::Options options )
855   {
856     _pimpl->setOptions(options);
857   }
858
859   Fetcher::Options Fetcher::options() const
860   {
861     return _pimpl->options();
862   }
863
864   void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker, const Pathname &deltafile )
865   {
866     _pimpl->enqueueDigested(resource, checker, deltafile);
867   }
868
869   void Fetcher::enqueueDir( const OnMediaLocation &resource,
870                             bool recursive,
871                             const FileChecker &checker )
872   {
873       _pimpl->enqueueDir(resource, recursive, checker);
874   }
875
876   void Fetcher::enqueueDigestedDir( const OnMediaLocation &resource,
877                                     bool recursive,
878                                     const FileChecker &checker )
879   {
880       _pimpl->enqueueDigestedDir(resource, recursive, checker);
881   }
882
883
884   void Fetcher::addIndex( const OnMediaLocation &resource )
885   {
886     _pimpl->addIndex(resource);
887   }
888
889
890   void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker  )
891   {
892     _pimpl->enqueue(resource, checker);
893   }
894
895   void Fetcher::addCachePath( const Pathname &cache_dir )
896   {
897     _pimpl->addCachePath(cache_dir);
898   }
899
900   void Fetcher::reset()
901   {
902     _pimpl->reset();
903   }
904
905   void Fetcher::start( const Pathname &dest_dir,
906                        MediaSetAccess &media,
907                        const ProgressData::ReceiverFnc & progress_receiver )
908   {
909     _pimpl->start(dest_dir, media, progress_receiver);
910   }
911
912   std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
913   {
914     return str << *obj._pimpl;
915   }
916
917   /////////////////////////////////////////////////////////////////
918 } // namespace zypp
919 ///////////////////////////////////////////////////////////////////
920