getRepositoryInfo(Url,url::ViewOption) added
[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 <iostream>
14 #include <fstream>
15 #include <list>
16 #include <algorithm>
17 #include "zypp/base/InputStream.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/Gettext.h"
20 #include "zypp/base/Function.h"
21 #include "zypp/PathInfo.h"
22 #include "zypp/TmpPath.h"
23
24 #include "zypp/repo/RepoException.h"
25 #include "zypp/RepoManager.h"
26
27 #include "zypp/cache/CacheStore.h"
28 #include "zypp/repo/cached/RepoImpl.h"
29 #include "zypp/media/MediaManager.h"
30 #include "zypp/MediaSetAccess.h"
31
32 #include "zypp/parser/RepoFileReader.h"
33 #include "zypp/repo/yum/Downloader.h"
34 #include "zypp/parser/yum/RepoParser.h"
35 #include "zypp/parser/plaindir/RepoParser.h"
36 #include "zypp/repo/susetags/Downloader.h"
37 #include "zypp/parser/susetags/RepoParser.h"
38
39 #include "zypp/ZYppCallbacks.h"
40
41 using namespace std;
42 using namespace zypp;
43 using namespace zypp::repo;
44 using namespace zypp::filesystem;
45
46 using namespace zypp::repo;
47
48 ///////////////////////////////////////////////////////////////////
49 namespace zypp
50 { /////////////////////////////////////////////////////////////////
51
52   ///////////////////////////////////////////////////////////////////
53   //
54   //    CLASS NAME : RepoManagerOptions
55   //
56   ///////////////////////////////////////////////////////////////////
57
58   RepoManagerOptions::RepoManagerOptions()
59   {
60     ZConfig globalConfig;
61     repoCachePath = globalConfig.defaultRepoCachePath();
62     repoRawCachePath = globalConfig.defaultRepoRawCachePath();
63     knownReposPath = globalConfig.defaultKnownReposPath();
64   }
65
66   ////////////////////////////////////////////////////////////////////////////
67
68   /**
69     * \short Simple callback to collect the results
70     *
71     * Classes like RepoFileParser call the callback
72     * once per each repo in a file.
73     *
74     * Passing this functor as callback, you can collect
75     * all resuls at the end, without dealing with async
76     * code.
77     */
78     struct RepoCollector
79     {
80       RepoCollector()
81       {
82         MIL << endl;
83       }
84
85       ~RepoCollector()
86       {
87         MIL << endl;
88       }
89
90       bool collect( const RepoInfo &repo )
91       {
92         //MIL << "here in collector: " << repo.alias() << endl;
93         repos.push_back(repo);
94         //MIL << "added: " << repo.alias() << endl;
95         return true;
96       }
97
98       RepoInfoList repos;
99     };
100
101   ////////////////////////////////////////////////////////////////////////////
102
103   /**
104    * Reads RepoInfo's from a repo file.
105    *
106    * \param file pathname of the file to read.
107    */
108   static std::list<RepoInfo> repositories_in_file( const Pathname & file )
109   {
110     MIL << "repo file: " << file << endl;
111     RepoCollector collector;
112     parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
113     return collector.repos;
114   }
115
116   ////////////////////////////////////////////////////////////////////////////
117
118   std::list<RepoInfo> readRepoFile(const Url & repo_file)
119    {
120      // no interface to download a specific file, using workaround:
121      //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
122      Url url(repo_file);
123      Pathname path(url.getPathName());
124      url.setPathName ("/");
125      MediaSetAccess access(url);
126      Pathname local = access.provideFile(path);
127
128      DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
129
130      return repositories_in_file(local);
131    }
132
133   ////////////////////////////////////////////////////////////////////////////
134
135   /**
136    * \short List of RepoInfo's from a directory
137    *
138    * Goes trough every file in a directory and adds all
139    * RepoInfo's contained in that file.
140    *
141    * \param dir pathname of the directory to read.
142    */
143   static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
144   {
145     MIL << "directory " << dir << endl;
146     list<RepoInfo> repos;
147     list<Pathname> entries;
148     if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
149       ZYPP_THROW(Exception("failed to read directory"));
150
151     for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
152     {
153       list<RepoInfo> tmp = repositories_in_file( *it );
154       repos.insert( repos.end(), tmp.begin(), tmp.end() );
155
156       //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
157       //MIL << "ok" << endl;
158     }
159     return repos;
160   }
161
162   ////////////////////////////////////////////////////////////////////////////
163
164   static void assert_alias( const RepoInfo &info )
165   {
166     if (info.alias().empty())
167         ZYPP_THROW(RepoNoAliasException());
168   }
169
170   ////////////////////////////////////////////////////////////////////////////
171
172   static void assert_urls( const RepoInfo &info )
173   {
174     if (info.baseUrls().empty())
175         ZYPP_THROW(RepoNoUrlException());
176   }
177
178   ////////////////////////////////////////////////////////////////////////////
179
180   /**
181    * \short Calculates the raw cache path for a repository
182    */
183   static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
184   {
185     assert_alias(info);
186     return opt.repoRawCachePath + info.alias();
187   }
188
189   ///////////////////////////////////////////////////////////////////
190   //
191   //    CLASS NAME : RepoManager::Impl
192   //
193   ///////////////////////////////////////////////////////////////////
194
195   /**
196    * \short RepoManager implementation.
197    */
198   struct RepoManager::Impl
199   {
200     Impl( const RepoManagerOptions &opt )
201       : options(opt)
202     {
203
204     }
205
206     Impl()
207     {
208
209     }
210
211     RepoManagerOptions options;
212
213   public:
214     /** Offer default Impl. */
215     static shared_ptr<Impl> nullimpl()
216     {
217       static shared_ptr<Impl> _nullimpl( new Impl );
218       return _nullimpl;
219     }
220
221   private:
222     friend Impl * rwcowClone<Impl>( const Impl * rhs );
223     /** clone for RWCOW_pointer */
224     Impl * clone() const
225     { return new Impl( *this ); }
226   };
227   ///////////////////////////////////////////////////////////////////
228
229   /** \relates RepoManager::Impl Stream output */
230   inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
231   {
232     return str << "RepoManager::Impl";
233   }
234
235   ///////////////////////////////////////////////////////////////////
236   //
237   //    CLASS NAME : RepoManager
238   //
239   ///////////////////////////////////////////////////////////////////
240
241   RepoManager::RepoManager( const RepoManagerOptions &opt )
242   : _pimpl( new Impl(opt) )
243   {}
244
245   ////////////////////////////////////////////////////////////////////////////
246
247   RepoManager::~RepoManager()
248   {}
249
250   ////////////////////////////////////////////////////////////////////////////
251
252   std::list<RepoInfo> RepoManager::knownRepositories() const
253   {
254     MIL << endl;
255     
256     if ( PathInfo(_pimpl->options.knownReposPath).isExist() )
257       return repositories_in_dir(_pimpl->options.knownReposPath);
258     else
259       return std::list<RepoInfo>();
260
261     MIL << endl;
262   }
263
264   ////////////////////////////////////////////////////////////////////////////
265
266   RepoStatus RepoManager::rawMetadataStatus( const RepoInfo &info )
267   {
268     Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
269     RepoType repokind = info.type();
270     RepoStatus status;
271
272     switch ( repokind.toEnum() )
273     {
274       case RepoType::NONE_e:
275       // unknown, probe the local metadata
276         repokind = probe(rawpath.asUrl());
277       break;
278       default:
279       break;
280     }
281
282     switch ( repokind.toEnum() )
283     {
284       case RepoType::RPMMD_e :
285       {
286         status = RepoStatus( rawpath + "/repodata/repomd.xml");
287       }
288       break;
289
290       case RepoType::YAST2_e :
291       {
292         status = RepoStatus( rawpath + "/content");
293       }
294       break;
295       
296       case RepoType::RPMPLAINDIR_e :
297       {
298         if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
299           status = RepoStatus( rawpath + "/cookie");
300       }
301       break;
302
303       case RepoType::NONE_e :
304         // Return default RepoStatus in case of RepoType::NONE
305         // indicating it should be created?
306         // ZYPP_THROW(RepoUnknownTypeException());
307         break;
308     }
309     return status;
310   }
311
312
313   void RepoManager::refreshMetadata( const RepoInfo &info,
314                                      RawMetadataRefreshPolicy policy,
315                                      const ProgressData::ReceiverFnc & progress )
316   {
317     assert_alias(info);
318     assert_urls(info);
319
320     RepoStatus oldstatus;
321     RepoStatus newstatus;
322     // try urls one by one
323     for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
324     {
325       try
326       {
327         Url url(*it);
328         filesystem::TmpDir tmpdir;
329
330         repo::RepoType repokind = info.type();
331
332         // if the type is unknown, try probing.
333         switch ( repokind.toEnum() )
334         {
335           case RepoType::NONE_e:
336             // unknown, probe it
337             repokind = probe(*it);
338           break;
339           default:
340           break;
341         }
342
343         Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
344         oldstatus = rawMetadataStatus(info);
345
346         switch ( repokind.toEnum() )
347         {
348           case RepoType::RPMMD_e :
349           {
350             yum::Downloader downloader( url, "/" );
351
352             RepoStatus newstatus = downloader.status();
353             bool refresh = false;
354             if ( oldstatus.checksum() == newstatus.checksum() )
355             {
356               MIL << "repo has not changed" << endl;
357               if ( policy == RefreshForced )
358               {
359                 MIL << "refresh set to forced" << endl;
360                 refresh = true;
361               }
362             }
363             else
364             {
365               refresh = true;
366             }
367
368             if ( refresh )
369               downloader.download(tmpdir.path());
370             else
371               return;
372             // no error
373           }
374           break;
375           case RepoType::YAST2_e :
376           {
377             susetags::Downloader downloader( url, "/" );
378
379             RepoStatus newstatus = downloader.status();
380             bool refresh = false;
381             if ( oldstatus.checksum() == newstatus.checksum() )
382             {
383               MIL << "repo has not changed" << endl;
384               if ( policy == RefreshForced )
385               {
386                 MIL << "refresh set to forced" << endl;
387                 refresh = true;
388               }
389             }
390             else
391             {
392               refresh = true;
393             }
394
395             if ( refresh )
396               downloader.download(tmpdir.path());
397             else
398               return;
399             // no error
400           }
401           break;
402           
403           case RepoType::RPMPLAINDIR_e :
404           {
405             RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
406             bool refresh = false;
407             if ( oldstatus.checksum() == newstatus.checksum() )
408             {
409               MIL << "repo has not changed" << endl;
410               if ( policy == RefreshForced )
411               {
412                 MIL << "refresh set to forced" << endl;
413                 refresh = true;
414               }
415             }
416             else
417             {
418               refresh = true;
419             }
420
421             if ( refresh )
422             {
423               std::ofstream file(( tmpdir.path() + "/cookie").c_str());
424               if (!file) {
425                 ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
426               }
427               file << url << endl;
428               file << newstatus.checksum() << endl;
429
430               file.close();
431             }
432             else
433               return;
434             // no error
435           }
436           break;
437           default:
438             ZYPP_THROW(RepoUnknownTypeException());
439         }
440
441         // ok we have the metadata, now exchange
442         // the contents
443         TmpDir oldmetadata;
444         filesystem::assert_dir(rawpath);
445         filesystem::rename( rawpath, oldmetadata.path() );
446         // move the just downloaded there
447         filesystem::rename( tmpdir.path(), rawpath );
448         // we are done.
449         return;
450       }
451       catch ( const Exception &e )
452       {
453         ZYPP_CAUGHT(e);
454         ERR << "Trying another url..." << endl;
455       }
456     } // for every url
457     ERR << "No more urls..." << endl;
458     ZYPP_THROW(RepoException("Cant refresh metadata"));
459   }
460
461   ////////////////////////////////////////////////////////////////////////////
462
463   void RepoManager::cleanMetadata( const RepoInfo &info,
464                                    const ProgressData::ReceiverFnc & progress )
465   {
466     filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
467   }
468
469   void RepoManager::buildCache( const RepoInfo &info,
470                                 CacheBuildPolicy policy,
471                                 const ProgressData::ReceiverFnc & progressrcv )
472   {
473     ProgressData progress(100);
474     callback::SendReport<ProgressReport> report;
475     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
476     progress.name(str::form(_("Building repository '%s' cache"), info.alias().c_str()));
477     progress.toMin();
478     
479     assert_alias(info);
480     Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
481
482     cache::CacheStore store(_pimpl->options.repoCachePath);
483
484     RepoStatus raw_metadata_status = rawMetadataStatus(info);
485     if ( store.isCached( info.alias() ) )
486     {
487       MIL << info.alias() << " is already cached." << endl;
488       data::RecordId id = store.lookupRepository(info.alias());
489       RepoStatus cache_status = store.repositoryStatus(id);
490
491       if ( cache_status.checksum() == raw_metadata_status.checksum() )
492       {
493         MIL << info.alias() << " cache is up to date with metadata." << endl;
494         if ( policy == BuildIfNeeded ) {
495           progress.toMax();
496           return;
497         }
498         else {
499           MIL << info.alias() << " cache rebuild is forced" << endl;
500         }
501       }
502       MIL << info.alias() << " cleaning cache..." << endl;
503       store.cleanRepository(id);
504     }
505
506     MIL << info.alias() << " building cache..." << endl;
507     data::RecordId id = store.lookupOrAppendRepository(info.alias());
508     // do we have type?
509     repo::RepoType repokind = info.type();
510
511     // if the type is unknown, try probing.
512     switch ( repokind.toEnum() )
513     {
514       case RepoType::NONE_e:
515         // unknown, probe the local metadata
516         repokind = probe(rawpath.asUrl());
517       break;
518       default:
519       break;
520     }
521
522     CombinedProgressData subprogrcv( progress, 100);
523     
524     switch ( repokind.toEnum() )
525     {
526       case RepoType::RPMMD_e :
527       {
528         parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
529         parser.parse(rawpath);
530           // no error
531       }
532       break;
533       case RepoType::YAST2_e :
534       {
535         parser::susetags::RepoParser parser(id, store, subprogrcv);
536         parser.parse(rawpath);
537         // no error
538       }
539       break;
540       case RepoType::RPMPLAINDIR_e :
541       {
542         InputStream is(rawpath + "cookie");
543         string buffer;
544         getline( is.stream(), buffer);
545         Url url(buffer);
546         parser::plaindir::RepoParser parser(id, store, subprogrcv);
547         parser.parse(url.getPathName());
548       }
549       break;
550       default:
551         ZYPP_THROW(RepoUnknownTypeException());
552     }
553
554     // update timestamp and checksum
555     store.updateRepositoryStatus(id, raw_metadata_status);
556
557     MIL << "Commit cache.." << endl;
558     store.commit();
559     progress.toMax();
560   }
561
562   ////////////////////////////////////////////////////////////////////////////
563
564   repo::RepoType RepoManager::probe( const Url &url )
565   {
566     if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
567     {
568       // Handle non existing local directory in advance, as
569       // MediaSetAccess does not support it.
570       return repo::RepoType::NONE;
571     }
572
573     MediaSetAccess access(url);
574     if ( access.doesFileExist("/repodata/repomd.xml") )
575       return repo::RepoType::RPMMD;
576     if ( access.doesFileExist("/content") )
577       return repo::RepoType::YAST2;
578
579     // if it is a local url of type dir
580     if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
581     {
582       Pathname path = Pathname(url.getPathName());
583       if ( PathInfo(path).isDir() )
584       {
585         // allow empty dirs for now
586         return repo::RepoType::RPMPLAINDIR;
587       }
588     }
589
590     return repo::RepoType::NONE;
591   }
592
593   ////////////////////////////////////////////////////////////////////////////
594
595   void RepoManager::cleanCache( const RepoInfo &info,
596                                 const ProgressData::ReceiverFnc & progressrcv )
597   {
598     ProgressData progress(100);
599     callback::SendReport<ProgressReport> report;
600     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
601     progress.name(str::form(_("Cleaning repository '%s' cache"), info.alias().c_str()));
602     progress.toMin();
603     
604     cache::CacheStore store(_pimpl->options.repoCachePath);
605
606     data::RecordId id = store.lookupRepository(info.alias());
607     store.cleanRepository(id);
608     store.commit();
609   }
610
611   ////////////////////////////////////////////////////////////////////////////
612
613   bool RepoManager::isCached( const RepoInfo &info ) const
614   {
615     cache::CacheStore store(_pimpl->options.repoCachePath);
616     return store.isCached(info.alias());
617   }
618
619   Repository RepoManager::createFromCache( const RepoInfo &info,
620                                            const ProgressData::ReceiverFnc & progressrcv )
621   {
622     callback::SendReport<ProgressReport> report;
623     ProgressData progress;
624     progress.sendTo(ProgressReportAdaptor( progressrcv, report ));
625     progress.sendTo( progressrcv );
626     progress.name(str::form(_("Reading repository '%s' cache"), info.alias().c_str()));
627     progress.toMin();
628     
629     cache::CacheStore store(_pimpl->options.repoCachePath);
630
631     if ( ! store.isCached( info.alias() ) )
632       ZYPP_THROW(RepoNotCachedException());
633
634     MIL << "Repository " << info.alias() << " is cached" << endl;
635
636     data::RecordId id = store.lookupRepository(info.alias());
637     
638     repo::cached::RepoOptions opts( info, _pimpl->options.repoCachePath, id );
639     opts.readingResolvablesProgress = progressrcv;
640     repo::cached::RepoImpl::Ptr repoimpl =
641         new repo::cached::RepoImpl( opts );
642     
643     repoimpl->resolvables();
644     // read the resolvables from cache
645     return Repository(repoimpl);
646   }
647
648   ////////////////////////////////////////////////////////////////////////////
649
650   /**
651    * Generate a non existing filename in a directory, using a base
652    * name. For example if a directory contains 3 files
653    *
654    * |-- bar
655    * |-- foo
656    * `-- moo
657    *
658    * If you try to generate a unique filename for this directory,
659    * based on "ruu" you will get "ruu", but if you use the base
660    * "foo" you will get "foo_1"
661    *
662    * \param dir Directory where the file needs to be unique
663    * \param basefilename string to base the filename on.
664    */
665   static Pathname generate_non_existing_name( const Pathname &dir,
666                                               const std::string &basefilename )
667   {
668     string final_filename = basefilename;
669     int counter = 1;
670     while ( PathInfo(dir + final_filename).isExist() )
671     {
672       final_filename = basefilename + "_" + str::numstring(counter);
673       counter++;
674     }
675     return dir + Pathname(final_filename);
676   }
677
678   ////////////////////////////////////////////////////////////////////////////
679
680   void RepoManager::addRepository( const RepoInfo &info,
681                                    const ProgressData::ReceiverFnc & progressrcv )
682   {
683     assert_alias(info);
684
685     ProgressData progress(100);
686     callback::SendReport<ProgressReport> report;
687     progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
688     progress.name(str::form(_("Adding repository '%s'"), info.alias().c_str()));
689     progress.toMin();
690     
691     std::list<RepoInfo> repos = knownRepositories();
692     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
693           it != repos.end();
694           ++it )
695     {
696       if ( info.alias() == (*it).alias() )
697         ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
698     }
699
700     progress.set(50);
701     
702     // assert the directory exists
703     filesystem::assert_dir(_pimpl->options.knownReposPath);
704     
705     Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath,
706                                                     Pathname(info.alias()).extend(".repo").asString());
707     // now we have a filename that does not exists
708     MIL << "Saving repo in " << repofile << endl;
709
710     std::ofstream file(repofile.c_str());
711     if (!file) {
712       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
713     }
714
715     info.dumpRepoOn(file);
716     progress.toMax();
717     MIL << "done" << endl;
718   }
719
720   void RepoManager::addRepositories( const Url &url,
721                                      const ProgressData::ReceiverFnc & progressrcv )
722   {
723     std::list<RepoInfo> knownrepos = knownRepositories();
724     std::list<RepoInfo> repos = readRepoFile(url);
725     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
726           it != repos.end();
727           ++it )
728     {
729       // look if the alias is in the known repos.
730       for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
731           kit != knownrepos.end();
732           ++kit )
733       {
734         if ( (*it).alias() == (*kit).alias() )
735         {
736           ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
737           ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
738         }
739       }
740     }
741
742     string filename = Pathname(url.getPathName()).basename();
743
744     if ( filename == Pathname() )
745       ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
746
747     // assert the directory exists
748     filesystem::assert_dir(_pimpl->options.knownReposPath);
749     
750     Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath, filename);
751     // now we have a filename that does not exists
752     MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
753
754     std::ofstream file(repofile.c_str());
755     if (!file) {
756       ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
757     }
758
759     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
760           it != repos.end();
761           ++it )
762     {
763       MIL << "Saving " << (*it).alias() << endl;
764       (*it).dumpRepoOn(file);
765     }
766     MIL << "done" << endl;
767   }
768
769   ////////////////////////////////////////////////////////////////////////////
770
771   void RepoManager::removeRepository( const RepoInfo & info,
772                                       const ProgressData::ReceiverFnc & progressrcv)
773   {
774     MIL << "Going to delete repo " << info.alias() << endl;
775
776     std::list<RepoInfo> repos = knownRepositories();
777     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
778           it != repos.end();
779           ++it )
780     {
781       // they can be the same only if the provided is empty, that means
782       // the provided repo has no alias
783       // then skip
784       if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
785         continue;
786
787       // TODO match by url
788
789       // we have a matcing repository, now we need to know
790       // where it does come from.
791       RepoInfo todelete = *it;
792       if (todelete.filepath().empty())
793       {
794         ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
795       }
796       else
797       {
798         // figure how many repos are there in the file:
799         std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
800         if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
801         {
802           // easy, only this one, just delete the file
803           if ( filesystem::unlink(todelete.filepath()) != 0 )
804           {
805             ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
806           }
807           MIL << todelete.alias() << " sucessfully deleted." << endl;
808           return;
809         }
810         else
811         {
812           // there are more repos in the same file
813           // write them back except the deleted one.
814           //TmpFile tmp;
815           //std::ofstream file(tmp.path().c_str());
816           
817           // assert the directory exists
818           filesystem::assert_dir(todelete.filepath().dirname());
819           
820           std::ofstream file(todelete.filepath().c_str());
821           if (!file) {
822             //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
823             ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
824           }
825           for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
826                 fit != filerepos.end();
827                 ++fit )
828           {
829             if ( (*fit).alias() != todelete.alias() )
830               (*fit).dumpRepoOn(file);
831           }
832
833           cache::CacheStore store(_pimpl->options.repoCachePath);
834
835           if ( store.isCached( todelete.alias() ) ) {
836             MIL << "repository was cached. cleaning cache" << endl;
837             store.cleanRepository(todelete.alias());
838           }
839
840           MIL << todelete.alias() << " sucessfully deleted." << endl;
841           return;
842         }
843       } // else filepath is empty
844
845     }
846     // should not be reached on a sucess workflow
847     ZYPP_THROW(RepoNotFoundException(info));
848   }
849
850   ////////////////////////////////////////////////////////////////////////////
851
852   void RepoManager::modifyRepository( const std::string &alias,
853                                       const RepoInfo & newinfo,
854                                       const ProgressData::ReceiverFnc & progressrcv )
855   {
856     RepoInfo toedit = getRepositoryInfo(alias);
857
858     if (toedit.filepath().empty())
859     {
860       ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
861     }
862     else
863     {
864       // figure how many repos are there in the file:
865       std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
866
867       // there are more repos in the same file
868       // write them back except the deleted one.
869       //TmpFile tmp;
870       //std::ofstream file(tmp.path().c_str());
871       
872       // assert the directory exists
873       filesystem::assert_dir(toedit.filepath().dirname());
874       
875       std::ofstream file(toedit.filepath().c_str());
876       if (!file) {
877         //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
878         ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
879       }
880       for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
881             fit != filerepos.end();
882             ++fit )
883       {
884           // if the alias is different, dump the original
885           // if it is the same, dump the provided one
886           if ( (*fit).alias() != toedit.alias() )
887             (*fit).dumpRepoOn(file);
888           else
889             newinfo.dumpRepoOn(file);
890       }
891     }
892   }
893
894   ////////////////////////////////////////////////////////////////////////////
895
896   RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
897                                            const ProgressData::ReceiverFnc & progressrcv )
898   {
899     std::list<RepoInfo> repos = knownRepositories();
900     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
901           it != repos.end();
902           ++it )
903     {
904       if ( (*it).alias() == alias )
905         return *it;
906     }
907     RepoInfo info;
908     info.setAlias(info.alias());
909     ZYPP_THROW(RepoNotFoundException(info));
910   }
911
912   ////////////////////////////////////////////////////////////////////////////
913
914   RepoInfo RepoManager::getRepositoryInfo( const Url & url,
915                                            const url::ViewOption & urlview,
916                                            const ProgressData::ReceiverFnc & progressrcv )
917   {
918     std::list<RepoInfo> repos = knownRepositories();
919     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
920           it != repos.end();
921           ++it )
922     {
923       for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
924           urlit != (*it).baseUrlsEnd();
925           ++urlit)
926       {
927         if ((*urlit).asString(urlview) == url.asString(urlview))
928           return *it;
929       }
930     }
931     RepoInfo info;
932     info.setAlias(info.alias());
933     info.setBaseUrl(url);
934     ZYPP_THROW(RepoNotFoundException(info));
935   }
936
937   ////////////////////////////////////////////////////////////////////////////
938
939   std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
940   {
941     return str << *obj._pimpl;
942   }
943
944   /////////////////////////////////////////////////////////////////
945 } // namespace zypp
946 ///////////////////////////////////////////////////////////////////