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