1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/RepoManager.cc
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/base/Regex.h"
22 #include "zypp/PathInfo.h"
23 #include "zypp/TmpPath.h"
25 #include "zypp/repo/RepoException.h"
26 #include "zypp/RepoManager.h"
28 #include "zypp/cache/CacheStore.h"
29 #include "zypp/repo/cached/RepoImpl.h"
30 #include "zypp/media/MediaManager.h"
31 #include "zypp/MediaSetAccess.h"
33 #include "zypp/parser/RepoFileReader.h"
34 #include "zypp/repo/yum/Downloader.h"
35 #include "zypp/parser/yum/RepoParser.h"
36 #include "zypp/parser/plaindir/RepoParser.h"
37 #include "zypp/repo/susetags/Downloader.h"
38 #include "zypp/parser/susetags/RepoParser.h"
40 #include "zypp/ZYppCallbacks.h"
44 using namespace zypp::repo;
45 using namespace zypp::filesystem;
47 using namespace zypp::repo;
49 ///////////////////////////////////////////////////////////////////
51 { /////////////////////////////////////////////////////////////////
53 ///////////////////////////////////////////////////////////////////
55 // CLASS NAME : RepoManagerOptions
57 ///////////////////////////////////////////////////////////////////
59 RepoManagerOptions::RepoManagerOptions()
61 repoCachePath = ZConfig::instance().repoCachePath();
62 repoRawCachePath = ZConfig::instance().repoMetadataPath();
63 knownReposPath = ZConfig::instance().knownReposPath();
66 ////////////////////////////////////////////////////////////////////////////
69 * \short Simple callback to collect the results
71 * Classes like RepoFileParser call the callback
72 * once per each repo in a file.
74 * Passing this functor as callback, you can collect
75 * all resuls at the end, without dealing with async
90 bool collect( const RepoInfo &repo )
92 //MIL << "here in collector: " << repo.alias() << endl;
93 repos.push_back(repo);
94 //MIL << "added: " << repo.alias() << endl;
101 ////////////////////////////////////////////////////////////////////////////
104 * \short Internal version of clean cache
106 * Takes an extra CacheStore reference, so we avoid internally
107 * having 2 CacheStores writing to the same database.
109 static void cleanCacheInternal( cache::CacheStore &store,
110 const RepoInfo &info,
111 const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() )
113 ProgressData progress;
114 callback::SendReport<ProgressReport> report;
115 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
116 progress.name(str::form(_("Cleaning repository '%s' cache"), info.name().c_str()));
118 if ( !store.isCached(info.alias()) )
121 MIL << info.alias() << " cleaning cache..." << endl;
122 data::RecordId id = store.lookupRepository(info.alias());
124 CombinedProgressData subprogrcv(progress);
126 store.cleanRepository(id, subprogrcv);
129 ////////////////////////////////////////////////////////////////////////////
132 * Reads RepoInfo's from a repo file.
134 * \param file pathname of the file to read.
136 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
138 MIL << "repo file: " << file << endl;
139 RepoCollector collector;
140 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
141 return collector.repos;
144 ////////////////////////////////////////////////////////////////////////////
146 std::list<RepoInfo> readRepoFile(const Url & repo_file)
148 // no interface to download a specific file, using workaround:
149 //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
151 Pathname path(url.getPathName());
152 url.setPathName ("/");
153 MediaSetAccess access(url);
154 Pathname local = access.provideFile(path);
156 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
158 return repositories_in_file(local);
161 ////////////////////////////////////////////////////////////////////////////
164 * \short List of RepoInfo's from a directory
166 * Goes trough every file ending with ".repo" in a directory and adds all
167 * RepoInfo's contained in that file.
169 * \param dir pathname of the directory to read.
171 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
173 MIL << "directory " << dir << endl;
174 list<RepoInfo> repos;
175 list<Pathname> entries;
176 if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
177 ZYPP_THROW(Exception("failed to read directory"));
179 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
180 for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
182 if (str::regex_match(it->extension(), allowedRepoExt))
184 list<RepoInfo> tmp = repositories_in_file( *it );
185 repos.insert( repos.end(), tmp.begin(), tmp.end() );
187 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
188 //MIL << "ok" << endl;
194 ////////////////////////////////////////////////////////////////////////////
196 static void assert_alias( const RepoInfo &info )
198 if (info.alias().empty())
199 ZYPP_THROW(RepoNoAliasException());
202 ////////////////////////////////////////////////////////////////////////////
204 static void assert_urls( const RepoInfo &info )
206 if (info.baseUrlsEmpty())
207 ZYPP_THROW(RepoNoUrlException());
210 ////////////////////////////////////////////////////////////////////////////
213 * \short Calculates the raw cache path for a repository
215 static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
218 return opt.repoRawCachePath + info.alias();
221 ///////////////////////////////////////////////////////////////////
223 // CLASS NAME : RepoManager::Impl
225 ///////////////////////////////////////////////////////////////////
228 * \short RepoManager implementation.
230 struct RepoManager::Impl
232 Impl( const RepoManagerOptions &opt )
243 RepoManagerOptions options;
246 /** Offer default Impl. */
247 static shared_ptr<Impl> nullimpl()
249 static shared_ptr<Impl> _nullimpl( new Impl );
254 friend Impl * rwcowClone<Impl>( const Impl * rhs );
255 /** clone for RWCOW_pointer */
257 { return new Impl( *this ); }
259 ///////////////////////////////////////////////////////////////////
261 /** \relates RepoManager::Impl Stream output */
262 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
264 return str << "RepoManager::Impl";
267 ///////////////////////////////////////////////////////////////////
269 // CLASS NAME : RepoManager
271 ///////////////////////////////////////////////////////////////////
273 RepoManager::RepoManager( const RepoManagerOptions &opt )
274 : _pimpl( new Impl(opt) )
277 ////////////////////////////////////////////////////////////////////////////
279 RepoManager::~RepoManager()
282 ////////////////////////////////////////////////////////////////////////////
284 std::list<RepoInfo> RepoManager::knownRepositories() const
288 if ( PathInfo(_pimpl->options.knownReposPath).isExist() )
290 RepoInfoList repos = repositories_in_dir(_pimpl->options.knownReposPath);
291 for ( RepoInfoList::iterator it = repos.begin();
295 // set the metadata path for the repo
296 Pathname metadata_path = rawcache_path_for_repoinfo(_pimpl->options, (*it));
297 (*it).setMetadataPath(metadata_path);
302 return std::list<RepoInfo>();
307 ////////////////////////////////////////////////////////////////////////////
309 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
311 return rawcache_path_for_repoinfo(_pimpl->options, info );
314 ////////////////////////////////////////////////////////////////////////////
316 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
318 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
319 RepoType repokind = info.type();
322 switch ( repokind.toEnum() )
324 case RepoType::NONE_e:
325 // unknown, probe the local metadata
326 repokind = probe(rawpath.asUrl());
332 switch ( repokind.toEnum() )
334 case RepoType::RPMMD_e :
336 status = RepoStatus( rawpath + "/repodata/repomd.xml");
340 case RepoType::YAST2_e :
342 // the order of RepoStatus && RepoStatus matters! (#304310)
343 status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
347 case RepoType::RPMPLAINDIR_e :
349 if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
350 status = RepoStatus( rawpath + "/cookie");
354 case RepoType::NONE_e :
355 // Return default RepoStatus in case of RepoType::NONE
356 // indicating it should be created?
357 // ZYPP_THROW(RepoUnknownTypeException());
363 void RepoManager::touchIndexFile(const RepoInfo & info)
365 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
367 RepoType repokind = info.type();
368 if ( repokind.toEnum() == RepoType::NONE_e )
369 // unknown, probe the local metadata
370 repokind = probe(rawpath.asUrl());
371 // if still unknown, just return
372 if (repokind == RepoType::NONE_e)
376 switch ( repokind.toEnum() )
378 case RepoType::RPMMD_e :
379 p = Pathname(rawpath + "/repodata/repomd.xml");
382 case RepoType::YAST2_e :
383 p = Pathname(rawpath + "/content");
386 case RepoType::RPMPLAINDIR_e :
387 p = Pathname(rawpath + "/cookie");
390 case RepoType::NONE_e :
395 // touch the file, ignore error (they are logged anyway)
396 filesystem::touch(p);
399 bool RepoManager::checkIfToRefreshMetadata( const RepoInfo &info,
401 RawMetadataRefreshPolicy policy )
405 RepoStatus oldstatus;
406 RepoStatus newstatus;
410 MIL << "Going to try to check whether refresh is needed for " << url << endl;
412 repo::RepoType repokind = info.type();
414 // if the type is unknown, try probing.
415 switch ( repokind.toEnum() )
417 case RepoType::NONE_e:
419 repokind = probe(url);
425 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
426 filesystem::assert_dir(rawpath);
427 oldstatus = metadataStatus(info);
429 // now we've got the old (cached) status, we can decide repo.refresh.delay
430 if (policy != RefreshForced)
432 // difference in seconds
433 double diff = difftime(
434 (Date::ValueType)Date::now(),
435 (Date::ValueType)oldstatus.timestamp()) / 60;
437 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
438 DBG << "current time: " << (Date::ValueType)Date::now() << endl;
439 DBG << "last refresh = " << diff << " minutes ago" << endl;
441 if (diff < ZConfig::instance().repo_refresh_delay())
443 MIL << "Repository '" << info.alias()
444 << "' has been refreshed less than repo.refresh.delay ("
445 << ZConfig::instance().repo_refresh_delay()
446 << ") minutes ago. Advising to skip refresh" << endl;
451 // create temp dir as sibling of rawpath
452 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
454 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
455 ( repokind.toEnum() == RepoType::YAST2_e ) )
457 MediaSetAccess media(url);
458 shared_ptr<repo::Downloader> downloader_ptr;
460 if ( repokind.toEnum() == RepoType::RPMMD_e )
461 downloader_ptr.reset(new yum::Downloader(info.path()));
463 downloader_ptr.reset( new susetags::Downloader(info.path()));
465 RepoStatus newstatus = downloader_ptr->status(media);
466 bool refresh = false;
467 if ( oldstatus.checksum() == newstatus.checksum() )
469 MIL << "repo has not changed" << endl;
470 if ( policy == RefreshForced )
472 MIL << "refresh set to forced" << endl;
478 MIL << "repo has changed, going to refresh" << endl;
483 touchIndexFile(info);
487 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
489 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
490 bool refresh = false;
491 if ( oldstatus.checksum() == newstatus.checksum() )
493 MIL << "repo has not changed" << endl;
494 if ( policy == RefreshForced )
496 MIL << "refresh set to forced" << endl;
502 MIL << "repo has changed, going to refresh" << endl;
507 touchIndexFile(info);
513 ZYPP_THROW(RepoUnknownTypeException());
516 catch ( const Exception &e )
519 ERR << "refresh check failed for " << url << endl;
523 return true; // default
526 void RepoManager::refreshMetadata( const RepoInfo &info,
527 RawMetadataRefreshPolicy policy,
528 const ProgressData::ReceiverFnc & progress )
533 // we will throw this later if no URL checks out fine
534 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
536 // try urls one by one
537 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
543 // check whether to refresh metadata
544 // if the check fails for this url, it throws, so another url will be checked
545 if (!checkIfToRefreshMetadata(info, url, policy))
548 MIL << "Going to refresh metadata from " << url << endl;
550 repo::RepoType repokind = info.type();
552 // if the type is unknown, try probing.
553 switch ( repokind.toEnum() )
555 case RepoType::NONE_e:
557 repokind = probe(*it);
563 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
564 filesystem::assert_dir(rawpath);
566 // create temp dir as sibling of rawpath
567 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
569 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
570 ( repokind.toEnum() == RepoType::YAST2_e ) )
572 MediaSetAccess media(url);
573 shared_ptr<repo::Downloader> downloader_ptr;
575 if ( repokind.toEnum() == RepoType::RPMMD_e )
576 downloader_ptr.reset(new yum::Downloader(info.path()));
578 downloader_ptr.reset( new susetags::Downloader(info.path()));
581 * Given a downloader, sets the other repos raw metadata
582 * path as cache paths for the fetcher, so if another
583 * repo has the same file, it will not download it
584 * but copy it from the other repository
586 std::list<RepoInfo> repos = knownRepositories();
587 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
591 downloader_ptr->addCachePath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
594 downloader_ptr->download( media, tmpdir.path());
596 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
598 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
600 std::ofstream file(( tmpdir.path() + "/cookie").c_str());
602 ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
605 file << newstatus.checksum() << endl;
611 ZYPP_THROW(RepoUnknownTypeException());
614 // ok we have the metadata, now exchange
616 TmpDir oldmetadata( TmpDir::makeSibling( rawpath ) );
617 filesystem::rename( rawpath, oldmetadata.path() );
618 // move the just downloaded there
619 filesystem::rename( tmpdir.path(), rawpath );
623 catch ( const Exception &e )
626 ERR << "Trying another url..." << endl;
628 // remember the exception caught for the *first URL*
629 // if all other URLs fail, the rexception will be thrown with the
630 // cause of the problem of the first URL remembered
631 if (it == info.baseUrlsBegin())
632 rexception.remember(e);
635 ERR << "No more urls..." << endl;
636 ZYPP_THROW(rexception);
639 ////////////////////////////////////////////////////////////////////////////
641 void RepoManager::cleanMetadata( const RepoInfo &info,
642 const ProgressData::ReceiverFnc & progressfnc )
644 ProgressData progress(100);
645 progress.sendTo(progressfnc);
647 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
651 void RepoManager::buildCache( const RepoInfo &info,
652 CacheBuildPolicy policy,
653 const ProgressData::ReceiverFnc & progressrcv )
656 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
658 cache::CacheStore store(_pimpl->options.repoCachePath);
660 RepoStatus raw_metadata_status = metadataStatus(info);
661 if ( raw_metadata_status.empty() )
663 ZYPP_THROW(RepoMetadataException(info));
666 bool needs_cleaning = false;
667 if ( store.isCached( info.alias() ) )
669 MIL << info.alias() << " is already cached." << endl;
670 data::RecordId id = store.lookupRepository(info.alias());
671 RepoStatus cache_status = store.repositoryStatus(id);
673 if ( cache_status.checksum() == raw_metadata_status.checksum() )
675 MIL << info.alias() << " cache is up to date with metadata." << endl;
676 if ( policy == BuildIfNeeded ) {
680 MIL << info.alias() << " cache rebuild is forced" << endl;
684 needs_cleaning = true;
687 ProgressData progress(100);
688 callback::SendReport<ProgressReport> report;
689 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
690 progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
694 cleanCacheInternal( store, info);
696 MIL << info.alias() << " building cache..." << endl;
697 data::RecordId id = store.lookupOrAppendRepository(info.alias());
699 repo::RepoType repokind = info.type();
701 // if the type is unknown, try probing.
702 switch ( repokind.toEnum() )
704 case RepoType::NONE_e:
705 // unknown, probe the local metadata
706 repokind = probe(rawpath.asUrl());
713 switch ( repokind.toEnum() )
715 case RepoType::RPMMD_e :
717 CombinedProgressData subprogrcv( progress, 100);
718 parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
719 parser.parse(rawpath);
723 case RepoType::YAST2_e :
725 CombinedProgressData subprogrcv( progress, 100);
726 parser::susetags::RepoParser parser(id, store, subprogrcv);
727 parser.parse(rawpath);
731 case RepoType::RPMPLAINDIR_e :
733 CombinedProgressData subprogrcv( progress, 100);
734 InputStream is(rawpath + "cookie");
736 getline( is.stream(), buffer);
738 parser::plaindir::RepoParser parser(id, store, subprogrcv);
739 parser.parse(url.getPathName());
743 ZYPP_THROW(RepoUnknownTypeException());
746 // update timestamp and checksum
747 store.updateRepositoryStatus(id, raw_metadata_status);
749 MIL << "Commit cache.." << endl;
754 ////////////////////////////////////////////////////////////////////////////
756 repo::RepoType RepoManager::probe( const Url &url ) const
758 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
760 // Handle non existing local directory in advance, as
761 // MediaSetAccess does not support it.
762 return repo::RepoType::NONE;
767 MediaSetAccess access(url);
768 if ( access.doesFileExist("/repodata/repomd.xml") )
769 return repo::RepoType::RPMMD;
770 if ( access.doesFileExist("/content") )
771 return repo::RepoType::YAST2;
773 // if it is a local url of type dir
774 if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
776 Pathname path = Pathname(url.getPathName());
777 if ( PathInfo(path).isDir() )
779 // allow empty dirs for now
780 return repo::RepoType::RPMPLAINDIR;
784 catch ( const media::MediaException &e )
787 RepoException enew("Error trying to read from " + url.asString());
791 catch ( const Exception &e )
794 Exception enew("Unknown error reading from " + url.asString());
799 return repo::RepoType::NONE;
802 ////////////////////////////////////////////////////////////////////////////
804 void RepoManager::cleanCache( const RepoInfo &info,
805 const ProgressData::ReceiverFnc & progressrcv )
807 cache::CacheStore store(_pimpl->options.repoCachePath);
808 cleanCacheInternal( store, info, progressrcv );
812 ////////////////////////////////////////////////////////////////////////////
814 bool RepoManager::isCached( const RepoInfo &info ) const
816 cache::CacheStore store(_pimpl->options.repoCachePath);
817 return store.isCached(info.alias());
820 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
822 cache::CacheStore store(_pimpl->options.repoCachePath);
823 data::RecordId id = store.lookupRepository(info.alias());
824 RepoStatus cache_status = store.repositoryStatus(id);
828 Repository RepoManager::createFromCache( const RepoInfo &info,
829 const ProgressData::ReceiverFnc & progressrcv )
831 callback::SendReport<ProgressReport> report;
832 ProgressData progress;
833 progress.sendTo(ProgressReportAdaptor( progressrcv, report ));
834 //progress.sendTo( progressrcv );
835 progress.name(str::form(_("Reading repository '%s' cache"), info.name().c_str()));
837 cache::CacheStore store(_pimpl->options.repoCachePath);
839 if ( ! store.isCached( info.alias() ) )
840 ZYPP_THROW(RepoNotCachedException());
842 MIL << "Repository " << info.alias() << " is cached" << endl;
844 data::RecordId id = store.lookupRepository(info.alias());
846 CombinedProgressData subprogrcv(progress);
848 repo::cached::RepoOptions opts( info, _pimpl->options.repoCachePath, id );
849 opts.readingResolvablesProgress = subprogrcv;
850 repo::cached::RepoImpl::Ptr repoimpl =
851 new repo::cached::RepoImpl( opts );
853 repoimpl->resolvables();
854 // read the resolvables from cache
855 return Repository(repoimpl);
858 ////////////////////////////////////////////////////////////////////////////
861 * Generate a non existing filename in a directory, using a base
862 * name. For example if a directory contains 3 files
868 * If you try to generate a unique filename for this directory,
869 * based on "ruu" you will get "ruu", but if you use the base
870 * "foo" you will get "foo_1"
872 * \param dir Directory where the file needs to be unique
873 * \param basefilename string to base the filename on.
875 static Pathname generate_non_existing_name( const Pathname &dir,
876 const std::string &basefilename )
878 string final_filename = basefilename;
880 while ( PathInfo(dir + final_filename).isExist() )
882 final_filename = basefilename + "_" + str::numstring(counter);
885 return dir + Pathname(final_filename);
888 ////////////////////////////////////////////////////////////////////////////
891 * \short Generate a related filename from a repo info
893 * From a repo info, it will try to use the alias as a filename
894 * escaping it if necessary. Other fallbacks can be added to
895 * this function in case there is no way to use the alias
897 static std::string generate_filename( const RepoInfo &info )
901 std::string filename = info.alias();
902 // replace slashes with underscores
903 size_t pos = filename.find(fnd);
904 while(pos!=string::npos)
906 filename.replace(pos,fnd.length(),rep);
907 pos = filename.find(fnd,pos+rep.length());
909 filename = Pathname(filename).extend(".repo").asString();
910 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
915 ////////////////////////////////////////////////////////////////////////////
917 void RepoManager::addRepository( const RepoInfo &info,
918 const ProgressData::ReceiverFnc & progressrcv )
922 ProgressData progress(100);
923 callback::SendReport<ProgressReport> report;
924 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
925 progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
928 std::list<RepoInfo> repos = knownRepositories();
929 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
933 if ( info.alias() == (*it).alias() )
934 ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
937 RepoInfo tosave = info;
939 // check the first url for now
940 if ( ZConfig::instance().repo_add_probe()
941 || ( tosave.type() == RepoType::NONE && tosave.enabled()) )
943 DBG << "unknown repository type, probing" << endl;
946 probedtype = probe(*tosave.baseUrlsBegin());
947 if ( tosave.baseUrlsSize() > 0 )
949 if ( probedtype == RepoType::NONE )
950 ZYPP_THROW(RepoUnknownTypeException());
952 tosave.setType(probedtype);
958 // assert the directory exists
959 filesystem::assert_dir(_pimpl->options.knownReposPath);
961 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath,
962 generate_filename(tosave));
963 // now we have a filename that does not exists
964 MIL << "Saving repo in " << repofile << endl;
966 std::ofstream file(repofile.c_str());
968 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
971 tosave.dumpRepoOn(file);
973 MIL << "done" << endl;
976 void RepoManager::addRepositories( const Url &url,
977 const ProgressData::ReceiverFnc & progressrcv )
979 std::list<RepoInfo> knownrepos = knownRepositories();
980 std::list<RepoInfo> repos = readRepoFile(url);
981 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
985 // look if the alias is in the known repos.
986 for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
987 kit != knownrepos.end();
990 if ( (*it).alias() == (*kit).alias() )
992 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
993 ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
998 string filename = Pathname(url.getPathName()).basename();
1000 if ( filename == Pathname() )
1001 ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
1003 // assert the directory exists
1004 filesystem::assert_dir(_pimpl->options.knownReposPath);
1006 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath, filename);
1007 // now we have a filename that does not exists
1008 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1010 std::ofstream file(repofile.c_str());
1012 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1015 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1019 MIL << "Saving " << (*it).alias() << endl;
1020 (*it).dumpRepoOn(file);
1022 MIL << "done" << endl;
1025 ////////////////////////////////////////////////////////////////////////////
1027 void RepoManager::removeRepository( const RepoInfo & info,
1028 const ProgressData::ReceiverFnc & progressrcv)
1030 ProgressData progress;
1031 callback::SendReport<ProgressReport> report;
1032 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1033 progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
1035 MIL << "Going to delete repo " << info.alias() << endl;
1037 std::list<RepoInfo> repos = knownRepositories();
1038 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1042 // they can be the same only if the provided is empty, that means
1043 // the provided repo has no alias
1045 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1048 // TODO match by url
1050 // we have a matcing repository, now we need to know
1051 // where it does come from.
1052 RepoInfo todelete = *it;
1053 if (todelete.filepath().empty())
1055 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1059 // figure how many repos are there in the file:
1060 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1061 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
1063 // easy, only this one, just delete the file
1064 if ( filesystem::unlink(todelete.filepath()) != 0 )
1066 ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
1068 MIL << todelete.alias() << " sucessfully deleted." << endl;
1072 // there are more repos in the same file
1073 // write them back except the deleted one.
1075 //std::ofstream file(tmp.path().c_str());
1077 // assert the directory exists
1078 filesystem::assert_dir(todelete.filepath().dirname());
1080 std::ofstream file(todelete.filepath().c_str());
1082 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1083 ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
1085 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1086 fit != filerepos.end();
1089 if ( (*fit).alias() != todelete.alias() )
1090 (*fit).dumpRepoOn(file);
1094 CombinedProgressData subprogrcv(progress, 70);
1095 CombinedProgressData cleansubprogrcv(progress, 30);
1096 // now delete it from cache
1097 cleanCache( todelete, subprogrcv);
1098 // now delete metadata (#301037)
1099 cleanMetadata( todelete, cleansubprogrcv);
1100 MIL << todelete.alias() << " sucessfully deleted." << endl;
1102 } // else filepath is empty
1105 // should not be reached on a sucess workflow
1106 ZYPP_THROW(RepoNotFoundException(info));
1109 ////////////////////////////////////////////////////////////////////////////
1111 void RepoManager::modifyRepository( const std::string &alias,
1112 const RepoInfo & newinfo,
1113 const ProgressData::ReceiverFnc & progressrcv )
1115 RepoInfo toedit = getRepositoryInfo(alias);
1117 if (toedit.filepath().empty())
1119 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1123 // figure how many repos are there in the file:
1124 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1126 // there are more repos in the same file
1127 // write them back except the deleted one.
1129 //std::ofstream file(tmp.path().c_str());
1131 // assert the directory exists
1132 filesystem::assert_dir(toedit.filepath().dirname());
1134 std::ofstream file(toedit.filepath().c_str());
1136 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1137 ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
1139 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1140 fit != filerepos.end();
1143 // if the alias is different, dump the original
1144 // if it is the same, dump the provided one
1145 if ( (*fit).alias() != toedit.alias() )
1146 (*fit).dumpRepoOn(file);
1148 newinfo.dumpRepoOn(file);
1153 ////////////////////////////////////////////////////////////////////////////
1155 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
1156 const ProgressData::ReceiverFnc & progressrcv )
1158 std::list<RepoInfo> repos = knownRepositories();
1159 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1163 if ( (*it).alias() == alias )
1167 info.setAlias(info.alias());
1168 ZYPP_THROW(RepoNotFoundException(info));
1171 ////////////////////////////////////////////////////////////////////////////
1173 RepoInfo RepoManager::getRepositoryInfo( const Url & url,
1174 const url::ViewOption & urlview,
1175 const ProgressData::ReceiverFnc & progressrcv )
1177 std::list<RepoInfo> repos = knownRepositories();
1178 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1182 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
1183 urlit != (*it).baseUrlsEnd();
1186 if ((*urlit).asString(urlview) == url.asString(urlview))
1191 info.setAlias(info.alias());
1192 info.setBaseUrl(url);
1193 ZYPP_THROW(RepoNotFoundException(info));
1196 ////////////////////////////////////////////////////////////////////////////
1198 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
1200 return str << *obj._pimpl;
1203 /////////////////////////////////////////////////////////////////
1205 ///////////////////////////////////////////////////////////////////