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