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