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