- A missing cookie file must not be treated as an error. Simply
[platform/upstream/libzypp.git] / zypp / RepoManager.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/RepoManager.cc
10  *
11 */
12
13 #include <cstdlib>
14 #include <iostream>
15 #include <fstream>
16 #include <sstream>
17 #include <list>
18 #include <map>
19 #include <algorithm>
20 #include "zypp/base/InputStream.h"
21 #include "zypp/base/Logger.h"
22 #include "zypp/base/Gettext.h"
23 #include "zypp/base/Function.h"
24 #include "zypp/base/Regex.h"
25 #include "zypp/PathInfo.h"
26 #include "zypp/TmpPath.h"
27 #include "zypp/Service.h"
28
29 #include "zypp/repo/RepoException.h"
30 #include "zypp/RepoManager.h"
31
32 #include "zypp/media/MediaManager.h"
33 #include "zypp/MediaSetAccess.h"
34 #include "zypp/ExternalProgram.h"
35 #include "zypp/ManagedFile.h"
36
37 #include "zypp/parser/RepoFileReader.h"
38 #include "zypp/parser/ServiceFileReader.h"
39 #include "zypp/parser/RepoindexFileReader.h"
40 #include "zypp/repo/yum/Downloader.h"
41 #include "zypp/parser/yum/RepoParser.h"
42 #include "zypp/repo/susetags/Downloader.h"
43 #include "zypp/parser/susetags/RepoParser.h"
44 #include "zypp/parser/plaindir/RepoParser.h"
45
46 #include "zypp/ZYppCallbacks.h"
47
48 #include "sat/Pool.h"
49 #include "satsolver/pool.h"
50 #include "satsolver/repo.h"
51 #include "satsolver/repo_solv.h"
52
53 using namespace std;
54 using namespace zypp;
55 using namespace zypp::repo;
56 using namespace zypp::filesystem;
57
58 using namespace zypp::repo;
59
60 ///////////////////////////////////////////////////////////////////
61 namespace zypp
62 { /////////////////////////////////////////////////////////////////
63
64   ///////////////////////////////////////////////////////////////////
65   //
66   //    CLASS NAME : RepoManagerOptions
67   //
68   ///////////////////////////////////////////////////////////////////
69
70   RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
71   {
72     repoCachePath         = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
73     repoRawCachePath      = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
74     repoSolvCachePath     = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
75     repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
76     knownReposPath        = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
77     knownServicesPath     = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
78     probe                 = ZConfig::instance().repo_add_probe();
79   }
80
81   RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
82   {
83     RepoManagerOptions ret;
84     ret.repoCachePath         = root_r;
85     ret.repoRawCachePath      = root_r/"raw";
86     ret.repoSolvCachePath     = root_r/"solv";
87     ret.repoPackagesCachePath = root_r/"packages";
88     ret.knownReposPath        = root_r/"repos.d";
89     ret.knownServicesPath        = root_r/"services.d";
90     return ret;
91   }
92
93   ////////////////////////////////////////////////////////////////////////////
94
95   /**
96     * \short Simple callback to collect the results
97     *
98     * Classes like RepoFileParser call the callback
99     * once per each repo in a file.
100     *
101     * Passing this functor as callback, you can collect
102     * all resuls at the end, without dealing with async
103     * code.
104     */
105     struct RepoCollector
106     {
107       RepoCollector()
108       {}
109
110       ~RepoCollector()
111       {}
112
113       bool collect( const RepoInfo &repo )
114       {
115         repos.push_back(repo);
116         return true;
117       }
118
119       RepoInfoList repos;
120     };
121
122   ////////////////////////////////////////////////////////////////////////////
123
124   /**
125    * Reads RepoInfo's from a repo file.
126    *
127    * \param file pathname of the file to read.
128    */
129   static std::list<RepoInfo> repositories_in_file( const Pathname & file )
130   {
131     MIL << "repo file: " << file << endl;
132     RepoCollector collector;
133     parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
134     return collector.repos;
135   }
136
137   ////////////////////////////////////////////////////////////////////////////
138
139   std::list<RepoInfo> readRepoFile(const Url & repo_file)
140    {
141      // no interface to download a specific file, using workaround:
142      //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
143      Url url(repo_file);
144      Pathname path(url.getPathName());
145      url.setPathName ("/");
146      MediaSetAccess access(url);
147      Pathname local = access.provideFile(path);
148
149      DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
150
151      return repositories_in_file(local);
152    }
153
154   ////////////////////////////////////////////////////////////////////////////
155
156   /**
157    * \short List of RepoInfo's from a directory
158    *
159    * Goes trough every file ending with ".repo" in a directory and adds all
160    * RepoInfo's contained in that file.
161    *
162    * \param dir pathname of the directory to read.
163    */
164   static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
165   {
166     MIL << "directory " << dir << endl;
167     list<RepoInfo> repos;
168     list<Pathname> entries;
169     if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
170       ZYPP_THROW(Exception("failed to read directory"));
171
172     str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
173     for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
174     {
175       if (str::regex_match(it->extension(), allowedRepoExt))
176       {
177         list<RepoInfo> tmp = repositories_in_file( *it );
178         repos.insert( repos.end(), tmp.begin(), tmp.end() );
179
180         //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
181         //MIL << "ok" << endl;
182       }
183     }
184     return repos;
185   }
186
187   ////////////////////////////////////////////////////////////////////////////
188
189   static void assert_alias( const RepoInfo &info )
190   {
191     if (info.alias().empty())
192         ZYPP_THROW(RepoNoAliasException());
193   }
194
195   ////////////////////////////////////////////////////////////////////////////
196
197   static void assert_urls( const RepoInfo &info )
198   {
199     if (info.baseUrlsEmpty())
200         ZYPP_THROW(RepoNoUrlException());
201   }
202
203   ////////////////////////////////////////////////////////////////////////////
204
205   /**
206    * \short Calculates the raw cache path for a repository
207    */
208   static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
209   {
210     assert_alias(info);
211     return opt.repoRawCachePath / info.escaped_alias();
212   }
213
214   /**
215    * \short Calculates the packages cache path for a repository
216    */
217   static Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
218   {
219     assert_alias(info);
220     return opt.repoPackagesCachePath / info.escaped_alias();
221   }
222
223   static Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
224   {
225     assert_alias(info);
226     return opt.repoSolvCachePath / info.escaped_alias();
227   }
228
229   ///////////////////////////////////////////////////////////////////
230   //
231   //    CLASS NAME : RepoManager::Impl
232   //
233   ///////////////////////////////////////////////////////////////////
234
235   /**
236    * \short RepoManager implementation.
237    */
238   struct RepoManager::Impl
239   {
240     Impl( const RepoManagerOptions &opt )
241       : options(opt)
242     {
243       knownServices();
244       knownRepositories();
245
246     }
247
248     Impl()
249     {
250
251     }
252
253     RepoManagerOptions options;
254
255     RepoSet repos;
256
257     ServiceSet services;
258
259   public:
260     /** Offer default Impl. */
261     static shared_ptr<Impl> nullimpl()
262     {
263       static shared_ptr<Impl> _nullimpl( new Impl );
264       return _nullimpl;
265     }
266
267     void saveService( const Service& service ) const;
268
269     Pathname generateNonExistingName( const Pathname &dir,
270                                          const std::string &basefilename ) const;
271
272     std::string generateFilename( const RepoInfo &info ) const;
273     std::string generateFilename( const Service &info ) const;
274
275     struct ServiceCollector {
276       ServiceCollector(ServiceSet& services_) : services(services_) {}
277       bool collect(Service service) { services.insert(service); return true; }
278       private:
279         ServiceSet& services;
280     };
281
282     void knownServices();
283
284     void knownRepositories();
285
286   private:
287     friend Impl * rwcowClone<Impl>( const Impl * rhs );
288     /** clone for RWCOW_pointer */
289     Impl * clone() const
290     { return new Impl( *this ); }
291   };
292
293   ///////////////////////////////////////////////////////////////////
294
295   /** \relates RepoManager::Impl Stream output */
296   inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
297   {
298     return str << "RepoManager::Impl";
299   }
300
301   ///////////////////////////////////////////////////////////////////
302   //
303   //    CLASS NAME : RepoManager
304   //
305   ///////////////////////////////////////////////////////////////////
306
307   RepoManager::RepoManager( const RepoManagerOptions &opt )
308   : _pimpl( new Impl(opt) )
309   {}
310
311   ////////////////////////////////////////////////////////////////////////////
312
313   RepoManager::~RepoManager()
314   {}
315
316   ////////////////////////////////////////////////////////////////////////////
317
318   bool RepoManager::repoEmpty() const { return _pimpl->repos.empty(); }
319   RepoManager::RepoSizeType RepoManager::repoSize() const
320   { return _pimpl->repos.size(); }
321   RepoManager::RepoConstIterator RepoManager::repoBegin() const
322   { return _pimpl->repos.begin(); }
323   RepoManager::RepoConstIterator RepoManager::repoEnd() const
324   { return _pimpl->repos.end(); }
325
326
327   std::list<RepoInfo> RepoManager::knownRepositories() const
328   {
329     return std::list<RepoInfo>(repoBegin(),repoEnd());
330   }
331
332   void RepoManager::Impl::knownRepositories()
333   {
334     MIL << "start construct known repos" << endl;
335
336     if ( PathInfo(options.knownReposPath).isExist() )
337     {
338       RepoInfoList repol = repositories_in_dir(options.knownReposPath);
339       for ( RepoInfoList::iterator it = repol.begin();
340             it != repol.end();
341             ++it )
342       {
343         // set the metadata path for the repo
344         Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
345         (*it).setMetadataPath(metadata_path);
346
347         // set the downloaded packages path for the repo
348         Pathname packages_path = packagescache_path_for_repoinfo(options, (*it));
349         (*it).setPackagesPath(packages_path);
350
351         repos.insert(*it);
352       }
353     }
354
355     MIL << "end construct known repos" << endl;
356   }
357
358   ////////////////////////////////////////////////////////////////////////////
359
360   Pathname RepoManager::metadataPath( const RepoInfo &info ) const
361   {
362     return rawcache_path_for_repoinfo(_pimpl->options, info );
363   }
364
365   Pathname RepoManager::packagesPath( const RepoInfo &info ) const
366   {
367     return packagescache_path_for_repoinfo(_pimpl->options, info );
368   }
369
370   ////////////////////////////////////////////////////////////////////////////
371
372   RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
373   {
374     Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
375     RepoType repokind = info.type();
376     RepoStatus status;
377
378     switch ( repokind.toEnum() )
379     {
380       case RepoType::NONE_e:
381       // unknown, probe the local metadata
382         repokind = probe(rawpath.asUrl());
383       break;
384       default:
385       break;
386     }
387
388     switch ( repokind.toEnum() )
389     {
390       case RepoType::RPMMD_e :
391       {
392         status = RepoStatus( rawpath + "/repodata/repomd.xml");
393       }
394       break;
395
396       case RepoType::YAST2_e :
397       {
398         status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
399       }
400       break;
401
402       case RepoType::RPMPLAINDIR_e :
403       {
404         if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
405           status = RepoStatus( rawpath + "/cookie");
406       }
407       break;
408
409       case RepoType::NONE_e :
410         // Return default RepoStatus in case of RepoType::NONE
411         // indicating it should be created?
412         // ZYPP_THROW(RepoUnknownTypeException());
413         break;
414     }
415     return status;
416   }
417
418   void RepoManager::touchIndexFile(const RepoInfo & info)
419   {
420     Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
421
422     RepoType repokind = info.type();
423     if ( repokind.toEnum() == RepoType::NONE_e )
424       // unknown, probe the local metadata
425       repokind = probe(rawpath.asUrl());
426     // if still unknown, just return
427     if (repokind == RepoType::NONE_e)
428       return;
429
430     Pathname p;
431     switch ( repokind.toEnum() )
432     {
433       case RepoType::RPMMD_e :
434         p = Pathname(rawpath + "/repodata/repomd.xml");
435         break;
436
437       case RepoType::YAST2_e :
438         p = Pathname(rawpath + "/content");
439         break;
440
441       case RepoType::RPMPLAINDIR_e :
442         p = Pathname(rawpath + "/cookie");
443         break;
444
445       case RepoType::NONE_e :
446       default:
447         break;
448     }
449
450     // touch the file, ignore error (they are logged anyway)
451     filesystem::touch(p);
452   }
453
454   RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata(
455                                               const RepoInfo &info,
456                                               const Url &url,
457                                               RawMetadataRefreshPolicy policy )
458   {
459     assert_alias(info);
460
461     RepoStatus oldstatus;
462     RepoStatus newstatus;
463
464     try
465     {
466       MIL << "Going to try to check whether refresh is needed for " << url << endl;
467
468       repo::RepoType repokind = info.type();
469
470       // if the type is unknown, try probing.
471       switch ( repokind.toEnum() )
472       {
473         case RepoType::NONE_e:
474           // unknown, probe it
475           repokind = probe(url);
476         break;
477         default:
478         break;
479       }
480
481       Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
482       filesystem::assert_dir(rawpath);
483       oldstatus = metadataStatus(info);
484
485       // now we've got the old (cached) status, we can decide repo.refresh.delay
486       if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
487       {
488         // difference in seconds
489         double diff = difftime(
490           (Date::ValueType)Date::now(),
491           (Date::ValueType)oldstatus.timestamp()) / 60;
492
493         DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
494         DBG << "current time: " << (Date::ValueType)Date::now() << endl;
495         DBG << "last refresh = " << diff << " minutes ago" << endl;
496
497         if (diff < ZConfig::instance().repo_refresh_delay())
498         {
499           MIL << "Repository '" << info.alias()
500               << "' has been refreshed less than repo.refresh.delay ("
501               << ZConfig::instance().repo_refresh_delay()
502               << ") minutes ago. Advising to skip refresh" << endl;
503           return REPO_CHECK_DELAYED;
504         }
505       }
506
507       // create temp dir as sibling of rawpath
508       filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
509
510       if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
511            ( repokind.toEnum() == RepoType::YAST2_e ) )
512       {
513         MediaSetAccess media(url);
514         shared_ptr<repo::Downloader> downloader_ptr;
515
516         if ( repokind.toEnum() == RepoType::RPMMD_e )
517           downloader_ptr.reset(new yum::Downloader(info.path()));
518         else
519           downloader_ptr.reset( new susetags::Downloader(info.path()));
520
521         RepoStatus newstatus = downloader_ptr->status(media);
522         bool refresh = false;
523         if ( oldstatus.checksum() == newstatus.checksum() )
524         {
525           MIL << "repo has not changed" << endl;
526           if ( policy == RefreshForced )
527           {
528             MIL << "refresh set to forced" << endl;
529             refresh = true;
530           }
531         }
532         else
533         {
534           MIL << "repo has changed, going to refresh" << endl;
535           refresh = true;
536         }
537
538         if (!refresh)
539           touchIndexFile(info);
540
541         return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
542       }
543       else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
544       {
545         RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
546         bool refresh = false;
547         if ( oldstatus.checksum() == newstatus.checksum() )
548         {
549           MIL << "repo has not changed" << endl;
550           if ( policy == RefreshForced )
551           {
552             MIL << "refresh set to forced" << endl;
553             refresh = true;
554           }
555         }
556         else
557         {
558           MIL << "repo has changed, going to refresh" << endl;
559           refresh = true;
560         }
561
562         if (!refresh)
563           touchIndexFile(info);
564
565         return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
566       }
567       else
568       {
569         ZYPP_THROW(RepoUnknownTypeException(info));
570       }
571     }
572     catch ( const Exception &e )
573     {
574       ZYPP_CAUGHT(e);
575       ERR << "refresh check failed for " << url << endl;
576       ZYPP_RETHROW(e);
577     }
578
579     return REFRESH_NEEDED; // default
580   }
581
582   void RepoManager::refreshMetadata( const RepoInfo &info,
583                                      RawMetadataRefreshPolicy policy,
584                                      const ProgressData::ReceiverFnc & progress )
585   {
586     assert_alias(info);
587     assert_urls(info);
588
589     // we will throw this later if no URL checks out fine
590     RepoException rexception(_("Valid metadata not found at specified URL(s)"));
591
592     // try urls one by one
593     for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
594     {
595       try
596       {
597         Url url(*it);
598
599         // check whether to refresh metadata
600         // if the check fails for this url, it throws, so another url will be checked
601         if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
602           return;
603
604         MIL << "Going to refresh metadata from " << url << endl;
605
606         repo::RepoType repokind = info.type();
607
608         // if the type is unknown, try probing.
609         switch ( repokind.toEnum() )
610         {
611           case RepoType::NONE_e:
612             // unknown, probe it
613             repokind = probe(*it);
614
615             if (repokind.toEnum() != RepoType::NONE_e)
616             {
617               // Adjust the probed type in RepoInfo
618               info.setProbedType( repokind ); // lazy init!
619               //save probed type only for repos in system
620               for_( it, repoBegin(), repoEnd() )
621               {
622                 if ( info.alias() == (*it).alias() )
623                 {
624                   RepoInfo modifiedrepo = info;
625                   modifiedrepo.setType(repokind);
626                   modifyRepository(info.alias(),modifiedrepo);
627                 }
628               }
629             }
630           break;
631           default:
632           break;
633         }
634
635         Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
636         filesystem::assert_dir(rawpath);
637
638         // create temp dir as sibling of rawpath
639         filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
640
641         if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
642              ( repokind.toEnum() == RepoType::YAST2_e ) )
643         {
644           MediaSetAccess media(url);
645           shared_ptr<repo::Downloader> downloader_ptr;
646
647           if ( repokind.toEnum() == RepoType::RPMMD_e )
648             downloader_ptr.reset(new yum::Downloader(info.path()));
649           else
650             downloader_ptr.reset( new susetags::Downloader(info.path()));
651
652           /**
653            * Given a downloader, sets the other repos raw metadata
654            * path as cache paths for the fetcher, so if another
655            * repo has the same file, it will not download it
656            * but copy it from the other repository
657            */
658           for_( it, repoBegin(), repoEnd() )
659           {
660             Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
661             if ( PathInfo(cachepath).isExist() )
662               downloader_ptr->addCachePath(cachepath);
663           }
664
665           downloader_ptr->download( media, tmpdir.path());
666         }
667         else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
668         {
669           RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
670
671           std::ofstream file(( tmpdir.path() + "/cookie").c_str());
672           if (!file) {
673             ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
674           }
675           file << url << endl;
676           file << newstatus.checksum() << endl;
677
678           file.close();
679         }
680         else
681         {
682           ZYPP_THROW(RepoUnknownTypeException());
683         }
684
685         // ok we have the metadata, now exchange
686         // the contents
687
688         TmpDir oldmetadata( TmpDir::makeSibling( rawpath ) );
689         filesystem::rename( rawpath, oldmetadata.path() );
690         // move the just downloaded there
691         filesystem::rename( tmpdir.path(), rawpath );
692
693         // we are done.
694         return;
695       }
696       catch ( const Exception &e )
697       {
698         ZYPP_CAUGHT(e);
699         ERR << "Trying another url..." << endl;
700
701         // remember the exception caught for the *first URL*
702         // if all other URLs fail, the rexception will be thrown with the
703         // cause of the problem of the first URL remembered
704         if (it == info.baseUrlsBegin())
705           rexception.remember(e);
706       }
707     } // for every url
708     ERR << "No more urls..." << endl;
709     ZYPP_THROW(rexception);
710   }
711
712   ////////////////////////////////////////////////////////////////////////////
713
714   void RepoManager::cleanMetadata( const RepoInfo &info,
715                                    const ProgressData::ReceiverFnc & progressfnc )
716   {
717     ProgressData progress(100);
718     progress.sendTo(progressfnc);
719
720     filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
721     progress.toMax();
722   }
723
724   void RepoManager::cleanPackages( const RepoInfo &info,
725                                    const ProgressData::ReceiverFnc & progressfnc )
726   {
727     ProgressData progress(100);
728     progress.sendTo(progressfnc);
729
730     filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info));
731     progress.toMax();
732   }
733
734   void RepoManager::buildCache( const RepoInfo &info,
735                                 CacheBuildPolicy policy,
736                                 const ProgressData::ReceiverFnc & progressrcv )
737   {
738     assert_alias(info);
739     Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
740
741     filesystem::assert_dir(_pimpl->options.repoCachePath);
742     RepoStatus raw_metadata_status = metadataStatus(info);
743     if ( raw_metadata_status.empty() )
744     {
745        /* if there is no cache at this point, we refresh the raw
746           in case this is the first time - if it's !autorefresh,
747           we may still refresh */
748       refreshMetadata(info, RefreshIfNeeded, progressrcv );
749       raw_metadata_status = metadataStatus(info);
750     }
751
752     bool needs_cleaning = false;
753     if ( isCached( info ) )
754     {
755       MIL << info.alias() << " is already cached." << endl;
756       RepoStatus cache_status = cacheStatus(info);
757
758       if ( cache_status.checksum() == raw_metadata_status.checksum() )
759       {
760         MIL << info.alias() << " cache is up to date with metadata." << endl;
761         if ( policy == BuildIfNeeded ) {
762           return;
763         }
764         else {
765           MIL << info.alias() << " cache rebuild is forced" << endl;
766         }
767       }
768
769       needs_cleaning = true;
770     }
771
772     ProgressData progress(100);
773     callback::SendReport<ProgressReport> report;
774     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
775     progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
776     progress.toMin();
777
778     if (needs_cleaning)
779     {
780       cleanCache(info);
781     }
782
783     MIL << info.alias() << " building cache..." << endl;
784
785     Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
786     filesystem::assert_dir(base);
787     Pathname solvfile = base / "solv";
788
789     // do we have type?
790     repo::RepoType repokind = info.type();
791
792     // if the type is unknown, try probing.
793     switch ( repokind.toEnum() )
794     {
795       case RepoType::NONE_e:
796         // unknown, probe the local metadata
797         repokind = probe(rawpath.asUrl());
798       break;
799       default:
800       break;
801     }
802
803     MIL << "repo type is " << repokind << endl;
804
805     switch ( repokind.toEnum() )
806     {
807       case RepoType::RPMMD_e :
808       case RepoType::YAST2_e :
809       case RepoType::RPMPLAINDIR_e :
810       {
811         // Take care we unlink the solvfile on exception
812         ManagedFile guard( solvfile, filesystem::unlink );
813
814         ostringstream cmd;
815         std::string toFile( str::gsub(solvfile.asString(),"\"","\\\"") );
816         if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
817         {
818           cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
819                             str::gsub( info.baseUrlsBegin()->getPathName(),"\"","\\\"" ).c_str(),
820                             toFile.c_str() );
821         }
822         else
823         {
824           cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
825                             str::gsub( rawpath.asString(),"\"","\\\"" ).c_str(),
826                             toFile.c_str() );
827         }
828         MIL << "Executing: " << cmd.str() << endl;
829         ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
830
831         cmd << endl;
832         for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
833           WAR << "  " << output;
834           cmd << "     " << output;
835         }
836
837         int ret = prog.close();
838         if ( ret != 0 )
839         {
840           RepoException ex(str::form("Failed to cache repo (%d).", ret));
841           ex.remember( cmd.str() );
842           ZYPP_THROW(ex);
843         }
844
845         // We keep it.
846         guard.resetDispose();
847       }
848       break;
849       default:
850         ZYPP_THROW(RepoUnknownTypeException("Unhandled repository type"));
851       break;
852     }
853 #if 0
854     switch ( repokind.toEnum() )
855     {
856       case RepoType::RPMMD_e :
857       if (0)
858       {
859         CombinedProgressData subprogrcv( progress, 100);
860         parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
861         parser.parse(rawpath);
862           // no error
863       }
864       break;
865       case RepoType::YAST2_e :
866       if (0)
867       {
868         CombinedProgressData subprogrcv( progress, 100);
869         parser::susetags::RepoParser parser(id, store, subprogrcv);
870         parser.parse(rawpath);
871         // no error
872       }
873       break;
874
875       default:
876         ZYPP_THROW(RepoUnknownTypeException());
877     }
878 #endif
879     // update timestamp and checksum
880     //store.updateRepositoryStatus(id, raw_metadata_status);
881     setCacheStatus(info, raw_metadata_status);
882     MIL << "Commit cache.." << endl;
883     //store.commit();
884     //progress.toMax();
885   }
886
887   ////////////////////////////////////////////////////////////////////////////
888
889   repo::RepoType RepoManager::probe( const Url &url ) const
890   {
891     if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
892     {
893       // Handle non existing local directory in advance, as
894       // MediaSetAccess does not support it.
895       return repo::RepoType::NONE;
896     }
897
898     try
899     {
900       MediaSetAccess access(url);
901       if ( access.doesFileExist("/repodata/repomd.xml") )
902         return repo::RepoType::RPMMD;
903       if ( access.doesFileExist("/content") )
904         return repo::RepoType::YAST2;
905
906       // if it is a local url of type dir
907       if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
908       {
909         Pathname path = Pathname(url.getPathName());
910         if ( PathInfo(path).isDir() )
911         {
912           // allow empty dirs for now
913           return repo::RepoType::RPMPLAINDIR;
914         }
915       }
916     }
917     catch ( const media::MediaException &e )
918     {
919       ZYPP_CAUGHT(e);
920       RepoException enew("Error trying to read from " + url.asString());
921       enew.remember(e);
922       ZYPP_THROW(enew);
923     }
924     catch ( const Exception &e )
925     {
926       ZYPP_CAUGHT(e);
927       Exception enew("Unknown error reading from " + url.asString());
928       enew.remember(e);
929       ZYPP_THROW(enew);
930     }
931
932     return repo::RepoType::NONE;
933   }
934
935   ////////////////////////////////////////////////////////////////////////////
936
937   void RepoManager::cleanCache( const RepoInfo &info,
938                                 const ProgressData::ReceiverFnc & progressrcv )
939   {
940     ProgressData progress(100);
941     progress.sendTo(progressrcv);
942     progress.toMin();
943
944     filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
945
946     progress.toMax();
947   }
948
949   ////////////////////////////////////////////////////////////////////////////
950
951   bool RepoManager::isCached( const RepoInfo &info ) const
952   {
953     return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
954   }
955
956   RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
957   {
958
959     Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
960
961     return RepoStatus::fromCookieFile(cookiefile);
962   }
963
964   void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
965   {
966     Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
967     filesystem::assert_dir(base);
968     Pathname cookiefile = base / "cookie";
969
970     status.saveToCookieFile(cookiefile);
971   }
972
973   void RepoManager::loadFromCache( const RepoInfo & info,
974                                    const ProgressData::ReceiverFnc & progressrcv )
975   {
976     assert_alias(info);
977     Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
978
979     if ( ! PathInfo(solvfile).isExist() )
980       ZYPP_THROW(RepoNotCachedException(info));
981
982     try
983     {
984       sat::Pool::instance().addRepoSolv( solvfile, info );
985     }
986     catch ( const Exception & exp )
987     {
988       ZYPP_CAUGHT( exp );
989       MIL << "Try to handle exception by rebuilding the solv-file" << endl;
990       cleanCache( info, progressrcv );
991       buildCache( info, BuildIfNeeded, progressrcv );
992
993       sat::Pool::instance().addRepoSolv( solvfile, info );
994     }
995   }
996
997   ////////////////////////////////////////////////////////////////////////////
998
999   /**
1000    * Generate a non existing filename in a directory, using a base
1001    * name. For example if a directory contains 3 files
1002    *
1003    * |-- bar
1004    * |-- foo
1005    * `-- moo
1006    *
1007    * If you try to generate a unique filename for this directory,
1008    * based on "ruu" you will get "ruu", but if you use the base
1009    * "foo" you will get "foo_1"
1010    *
1011    * \param dir Directory where the file needs to be unique
1012    * \param basefilename string to base the filename on.
1013    */
1014   Pathname RepoManager::Impl::generateNonExistingName( const Pathname &dir,
1015                                        const std::string &basefilename ) const
1016   {
1017     string final_filename = basefilename;
1018     int counter = 1;
1019     while ( PathInfo(dir + final_filename).isExist() )
1020     {
1021       final_filename = basefilename + "_" + str::numstring(counter);
1022       counter++;
1023     }
1024     return dir + Pathname(final_filename);
1025   }
1026
1027   ////////////////////////////////////////////////////////////////////////////
1028
1029   /**
1030    * \short Generate a related filename from a repo info
1031    *
1032    * From a repo info, it will try to use the alias as a filename
1033    * escaping it if necessary. Other fallbacks can be added to
1034    * this function in case there is no way to use the alias
1035    */
1036   std::string RepoManager::Impl::generateFilename( const RepoInfo &info ) const
1037   {
1038     std::string filename = info.alias();
1039     // replace slashes with underscores
1040     str::replaceAll( filename, "/", "_" );
1041
1042     filename = Pathname(filename).extend(".repo").asString();
1043     MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
1044     return filename;
1045   }
1046
1047   std::string RepoManager::Impl::generateFilename( const Service &info ) const
1048   {
1049     std::string filename = info.name();
1050     // replace slashes with underscores
1051     str::replaceAll( filename, "/", "_" );
1052
1053     filename = Pathname(filename).extend(".service").asString();
1054     MIL << "generating filename for service [" << info.name() << "] : '" << filename << "'" << endl;
1055     return filename;
1056   }
1057
1058   ////////////////////////////////////////////////////////////////////////////
1059
1060   void RepoManager::addRepository( const RepoInfo &info,
1061                                    const ProgressData::ReceiverFnc & progressrcv )
1062   {
1063     assert_alias(info);
1064
1065     ProgressData progress(100);
1066     callback::SendReport<ProgressReport> report;
1067     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1068     progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
1069     progress.toMin();
1070
1071     RepoInfo tosave = info;
1072     if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
1073         ZYPP_THROW(RepoAlreadyExistsException(info));
1074
1075
1076     // check the first url for now
1077     if ( _pimpl->options.probe )
1078     {
1079       DBG << "unknown repository type, probing" << endl;
1080
1081       RepoType probedtype;
1082       probedtype = probe(*tosave.baseUrlsBegin());
1083       if ( tosave.baseUrlsSize() > 0 )
1084       {
1085         if ( probedtype == RepoType::NONE )
1086           ZYPP_THROW(RepoUnknownTypeException());
1087         else
1088           tosave.setType(probedtype);
1089       }
1090     }
1091
1092     progress.set(50);
1093
1094     // assert the directory exists
1095     filesystem::assert_dir(_pimpl->options.knownReposPath);
1096
1097     Pathname repofile = _pimpl->generateNonExistingName(
1098         _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
1099     // now we have a filename that does not exists
1100     MIL << "Saving repo in " << repofile << endl;
1101
1102     std::ofstream file(repofile.c_str());
1103     if (!file) {
1104       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1105     }
1106
1107     tosave.dumpRepoOn(file);
1108     tosave.setFilepath(repofile);
1109     _pimpl->repos.insert(tosave);
1110     progress.toMax();
1111     MIL << "done" << endl;
1112   }
1113
1114   void RepoManager::addRepositories( const Url &url,
1115                                      const ProgressData::ReceiverFnc & progressrcv )
1116   {
1117     std::list<RepoInfo> repos = readRepoFile(url);
1118     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1119           it != repos.end();
1120           ++it )
1121     {
1122       // look if the alias is in the known repos.
1123       for_ ( kit, repoBegin(), repoEnd() )
1124       {
1125         if ( (*it).alias() == (*kit).alias() )
1126         {
1127           ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1128           ZYPP_THROW(RepoAlreadyExistsException(*it));
1129         }
1130       }
1131     }
1132
1133     string filename = Pathname(url.getPathName()).basename();
1134
1135     if ( filename == Pathname() )
1136       ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
1137
1138     // assert the directory exists
1139     filesystem::assert_dir(_pimpl->options.knownReposPath);
1140
1141     Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
1142     // now we have a filename that does not exists
1143     MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1144
1145     std::ofstream file(repofile.c_str());
1146     if (!file) {
1147       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1148     }
1149
1150     for ( std::list<RepoInfo>::iterator it = repos.begin();
1151           it != repos.end();
1152           ++it )
1153     {
1154       MIL << "Saving " << (*it).alias() << endl;
1155       it->setFilepath(repofile.asString());
1156       it->dumpRepoOn(file);
1157       _pimpl->repos.insert(*it);
1158     }
1159     MIL << "done" << endl;
1160   }
1161
1162   ////////////////////////////////////////////////////////////////////////////
1163
1164   void RepoManager::removeRepository( const RepoInfo & info,
1165                                       const ProgressData::ReceiverFnc & progressrcv)
1166   {
1167     ProgressData progress;
1168     callback::SendReport<ProgressReport> report;
1169     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1170     progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
1171
1172     MIL << "Going to delete repo " << info.alias() << endl;
1173
1174     for_( it, repoBegin(), repoEnd() )
1175     {
1176       // they can be the same only if the provided is empty, that means
1177       // the provided repo has no alias
1178       // then skip
1179       if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1180         continue;
1181
1182       // TODO match by url
1183
1184       // we have a matcing repository, now we need to know
1185       // where it does come from.
1186       RepoInfo todelete = *it;
1187       if (todelete.filepath().empty())
1188       {
1189         ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1190       }
1191       else
1192       {
1193         // figure how many repos are there in the file:
1194         std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1195         if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
1196         {
1197           // easy, only this one, just delete the file
1198           if ( filesystem::unlink(todelete.filepath()) != 0 )
1199           {
1200             ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
1201           }
1202           MIL << todelete.alias() << " sucessfully deleted." << endl;
1203         }
1204         else
1205         {
1206           // there are more repos in the same file
1207           // write them back except the deleted one.
1208           //TmpFile tmp;
1209           //std::ofstream file(tmp.path().c_str());
1210
1211           // assert the directory exists
1212           filesystem::assert_dir(todelete.filepath().dirname());
1213
1214           std::ofstream file(todelete.filepath().c_str());
1215           if (!file) {
1216             //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1217             ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
1218           }
1219           for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1220                 fit != filerepos.end();
1221                 ++fit )
1222           {
1223             if ( (*fit).alias() != todelete.alias() )
1224               (*fit).dumpRepoOn(file);
1225           }
1226         }
1227
1228         CombinedProgressData subprogrcv(progress, 70);
1229         CombinedProgressData cleansubprogrcv(progress, 30);
1230         // now delete it from cache
1231         if ( isCached(todelete) )
1232           cleanCache( todelete, subprogrcv);
1233         // now delete metadata (#301037)
1234         cleanMetadata( todelete, cleansubprogrcv);
1235         _pimpl->repos.erase(todelete);
1236         MIL << todelete.alias() << " sucessfully deleted." << endl;
1237         return;
1238       } // else filepath is empty
1239
1240     }
1241     // should not be reached on a sucess workflow
1242     ZYPP_THROW(RepoNotFoundException(info));
1243   }
1244
1245   ////////////////////////////////////////////////////////////////////////////
1246
1247   void RepoManager::modifyRepository( const std::string &alias,
1248                                       const RepoInfo & newinfo,
1249                                       const ProgressData::ReceiverFnc & progressrcv )
1250   {
1251     RepoInfo toedit = getRepositoryInfo(alias);
1252
1253     // check if the new alias already exists when renaming the repo
1254     if (alias != newinfo.alias())
1255     {
1256       for_( it, repoBegin(), repoEnd() )
1257       {
1258         if ( newinfo.alias() == (*it).alias() )
1259           ZYPP_THROW(RepoAlreadyExistsException(newinfo));
1260       }
1261     }
1262
1263     if (toedit.filepath().empty())
1264     {
1265       ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1266     }
1267     else
1268     {
1269       // figure how many repos are there in the file:
1270       std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1271
1272       // there are more repos in the same file
1273       // write them back except the deleted one.
1274       //TmpFile tmp;
1275       //std::ofstream file(tmp.path().c_str());
1276
1277       // assert the directory exists
1278       filesystem::assert_dir(toedit.filepath().dirname());
1279
1280       std::ofstream file(toedit.filepath().c_str());
1281       if (!file) {
1282         //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1283         ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
1284       }
1285       for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1286             fit != filerepos.end();
1287             ++fit )
1288       {
1289           // if the alias is different, dump the original
1290           // if it is the same, dump the provided one
1291           if ( (*fit).alias() != toedit.alias() )
1292             (*fit).dumpRepoOn(file);
1293           else
1294             newinfo.dumpRepoOn(file);
1295       }
1296
1297       _pimpl->repos.erase(toedit);
1298       _pimpl->repos.insert(newinfo);
1299     }
1300   }
1301
1302   ////////////////////////////////////////////////////////////////////////////
1303
1304   RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
1305                                            const ProgressData::ReceiverFnc & progressrcv )
1306   {
1307     RepoInfo info;
1308     info.setAlias(alias);
1309     RepoConstIterator it = _pimpl->repos.find( info );
1310     if( it == repoEnd() )
1311       ZYPP_THROW(RepoNotFoundException(info));
1312     else
1313       return *it;
1314   }
1315
1316   ////////////////////////////////////////////////////////////////////////////
1317
1318   RepoInfo RepoManager::getRepositoryInfo( const Url & url,
1319                                            const url::ViewOption & urlview,
1320                                            const ProgressData::ReceiverFnc & progressrcv )
1321   {
1322     for_( it, repoBegin(), repoEnd() )
1323     {
1324       for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
1325           urlit != (*it).baseUrlsEnd();
1326           ++urlit)
1327       {
1328         if ((*urlit).asString(urlview) == url.asString(urlview))
1329           return *it;
1330       }
1331     }
1332     RepoInfo info;
1333     info.setBaseUrl(url);
1334     ZYPP_THROW(RepoNotFoundException(info));
1335   }
1336
1337   void RepoManager::addService( const std::string& name, const Url& url )
1338   {
1339     addService( Service(name,url) );
1340   }
1341
1342   void RepoManager::addService( const Service& service )
1343   {
1344     //check if service isn't already exist
1345     if( _pimpl->services.find(service)!= _pimpl->services.end() )
1346       return; //FIXME ZYPP_THROW(RepoAlreadyExistsException(service.name()));
1347
1348     //this is need to save location to correct service
1349     const Service& savedService = *(_pimpl->services.insert( service )).first;
1350
1351     _pimpl->saveService( savedService );
1352   }
1353
1354   void RepoManager::removeService( const string& name)
1355   {
1356     MIL << "Going to delete repo " << name << endl;
1357
1358     const Service& service = getService( name );
1359
1360     Pathname location = service.location();
1361     if( location.empty() )
1362     {
1363       ZYPP_THROW(RepoException("Can't figure where the service is stored"));
1364     }
1365
1366     ServiceSet tmpSet;
1367     Impl::ServiceCollector collector(tmpSet);
1368
1369     parser::ServiceFileReader reader( location,
1370         bind(&Impl::ServiceCollector::collect,collector,_1) );
1371
1372     if ( tmpSet.size() == 1 ) //only one record
1373     {
1374       if ( filesystem::unlink(location) != 0 )
1375       {
1376         ZYPP_THROW(RepoException("Can't delete " + location.asString()));
1377       }
1378       MIL << name << " sucessfully deleted." << endl;
1379     }
1380     else
1381     {
1382       filesystem::assert_dir(location.dirname());
1383
1384       std::ofstream file(location.c_str());
1385       if( file.fail() )
1386         ZYPP_THROW(Exception("failed open file to write"));
1387
1388       for_(it, tmpSet.begin(), tmpSet.end())
1389       {
1390         if( it->name() != name )
1391           it->dumpServiceOn(file);
1392       }
1393
1394       MIL << name << " sucessfully deleted from file " << location <<  endl;
1395     }
1396
1397     //now remove all repositories added by this service
1398     getRepositoriesInService( name,
1399       boost::make_function_output_iterator(bind(&RepoManager::removeRepository, this, _1, ProgressData::ReceiverFnc()) ) );
1400   }
1401
1402
1403   void RepoManager::Impl::saveService( const Service& service ) const
1404   {
1405     filesystem::assert_dir( options.knownServicesPath );
1406
1407     Pathname servfile = generateNonExistingName( options.knownServicesPath,
1408         generateFilename( service ) );
1409
1410     MIL << "saving service in " << servfile << endl;
1411
1412     std::ofstream file(servfile.c_str());
1413     if (!file) {
1414       ZYPP_THROW (Exception( "Can't open " + servfile.asString() ) );
1415     }
1416
1417     service.dumpServiceOn( file );
1418
1419     const_cast<Service&>(service).setLocation( servfile );
1420     MIL << "done" << endl;
1421   }
1422
1423   Service RepoManager::getService( const std::string& name ) const
1424   {
1425     ServiceConstIterator it = _pimpl->services.find(name);
1426     if ( it == serviceEnd() )
1427       return Service::noService;
1428     else
1429       return *it;
1430   }
1431
1432   bool RepoManager::serviceEmpty() const { return _pimpl->services.empty(); }
1433
1434   RepoManager::ServiceSizeType RepoManager::serviceSize() const
1435   {
1436     return _pimpl->services.size();
1437   }
1438
1439   RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
1440   {
1441     return _pimpl->services.begin();
1442   }
1443
1444   RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
1445   {
1446     return _pimpl->services.end();
1447   }
1448
1449   void RepoManager::refreshServices()
1450   {
1451     //cannot user for_, because it uses const version
1452     for (std::set<Service>::iterator it = _pimpl->services.begin();
1453       it != _pimpl->services.end(); ++it)
1454     {
1455       MIL << "refresh: "<<it->name() << " with url: "<< it->url().asString() << endl;
1456       refreshService(*it);
1457     }
1458   }
1459
1460   void RepoManager::refreshService( const Service& service )
1461   {
1462     //download index file
1463     media::MediaManager mediamanager;
1464     media::MediaAccessId mid = mediamanager.open( service.url() ); //FIXME check if url is not empty
1465     mediamanager.attachDesiredMedia( mid );
1466     mediamanager.provideFile( mid, "repo/repoindex.xml" );
1467     Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
1468
1469     //parse it
1470     RepoCollector collector;
1471     parser::RepoindexFileReader reader( path,
1472       bind( &RepoCollector::collect, &collector, _1 ) );
1473     mediamanager.release( mid );
1474     mediamanager.close( mid );
1475
1476     // set base url for all collected repositories
1477     for_( it, collector.repos.begin(), collector.repos.end())
1478     {
1479       it->setBaseUrl( service.url() );
1480       it->setService(service.name());
1481     }
1482
1483     //compare old and new repositories (hope not to much, if it change
1484     // then construct set and use set operation on it)
1485
1486     std::list<RepoInfo> oldRepos;
1487     getRepositoriesInService(service.name(),
1488         insert_iterator<std::list<RepoInfo> >
1489         (oldRepos,oldRepos.begin()));
1490
1491     //find old to remove
1492     for_( it, oldRepos.begin(), oldRepos.end() )
1493     {
1494       bool found = false;
1495
1496       for_( it2, collector.repos.begin(), collector.repos.end() )
1497         if ( it->alias() == it2->alias() )
1498         {
1499           found = true;
1500           break;
1501         }
1502
1503       if( !found )
1504         removeRepository( *it );
1505     }
1506
1507     //find new to add
1508     for_( it, collector.repos.begin(), collector.repos.end() )
1509     {
1510       bool found = false;
1511
1512       for_( it2, oldRepos.begin(), oldRepos.end() )
1513         if( it->alias() == it2->alias() )
1514         {
1515           found = true;
1516           break;
1517         }
1518
1519       if (!found)
1520         addRepository( *it );
1521     }
1522
1523   }
1524
1525   void RepoManager::modifyService(const std::string& oldName, const Service& service)
1526   {
1527     MIL << "Going to modify service " << oldName << endl;
1528
1529     const Service& oldService = getService(oldName);
1530
1531     Pathname location = oldService.location();
1532     if( location.empty() )
1533     {
1534       ZYPP_THROW(RepoException("Can't figure where the service is stored"));
1535     }
1536
1537     ServiceSet tmpSet;
1538     Impl::ServiceCollector collector(tmpSet);
1539
1540     parser::ServiceFileReader reader( location,
1541         bind(&Impl::ServiceCollector::collect,collector,_1) );
1542
1543     filesystem::assert_dir(location.dirname());
1544
1545     std::ofstream file(location.c_str());
1546
1547     for_(it, tmpSet.begin(), tmpSet.end())
1548     {
1549       if( *it != oldName )
1550         it->dumpServiceOn(file);
1551     }
1552
1553     service.dumpServiceOn(file);
1554
1555     file.close();
1556
1557     _pimpl->services.erase(oldName);
1558     _pimpl->services.insert(service);
1559
1560     if( oldName != service.name() ) //changed name, must change also repository
1561     {
1562       std::vector<RepoInfo> toModify;
1563       getRepositoriesInService(oldName,
1564         insert_iterator<std::vector<RepoInfo> >( toModify, toModify.begin() ));
1565       for_( it, toModify.begin(), toModify.end() )
1566       {
1567         it->setService(service.name());
1568         modifyRepository(it->alias(), *it);
1569       }
1570     }
1571
1572   }
1573
1574   void RepoManager::Impl::knownServices()
1575   {
1576     ServiceCollector collector(services);
1577     Pathname dir = options.knownServicesPath;
1578     list<Pathname> entries;
1579     if (PathInfo(dir).isExist())
1580     {
1581       if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
1582           ZYPP_THROW(Exception("failed to read directory"));
1583
1584       str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1585       for_(it, entries.begin(), entries.end() )
1586       {
1587         parser::ServiceFileReader reader(*it,
1588             bind(&ServiceCollector::collect, collector, _1) );
1589       }
1590     }
1591   }
1592
1593   ////////////////////////////////////////////////////////////////////////////
1594
1595   std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
1596   {
1597     return str << *obj._pimpl;
1598   }
1599
1600   /////////////////////////////////////////////////////////////////
1601 } // namespace zypp
1602 ///////////////////////////////////////////////////////////////////