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