change getService method to provide copy and not const reference
[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       //data::RecordId id = store.lookupRepository(info.alias());
757       RepoStatus cache_status = cacheStatus(info);
758
759       if ( cache_status.checksum() == raw_metadata_status.checksum() )
760       {
761         MIL << info.alias() << " cache is up to date with metadata." << endl;
762         if ( policy == BuildIfNeeded ) {
763           return;
764         }
765         else {
766           MIL << info.alias() << " cache rebuild is forced" << endl;
767         }
768       }
769
770       needs_cleaning = true;
771     }
772
773     ProgressData progress(100);
774     callback::SendReport<ProgressReport> report;
775     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
776     progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
777     progress.toMin();
778
779     if (needs_cleaning)
780     {
781       cleanCache(info);
782     }
783
784     MIL << info.alias() << " building cache..." << endl;
785
786     Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
787     filesystem::assert_dir(base);
788     Pathname solvfile = base / "solv";
789
790     // do we have type?
791     repo::RepoType repokind = info.type();
792
793     // if the type is unknown, try probing.
794     switch ( repokind.toEnum() )
795     {
796       case RepoType::NONE_e:
797         // unknown, probe the local metadata
798         repokind = probe(rawpath.asUrl());
799       break;
800       default:
801       break;
802     }
803
804     MIL << "repo type is " << repokind << endl;
805
806     switch ( repokind.toEnum() )
807     {
808       case RepoType::RPMMD_e :
809       case RepoType::YAST2_e :
810       case RepoType::RPMPLAINDIR_e :
811       {
812         // Take care we unlink the solvfile on exception
813         ManagedFile guard( solvfile, filesystem::unlink );
814
815         ostringstream cmd;
816         std::string toFile( str::gsub(solvfile.asString(),"\"","\\\"") );
817         if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
818         {
819           cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
820                             str::gsub( info.baseUrlsBegin()->getPathName(),"\"","\\\"" ).c_str(),
821                             toFile.c_str() );
822         }
823         else
824         {
825           cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
826                             str::gsub( rawpath.asString(),"\"","\\\"" ).c_str(),
827                             toFile.c_str() );
828         }
829         MIL << "Executing: " << cmd.str() << endl;
830         ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
831
832         cmd << endl;
833         for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
834           WAR << "  " << output;
835           cmd << "     " << output;
836         }
837
838         int ret = prog.close();
839         if ( ret != 0 )
840         {
841           RepoException ex(str::form("Failed to cache repo (%d).", ret));
842           ex.remember( cmd.str() );
843           ZYPP_THROW(ex);
844         }
845
846         // We keep it.
847         guard.resetDispose();
848       }
849       break;
850       default:
851         ZYPP_THROW(RepoUnknownTypeException("Unhandled repository type"));
852       break;
853     }
854 #if 0
855     switch ( repokind.toEnum() )
856     {
857       case RepoType::RPMMD_e :
858       if (0)
859       {
860         CombinedProgressData subprogrcv( progress, 100);
861         parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
862         parser.parse(rawpath);
863           // no error
864       }
865       break;
866       case RepoType::YAST2_e :
867       if (0)
868       {
869         CombinedProgressData subprogrcv( progress, 100);
870         parser::susetags::RepoParser parser(id, store, subprogrcv);
871         parser.parse(rawpath);
872         // no error
873       }
874       break;
875
876       default:
877         ZYPP_THROW(RepoUnknownTypeException());
878     }
879 #endif
880     // update timestamp and checksum
881     //store.updateRepositoryStatus(id, raw_metadata_status);
882     setCacheStatus(info, raw_metadata_status);
883     MIL << "Commit cache.." << endl;
884     //store.commit();
885     //progress.toMax();
886   }
887
888   ////////////////////////////////////////////////////////////////////////////
889
890   repo::RepoType RepoManager::probe( const Url &url ) const
891   {
892     if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
893     {
894       // Handle non existing local directory in advance, as
895       // MediaSetAccess does not support it.
896       return repo::RepoType::NONE;
897     }
898
899     try
900     {
901       MediaSetAccess access(url);
902       if ( access.doesFileExist("/repodata/repomd.xml") )
903         return repo::RepoType::RPMMD;
904       if ( access.doesFileExist("/content") )
905         return repo::RepoType::YAST2;
906
907       // if it is a local url of type dir
908       if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
909       {
910         Pathname path = Pathname(url.getPathName());
911         if ( PathInfo(path).isDir() )
912         {
913           // allow empty dirs for now
914           return repo::RepoType::RPMPLAINDIR;
915         }
916       }
917     }
918     catch ( const media::MediaException &e )
919     {
920       ZYPP_CAUGHT(e);
921       RepoException enew("Error trying to read from " + url.asString());
922       enew.remember(e);
923       ZYPP_THROW(enew);
924     }
925     catch ( const Exception &e )
926     {
927       ZYPP_CAUGHT(e);
928       Exception enew("Unknown error reading from " + url.asString());
929       enew.remember(e);
930       ZYPP_THROW(enew);
931     }
932
933     return repo::RepoType::NONE;
934   }
935
936   ////////////////////////////////////////////////////////////////////////////
937
938   void RepoManager::cleanCache( const RepoInfo &info,
939                                 const ProgressData::ReceiverFnc & progressrcv )
940   {
941     ProgressData progress(100);
942     progress.sendTo(progressrcv);
943     progress.toMin();
944
945     filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
946
947     progress.toMax();
948   }
949
950   ////////////////////////////////////////////////////////////////////////////
951
952   bool RepoManager::isCached( const RepoInfo &info ) const
953   {
954     return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
955   }
956
957   RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
958   {
959
960     Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
961
962     return RepoStatus::fromCookieFile(cookiefile);
963   }
964
965   void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
966   {
967     Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
968     filesystem::assert_dir(base);
969     Pathname cookiefile = base / "cookie";
970
971     status.saveToCookieFile(cookiefile);
972   }
973
974   void RepoManager::loadFromCache( const RepoInfo & info,
975                                    const ProgressData::ReceiverFnc & progressrcv )
976   {
977     assert_alias(info);
978     Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
979
980     if ( ! PathInfo(solvfile).isExist() )
981       ZYPP_THROW(RepoNotCachedException(info));
982
983     try
984     {
985       sat::Pool::instance().addRepoSolv( solvfile, info );
986     }
987     catch ( const Exception & exp )
988     {
989       ZYPP_CAUGHT( exp );
990       MIL << "Try to handle exception by rebuilding the solv-file" << endl;
991       cleanCache( info, progressrcv );
992       buildCache( info, BuildIfNeeded, progressrcv );
993
994       sat::Pool::instance().addRepoSolv( solvfile, info );
995     }
996   }
997
998   ////////////////////////////////////////////////////////////////////////////
999
1000   /**
1001    * Generate a non existing filename in a directory, using a base
1002    * name. For example if a directory contains 3 files
1003    *
1004    * |-- bar
1005    * |-- foo
1006    * `-- moo
1007    *
1008    * If you try to generate a unique filename for this directory,
1009    * based on "ruu" you will get "ruu", but if you use the base
1010    * "foo" you will get "foo_1"
1011    *
1012    * \param dir Directory where the file needs to be unique
1013    * \param basefilename string to base the filename on.
1014    */
1015   Pathname RepoManager::Impl::generateNonExistingName( const Pathname &dir,
1016                                        const std::string &basefilename ) const
1017   {
1018     string final_filename = basefilename;
1019     int counter = 1;
1020     while ( PathInfo(dir + final_filename).isExist() )
1021     {
1022       final_filename = basefilename + "_" + str::numstring(counter);
1023       counter++;
1024     }
1025     return dir + Pathname(final_filename);
1026   }
1027
1028   ////////////////////////////////////////////////////////////////////////////
1029
1030   /**
1031    * \short Generate a related filename from a repo info
1032    *
1033    * From a repo info, it will try to use the alias as a filename
1034    * escaping it if necessary. Other fallbacks can be added to
1035    * this function in case there is no way to use the alias
1036    */
1037   std::string RepoManager::Impl::generateFilename( const RepoInfo &info ) const
1038   {
1039     std::string filename = info.alias();
1040     // replace slashes with underscores
1041     str::replaceAll( filename, "/", "_" );
1042
1043     filename = Pathname(filename).extend(".repo").asString();
1044     MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
1045     return filename;
1046   }
1047
1048   std::string RepoManager::Impl::generateFilename( const Service &info ) const
1049   {
1050     std::string filename = info.name();
1051     // replace slashes with underscores
1052     str::replaceAll( filename, "/", "_" );
1053
1054     filename = Pathname(filename).extend(".service").asString();
1055     MIL << "generating filename for service [" << info.name() << "] : '" << filename << "'" << endl;
1056     return filename;
1057   }
1058
1059   ////////////////////////////////////////////////////////////////////////////
1060
1061   void RepoManager::addRepository( const RepoInfo &info,
1062                                    const ProgressData::ReceiverFnc & progressrcv )
1063   {
1064     assert_alias(info);
1065
1066     ProgressData progress(100);
1067     callback::SendReport<ProgressReport> report;
1068     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1069     progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
1070     progress.toMin();
1071
1072     RepoInfo tosave = info;
1073     if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
1074         ZYPP_THROW(RepoAlreadyExistsException(info));
1075
1076
1077     // check the first url for now
1078     if ( _pimpl->options.probe )
1079     {
1080       DBG << "unknown repository type, probing" << endl;
1081
1082       RepoType probedtype;
1083       probedtype = probe(*tosave.baseUrlsBegin());
1084       if ( tosave.baseUrlsSize() > 0 )
1085       {
1086         if ( probedtype == RepoType::NONE )
1087           ZYPP_THROW(RepoUnknownTypeException());
1088         else
1089           tosave.setType(probedtype);
1090       }
1091     }
1092
1093     progress.set(50);
1094
1095     // assert the directory exists
1096     filesystem::assert_dir(_pimpl->options.knownReposPath);
1097
1098     Pathname repofile = _pimpl->generateNonExistingName(
1099         _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
1100     // now we have a filename that does not exists
1101     MIL << "Saving repo in " << repofile << endl;
1102
1103     std::ofstream file(repofile.c_str());
1104     if (!file) {
1105       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1106     }
1107
1108     tosave.dumpRepoOn(file);
1109     tosave.setFilepath(repofile);
1110     _pimpl->repos.insert(tosave);
1111     progress.toMax();
1112     MIL << "done" << endl;
1113   }
1114
1115   void RepoManager::addRepositories( const Url &url,
1116                                      const ProgressData::ReceiverFnc & progressrcv )
1117   {
1118     std::list<RepoInfo> repos = readRepoFile(url);
1119     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1120           it != repos.end();
1121           ++it )
1122     {
1123       // look if the alias is in the known repos.
1124       for_ ( kit, repoBegin(), repoEnd() )
1125       {
1126         if ( (*it).alias() == (*kit).alias() )
1127         {
1128           ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1129           ZYPP_THROW(RepoAlreadyExistsException(*it));
1130         }
1131       }
1132     }
1133
1134     string filename = Pathname(url.getPathName()).basename();
1135
1136     if ( filename == Pathname() )
1137       ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
1138
1139     // assert the directory exists
1140     filesystem::assert_dir(_pimpl->options.knownReposPath);
1141
1142     Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
1143     // now we have a filename that does not exists
1144     MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1145
1146     std::ofstream file(repofile.c_str());
1147     if (!file) {
1148       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1149     }
1150
1151     for ( std::list<RepoInfo>::iterator it = repos.begin();
1152           it != repos.end();
1153           ++it )
1154     {
1155       MIL << "Saving " << (*it).alias() << endl;
1156       it->setFilepath(repofile.asString());
1157       it->dumpRepoOn(file);
1158       _pimpl->repos.insert(*it);
1159     }
1160     MIL << "done" << endl;
1161   }
1162
1163   ////////////////////////////////////////////////////////////////////////////
1164
1165   void RepoManager::removeRepository( const RepoInfo & info,
1166                                       const ProgressData::ReceiverFnc & progressrcv)
1167   {
1168     ProgressData progress;
1169     callback::SendReport<ProgressReport> report;
1170     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1171     progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
1172
1173     MIL << "Going to delete repo " << info.alias() << endl;
1174
1175     for_( it, repoBegin(), repoEnd() )
1176     {
1177       // they can be the same only if the provided is empty, that means
1178       // the provided repo has no alias
1179       // then skip
1180       if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1181         continue;
1182
1183       // TODO match by url
1184
1185       // we have a matcing repository, now we need to know
1186       // where it does come from.
1187       RepoInfo todelete = *it;
1188       if (todelete.filepath().empty())
1189       {
1190         ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1191       }
1192       else
1193       {
1194         // figure how many repos are there in the file:
1195         std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1196         if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
1197         {
1198           // easy, only this one, just delete the file
1199           if ( filesystem::unlink(todelete.filepath()) != 0 )
1200           {
1201             ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
1202           }
1203           MIL << todelete.alias() << " sucessfully deleted." << endl;
1204         }
1205         else
1206         {
1207           // there are more repos in the same file
1208           // write them back except the deleted one.
1209           //TmpFile tmp;
1210           //std::ofstream file(tmp.path().c_str());
1211
1212           // assert the directory exists
1213           filesystem::assert_dir(todelete.filepath().dirname());
1214
1215           std::ofstream file(todelete.filepath().c_str());
1216           if (!file) {
1217             //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1218             ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
1219           }
1220           for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1221                 fit != filerepos.end();
1222                 ++fit )
1223           {
1224             if ( (*fit).alias() != todelete.alias() )
1225               (*fit).dumpRepoOn(file);
1226           }
1227         }
1228
1229         CombinedProgressData subprogrcv(progress, 70);
1230         CombinedProgressData cleansubprogrcv(progress, 30);
1231         // now delete it from cache
1232         if ( isCached(todelete) )
1233           cleanCache( todelete, subprogrcv);
1234         // now delete metadata (#301037)
1235         cleanMetadata( todelete, cleansubprogrcv);
1236         _pimpl->repos.erase(todelete);
1237         MIL << todelete.alias() << " sucessfully deleted." << endl;
1238         return;
1239       } // else filepath is empty
1240
1241     }
1242     // should not be reached on a sucess workflow
1243     ZYPP_THROW(RepoNotFoundException(info));
1244   }
1245
1246   ////////////////////////////////////////////////////////////////////////////
1247
1248   void RepoManager::modifyRepository( const std::string &alias,
1249                                       const RepoInfo & newinfo,
1250                                       const ProgressData::ReceiverFnc & progressrcv )
1251   {
1252     RepoInfo toedit = getRepositoryInfo(alias);
1253
1254     // check if the new alias already exists when renaming the repo
1255     if (alias != newinfo.alias())
1256     {
1257       for_( it, repoBegin(), repoEnd() )
1258       {
1259         if ( newinfo.alias() == (*it).alias() )
1260           ZYPP_THROW(RepoAlreadyExistsException(newinfo));
1261       }
1262     }
1263
1264     if (toedit.filepath().empty())
1265     {
1266       ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1267     }
1268     else
1269     {
1270       // figure how many repos are there in the file:
1271       std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1272
1273       // there are more repos in the same file
1274       // write them back except the deleted one.
1275       //TmpFile tmp;
1276       //std::ofstream file(tmp.path().c_str());
1277
1278       // assert the directory exists
1279       filesystem::assert_dir(toedit.filepath().dirname());
1280
1281       std::ofstream file(toedit.filepath().c_str());
1282       if (!file) {
1283         //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1284         ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
1285       }
1286       for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1287             fit != filerepos.end();
1288             ++fit )
1289       {
1290           // if the alias is different, dump the original
1291           // if it is the same, dump the provided one
1292           if ( (*fit).alias() != toedit.alias() )
1293             (*fit).dumpRepoOn(file);
1294           else
1295             newinfo.dumpRepoOn(file);
1296       }
1297
1298       _pimpl->repos.erase(toedit);
1299       _pimpl->repos.insert(newinfo);
1300     }
1301   }
1302
1303   ////////////////////////////////////////////////////////////////////////////
1304
1305   RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
1306                                            const ProgressData::ReceiverFnc & progressrcv )
1307   {
1308     RepoInfo info;
1309     info.setAlias(info.alias());
1310     RepoConstIterator it = _pimpl->repos.find( info );
1311     if( it == repoEnd() )
1312       ZYPP_THROW(RepoNotFoundException(info));
1313     else
1314       return *it;
1315   }
1316
1317   ////////////////////////////////////////////////////////////////////////////
1318
1319   RepoInfo RepoManager::getRepositoryInfo( const Url & url,
1320                                            const url::ViewOption & urlview,
1321                                            const ProgressData::ReceiverFnc & progressrcv )
1322   {
1323     for_( it, repoBegin(), repoEnd() )
1324     {
1325       for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
1326           urlit != (*it).baseUrlsEnd();
1327           ++urlit)
1328       {
1329         if ((*urlit).asString(urlview) == url.asString(urlview))
1330           return *it;
1331       }
1332     }
1333     RepoInfo info;
1334     info.setBaseUrl(url);
1335     ZYPP_THROW(RepoNotFoundException(info));
1336   }
1337   
1338   void RepoManager::addService( const std::string& name, const Url& url )
1339   {
1340     addService( Service(name,url) );
1341   }
1342
1343   void RepoManager::addService( const Service& service )
1344   {
1345     //check if service isn't already exist
1346     if( _pimpl->services.find(service)!= _pimpl->services.end() )
1347       return; //TODO throw exception
1348
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
1386       for_(it, tmpSet.begin(), tmpSet.end())
1387       {
1388         if( it->name() != name )
1389           it->dumpServiceOn(file);
1390       }
1391     }
1392
1393     //now remove all repositories added by this service
1394     getRepositoriesInService( name, 
1395       boost::make_function_output_iterator(bind(&RepoManager::removeRepository, this, _1, ProgressData::ReceiverFnc()) ) );
1396   }
1397
1398
1399   void RepoManager::Impl::saveService( const Service& service ) const
1400   {
1401     filesystem::assert_dir( options.knownServicesPath );
1402
1403     Pathname servfile = generateNonExistingName( options.knownServicesPath,
1404         generateFilename( service ) );
1405
1406     MIL << "saving service in " << servfile << endl;
1407
1408     std::ofstream file(servfile.c_str());
1409     if (!file) {
1410       ZYPP_THROW (Exception( "Can't open " + servfile.asString() ) );
1411     }
1412
1413     service.dumpServiceOn( file );
1414
1415     const_cast<Service&>(service).setLocation( servfile );
1416     MIL << "done" << endl;
1417   }
1418
1419   Service RepoManager::getService( const std::string& name ) const
1420   {
1421     ServiceConstIterator it = _pimpl->services.find(name);
1422     if ( it == serviceEnd() )
1423       return Service::noService;
1424     else
1425       return *it;
1426   }
1427
1428   bool RepoManager::serviceEmpty() const { return _pimpl->services.empty(); }
1429
1430   RepoManager::ServiceSizeType RepoManager::serviceSize() const
1431   {
1432     return _pimpl->services.size();
1433   }
1434
1435   RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
1436   {
1437     return _pimpl->services.begin();
1438   }
1439
1440   RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
1441   {
1442     return _pimpl->services.end();
1443   }
1444
1445   void RepoManager::refreshServices()
1446   {
1447     //cannot user for_, because it uses const version
1448     for (std::set<Service>::iterator it = _pimpl->services.begin();
1449       it != _pimpl->services.end(); ++it)
1450     {
1451       MIL << "refresh: "<<it->name() << " with url: "<< it->url().asString() << endl;
1452       refreshService(*it);
1453     }
1454   }
1455
1456   void RepoManager::refreshService( const Service& service )
1457   {
1458     //download index file
1459     media::MediaManager mediamanager;
1460     media::MediaAccessId mid = mediamanager.open( service.url() );
1461     mediamanager.attachDesiredMedia( mid );
1462     mediamanager.provideFile( mid, "repo/repoindex.xml" );
1463     Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
1464
1465     //parse it
1466     RepoCollector collector;
1467     parser::RepoindexFileReader reader( path,
1468       bind( &RepoCollector::collect, &collector, _1 ) );
1469     mediamanager.release( mid );
1470     mediamanager.close( mid );
1471
1472     // set base url for all collected repositories
1473     for_( it, collector.repos.begin(), collector.repos.end())
1474     {
1475       it->setBaseUrl( service.url() );
1476       it->setService(service.name());
1477     }
1478
1479     //compare old and new repositories (hope not to much, if it change
1480     // then construct set and use set operation on it)
1481     
1482     std::list<RepoInfo> oldRepos;
1483     getRepositoriesInService(service.name(),
1484         insert_iterator<std::list<RepoInfo> >
1485         (oldRepos,oldRepos.begin()));
1486
1487     //find old to remove
1488     for_( it, oldRepos.begin(), oldRepos.end() )
1489     {
1490       bool found = false;
1491
1492       for_( it2, collector.repos.begin(), collector.repos.end() )
1493         if ( it->alias() == it2->alias() )
1494         {
1495           found = true;
1496           break;
1497         }
1498
1499       if( !found )
1500         removeRepository( *it );
1501     }
1502
1503     //find new to add
1504     for_( it, collector.repos.begin(), collector.repos.end() )
1505     {
1506       bool found = false;
1507
1508       for_( it2, oldRepos.begin(), oldRepos.end() )
1509         if( it->alias() == it2->alias() )
1510         {
1511           found = true;
1512           break;
1513         }
1514
1515       if (!found)
1516         addRepository( *it );
1517     }
1518
1519   }
1520
1521   void RepoManager::modifyService(const std::string& oldName, const Service& service)
1522   {
1523     MIL << "Going to modify service " << oldName << endl;
1524
1525     const Service& oldService = getService(oldName);
1526
1527     Pathname location = oldService.location();
1528     if( location.empty() )
1529     {
1530       ZYPP_THROW(RepoException("Can't figure where the service is stored"));
1531     }
1532
1533     ServiceSet tmpSet;
1534     Impl::ServiceCollector collector(tmpSet);
1535
1536     parser::ServiceFileReader reader( location,
1537         bind(&Impl::ServiceCollector::collect,collector,_1) );
1538
1539     filesystem::assert_dir(location.dirname());
1540
1541     std::ofstream file(location.c_str());
1542
1543     for_(it, tmpSet.begin(), tmpSet.end())
1544     {
1545       if( *it != oldName )
1546         it->dumpServiceOn(file);
1547     }
1548
1549     service.dumpServiceOn(file);
1550
1551     file.close();
1552
1553     _pimpl->services.erase(oldName);
1554     _pimpl->services.insert(service);
1555
1556     if( oldName != service.name() ) //changed name, must change also repository
1557     {
1558       std::vector<RepoInfo> toModify;
1559       getRepositoriesInService(oldName,
1560         insert_iterator<std::vector<RepoInfo> >( toModify, toModify.begin() ));
1561       for_( it, toModify.begin(), toModify.end() )
1562       {
1563         it->setService(service.name());
1564         modifyRepository(it->alias(), *it);
1565       }
1566     }
1567
1568   }
1569
1570   void RepoManager::Impl::knownServices()
1571   {
1572     ServiceCollector collector(services);
1573     Pathname dir = options.knownServicesPath;
1574     list<Pathname> entries;
1575     
1576     if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
1577         ZYPP_THROW(Exception("failed to read directory"));
1578
1579     str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1580     for_(it, entries.begin(), entries.end() )
1581     {
1582       parser::ServiceFileReader reader(*it, 
1583           bind(&ServiceCollector::collect, collector, _1) );
1584     }
1585   }
1586
1587   ////////////////////////////////////////////////////////////////////////////
1588
1589   std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
1590   {
1591     return str << *obj._pimpl;
1592   }
1593
1594   /////////////////////////////////////////////////////////////////
1595 } // namespace zypp
1596 ///////////////////////////////////////////////////////////////////