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