00001
00002
00003
00004
00005
00006
00007
00008
00013 #include <cstdlib>
00014 #include <iostream>
00015 #include <fstream>
00016 #include <sstream>
00017 #include <list>
00018 #include <map>
00019 #include <algorithm>
00020
00021 #include "zypp/base/InputStream.h"
00022 #include "zypp/base/LogTools.h"
00023 #include "zypp/base/Gettext.h"
00024 #include "zypp/base/Function.h"
00025 #include "zypp/base/Regex.h"
00026 #include "zypp/PathInfo.h"
00027 #include "zypp/TmpPath.h"
00028
00029 #include "zypp/ServiceInfo.h"
00030 #include "zypp/repo/RepoException.h"
00031 #include "zypp/RepoManager.h"
00032
00033 #include "zypp/media/MediaManager.h"
00034 #include "zypp/media/CredentialManager.h"
00035 #include "zypp/MediaSetAccess.h"
00036 #include "zypp/ExternalProgram.h"
00037 #include "zypp/ManagedFile.h"
00038
00039 #include "zypp/parser/RepoFileReader.h"
00040 #include "zypp/parser/ServiceFileReader.h"
00041 #include "zypp/repo/ServiceRepos.h"
00042 #include "zypp/repo/yum/Downloader.h"
00043 #include "zypp/repo/susetags/Downloader.h"
00044 #include "zypp/parser/plaindir/RepoParser.h"
00045 #include "zypp/repo/PluginServices.h"
00046
00047 #include "zypp/Target.h"
00048 #include "zypp/ZYppFactory.h"
00049 #include "zypp/HistoryLog.h"
00050
00051 #include "zypp/ZYppCallbacks.h"
00052
00053 #include "sat/Pool.h"
00054
00055 using std::endl;
00056 using std::string;
00057 using namespace zypp::repo;
00058
00060 namespace zypp
00061 {
00062
00063 namespace
00064 {
00068 class MediaMounter
00069 {
00070 public:
00072 MediaMounter( const Url & url_r )
00073 {
00074 media::MediaManager mediamanager;
00075 _mid = mediamanager.open( url_r );
00076 mediamanager.attach( _mid );
00077 }
00078
00080 ~MediaMounter()
00081 {
00082 media::MediaManager mediamanager;
00083 mediamanager.release( _mid );
00084 mediamanager.close( _mid );
00085 }
00086
00091 Pathname getPathName( const Pathname & path_r = Pathname() ) const
00092 {
00093 media::MediaManager mediamanager;
00094 return mediamanager.localPath( _mid, path_r );
00095 }
00096
00097 private:
00098 media::MediaAccessId _mid;
00099 };
00100
00102 template <class Iterator>
00103 inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r )
00104 {
00105 for_( it, begin_r, end_r )
00106 if ( it->alias() == alias_r )
00107 return true;
00108 return false;
00109 }
00111 template <class Container>
00112 inline bool foundAliasIn( const std::string & alias_r, const Container & cont_r )
00113 { return foundAliasIn( alias_r, cont_r.begin(), cont_r.end() ); }
00114
00116 template <class Iterator>
00117 inline Iterator findAlias( const std::string & alias_r, Iterator begin_r, Iterator end_r )
00118 {
00119 for_( it, begin_r, end_r )
00120 if ( it->alias() == alias_r )
00121 return it;
00122 return end_r;
00123 }
00125 template <class Container>
00126 inline typename Container::iterator findAlias( const std::string & alias_r, Container & cont_r )
00127 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
00129 template <class Container>
00130 inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r )
00131 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
00132 }
00133
00135
00136
00137
00139
00140 RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
00141 {
00142 repoCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
00143 repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
00144 repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
00145 repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
00146 knownReposPath = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
00147 knownServicesPath = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
00148 probe = ZConfig::instance().repo_add_probe();
00149
00150 rootDir = root_r;
00151 }
00152
00153 RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
00154 {
00155 RepoManagerOptions ret;
00156 ret.repoCachePath = root_r;
00157 ret.repoRawCachePath = root_r/"raw";
00158 ret.repoSolvCachePath = root_r/"solv";
00159 ret.repoPackagesCachePath = root_r/"packages";
00160 ret.knownReposPath = root_r/"repos.d";
00161 ret.knownServicesPath = root_r/"services.d";
00162 ret.rootDir = root_r;
00163 return ret;
00164 }
00165
00166 Pathname RepoManagerOptions::pluginsPath() const
00167 {
00168 return repoCachePath == rootDir ? rootDir/"plugins"
00169 : Pathname::assertprefix( rootDir, ZConfig::instance().pluginsPath() );
00170 }
00171
00173
00189 struct RepoCollector : private base::NonCopyable
00190 {
00191 RepoCollector()
00192 {}
00193
00194 RepoCollector(const std::string & targetDistro_)
00195 : targetDistro(targetDistro_)
00196 {}
00197
00198 bool collect( const RepoInfo &repo )
00199 {
00200
00201 if (!targetDistro.empty()
00202 && !repo.targetDistribution().empty()
00203 && repo.targetDistribution() != targetDistro)
00204 {
00205 MIL
00206 << "Skipping repository meant for '" << targetDistro
00207 << "' distribution (current distro is '"
00208 << repo.targetDistribution() << "')." << endl;
00209
00210 return true;
00211 }
00212
00213 repos.push_back(repo);
00214 return true;
00215 }
00216
00217 RepoInfoList repos;
00218 std::string targetDistro;
00219 };
00220
00222
00228 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
00229 {
00230 MIL << "repo file: " << file << endl;
00231 RepoCollector collector;
00232 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
00233 return collector.repos;
00234 }
00235
00237
00246 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
00247 {
00248 MIL << "directory " << dir << endl;
00249 std::list<RepoInfo> repos;
00250 std::list<Pathname> entries;
00251 if ( filesystem::readdir( entries, dir, false ) != 0 )
00252 {
00253
00254 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
00255 }
00256
00257 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
00258 for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
00259 {
00260 if (str::regex_match(it->extension(), allowedRepoExt))
00261 {
00262 std::list<RepoInfo> tmp = repositories_in_file( *it );
00263 repos.insert( repos.end(), tmp.begin(), tmp.end() );
00264
00265
00266
00267 }
00268 }
00269 return repos;
00270 }
00271
00273
00274 std::list<RepoInfo> readRepoFile(const Url & repo_file)
00275 {
00276
00278 Url url(repo_file);
00279 Pathname path(url.getPathName());
00280 url.setPathName ("/");
00281 MediaSetAccess access(url);
00282 Pathname local = access.provideFile(path);
00283
00284 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
00285
00286 return repositories_in_file(local);
00287 }
00288
00290
00291 inline void assert_alias( const RepoInfo & info )
00292 {
00293 if ( info.alias().empty() )
00294 ZYPP_THROW( RepoNoAliasException() );
00295
00296
00297 if ( info.alias()[0] == '.')
00298 ZYPP_THROW(RepoInvalidAliasException(
00299 info, _("Repository alias cannot start with dot.")));
00300 }
00301
00302 inline void assert_alias( const ServiceInfo & info )
00303 {
00304 if ( info.alias().empty() )
00305 ZYPP_THROW( ServiceNoAliasException() );
00306
00307
00308 if ( info.alias()[0] == '.')
00309 ZYPP_THROW(ServiceInvalidAliasException(
00310 info, _("Service alias cannot start with dot.")));
00311 }
00312
00314
00315 inline void assert_urls( const RepoInfo & info )
00316 {
00317 if ( info.baseUrlsEmpty() )
00318 ZYPP_THROW( RepoNoUrlException( info ) );
00319 }
00320
00321 inline void assert_url( const ServiceInfo & info )
00322 {
00323 if ( ! info.url().isValid() )
00324 ZYPP_THROW( ServiceNoUrlException( info ) );
00325 }
00326
00328
00333 inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
00334 {
00335 assert_alias(info);
00336 return opt.repoRawCachePath / info.escaped_alias();
00337 }
00338
00347 inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
00348 {
00349 assert_alias(info);
00350 return opt.repoRawCachePath / info.escaped_alias() / info.path();
00351 }
00352
00353
00357 inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
00358 {
00359 assert_alias(info);
00360 return opt.repoPackagesCachePath / info.escaped_alias();
00361 }
00362
00366 inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
00367 {
00368 assert_alias(info);
00369 return opt.repoSolvCachePath / info.escaped_alias();
00370 }
00371
00373
00375 class ServiceCollector
00376 {
00377 public:
00378 typedef std::set<ServiceInfo> ServiceSet;
00379
00380 ServiceCollector( ServiceSet & services_r )
00381 : _services( services_r )
00382 {}
00383
00384 bool operator()( const ServiceInfo & service_r ) const
00385 {
00386 _services.insert( service_r );
00387 return true;
00388 }
00389
00390 private:
00391 ServiceSet & _services;
00392 };
00393
00395
00397
00398
00399
00401
00405 struct RepoManager::Impl
00406 {
00407 Impl( const RepoManagerOptions &opt )
00408 : options(opt)
00409 {
00410 init_knownServices();
00411 init_knownRepositories();
00412 }
00413
00414 RepoManagerOptions options;
00415
00416 RepoSet repos;
00417
00418 ServiceSet services;
00419
00420 public:
00421
00422 void saveService( ServiceInfo & service ) const;
00423
00424 Pathname generateNonExistingName( const Pathname &dir,
00425 const std::string &basefilename ) const;
00426
00427 std::string generateFilename( const RepoInfo & info ) const;
00428 std::string generateFilename( const ServiceInfo & info ) const;
00429
00430
00431 private:
00432 void init_knownServices();
00433 void init_knownRepositories();
00434
00435 private:
00436 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00438 Impl * clone() const
00439 { return new Impl( *this ); }
00440 };
00441
00443
00445 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
00446 {
00447 return str << "RepoManager::Impl";
00448 }
00449
00451
00452 void RepoManager::Impl::saveService( ServiceInfo & service ) const
00453 {
00454 filesystem::assert_dir( options.knownServicesPath );
00455 Pathname servfile = generateNonExistingName( options.knownServicesPath,
00456 generateFilename( service ) );
00457 service.setFilepath( servfile );
00458
00459 MIL << "saving service in " << servfile << endl;
00460
00461 std::ofstream file( servfile.c_str() );
00462 if ( !file )
00463 {
00464
00465 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
00466 }
00467 service.dumpAsIniOn( file );
00468 MIL << "done" << endl;
00469 }
00470
00486 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir,
00487 const std::string & basefilename ) const
00488 {
00489 std::string final_filename = basefilename;
00490 int counter = 1;
00491 while ( PathInfo(dir + final_filename).isExist() )
00492 {
00493 final_filename = basefilename + "_" + str::numstring(counter);
00494 counter++;
00495 }
00496 return dir + Pathname(final_filename);
00497 }
00498
00500
00508 std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const
00509 {
00510 std::string filename = info.alias();
00511
00512 str::replaceAll( filename, "/", "_" );
00513
00514 filename = Pathname(filename).extend(".repo").asString();
00515 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
00516 return filename;
00517 }
00518
00519 std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const
00520 {
00521 std::string filename = info.alias();
00522
00523 str::replaceAll( filename, "/", "_" );
00524
00525 filename = Pathname(filename).extend(".service").asString();
00526 MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl;
00527 return filename;
00528 }
00529
00530
00531 void RepoManager::Impl::init_knownServices()
00532 {
00533 Pathname dir = options.knownServicesPath;
00534 std::list<Pathname> entries;
00535 if (PathInfo(dir).isExist())
00536 {
00537 if ( filesystem::readdir( entries, dir, false ) != 0 )
00538 {
00539
00540 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
00541 }
00542
00543
00544 for_(it, entries.begin(), entries.end() )
00545 {
00546 parser::ServiceFileReader(*it, ServiceCollector(services));
00547 }
00548 }
00549
00550 repo::PluginServices(options.pluginsPath()/"services", ServiceCollector(services));
00551 }
00552
00553 void RepoManager::Impl::init_knownRepositories()
00554 {
00555 MIL << "start construct known repos" << endl;
00556
00557 if ( PathInfo(options.knownReposPath).isExist() )
00558 {
00559 RepoInfoList repol = repositories_in_dir(options.knownReposPath);
00560 for ( RepoInfoList::iterator it = repol.begin();
00561 it != repol.end();
00562 ++it )
00563 {
00564
00565 Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
00566 (*it).setMetadataPath(metadata_path);
00567
00568
00569 Pathname packages_path = packagescache_path_for_repoinfo(options, (*it));
00570 (*it).setPackagesPath(packages_path);
00571
00572 repos.insert(*it);
00573 }
00574 }
00575
00576 MIL << "end construct known repos" << endl;
00577 }
00578
00580
00581
00582
00584
00585 RepoManager::RepoManager( const RepoManagerOptions &opt )
00586 : _pimpl( new Impl(opt) )
00587 {}
00588
00590
00591 RepoManager::~RepoManager()
00592 {}
00593
00595
00596 bool RepoManager::repoEmpty() const
00597 { return _pimpl->repos.empty(); }
00598
00599 RepoManager::RepoSizeType RepoManager::repoSize() const
00600 { return _pimpl->repos.size(); }
00601
00602 RepoManager::RepoConstIterator RepoManager::repoBegin() const
00603 { return _pimpl->repos.begin(); }
00604
00605 RepoManager::RepoConstIterator RepoManager::repoEnd() const
00606 { return _pimpl->repos.end(); }
00607
00608 RepoInfo RepoManager::getRepo( const std::string & alias ) const
00609 {
00610 for_( it, repoBegin(), repoEnd() )
00611 if ( it->alias() == alias )
00612 return *it;
00613 return RepoInfo::noRepo;
00614 }
00615
00616 bool RepoManager::hasRepo( const std::string & alias ) const
00617 {
00618 for_( it, repoBegin(), repoEnd() )
00619 if ( it->alias() == alias )
00620 return true;
00621 return false;
00622 }
00623
00624 std::string RepoManager::makeStupidAlias( const Url & url_r )
00625 {
00626 std::string ret( url_r.getScheme() );
00627 if ( ret.empty() )
00628 ret = "repo-";
00629 else
00630 ret += "-";
00631
00632 std::string host( url_r.getHost() );
00633 if ( ! host.empty() )
00634 {
00635 ret += host;
00636 ret += "-";
00637 }
00638
00639 static Date::ValueType serial = Date::now();
00640 ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8);
00641 return ret;
00642 }
00643
00645
00646 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
00647 {
00648 return rawcache_path_for_repoinfo(_pimpl->options, info );
00649 }
00650
00651 Pathname RepoManager::packagesPath( const RepoInfo &info ) const
00652 {
00653 return packagescache_path_for_repoinfo(_pimpl->options, info );
00654 }
00655
00657
00658 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
00659 {
00660 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00661 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
00662 RepoType repokind = info.type();
00663 RepoStatus status;
00664
00665 switch ( repokind.toEnum() )
00666 {
00667 case RepoType::NONE_e:
00668
00669 repokind = probe( productdatapath.asUrl() );
00670 break;
00671 default:
00672 break;
00673 }
00674
00675 switch ( repokind.toEnum() )
00676 {
00677 case RepoType::RPMMD_e :
00678 {
00679 status = RepoStatus( productdatapath + "/repodata/repomd.xml");
00680 }
00681 break;
00682
00683 case RepoType::YAST2_e :
00684 {
00685 status = RepoStatus( productdatapath + "/content") && (RepoStatus( mediarootpath + "/media.1/media"));
00686 }
00687 break;
00688
00689 case RepoType::RPMPLAINDIR_e :
00690 {
00691 if ( PathInfo(Pathname(productdatapath + "/cookie")).isExist() )
00692 status = RepoStatus( productdatapath + "/cookie");
00693 }
00694 break;
00695
00696 case RepoType::NONE_e :
00697
00698
00699
00700 break;
00701 }
00702 return status;
00703 }
00704
00705 void RepoManager::touchIndexFile(const RepoInfo & info)
00706 {
00707 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
00708
00709 RepoType repokind = info.type();
00710 if ( repokind.toEnum() == RepoType::NONE_e )
00711
00712 repokind = probe( productdatapath.asUrl() );
00713
00714 if (repokind == RepoType::NONE_e)
00715 return;
00716
00717 Pathname p;
00718 switch ( repokind.toEnum() )
00719 {
00720 case RepoType::RPMMD_e :
00721 p = Pathname(productdatapath + "/repodata/repomd.xml");
00722 break;
00723
00724 case RepoType::YAST2_e :
00725 p = Pathname(productdatapath + "/content");
00726 break;
00727
00728 case RepoType::RPMPLAINDIR_e :
00729 p = Pathname(productdatapath + "/cookie");
00730 break;
00731
00732 case RepoType::NONE_e :
00733 default:
00734 break;
00735 }
00736
00737
00738 filesystem::touch(p);
00739 }
00740
00741 RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata(
00742 const RepoInfo &info,
00743 const Url &url,
00744 RawMetadataRefreshPolicy policy )
00745 {
00746 assert_alias(info);
00747
00748 RepoStatus oldstatus;
00749 RepoStatus newstatus;
00750
00751 try
00752 {
00753 MIL << "Going to try to check whether refresh is needed for " << url << endl;
00754
00755
00756 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00757 filesystem::assert_dir(mediarootpath);
00758 oldstatus = metadataStatus(info);
00759
00760 if ( oldstatus.empty() )
00761 {
00762 MIL << "No cached metadata, going to refresh" << endl;
00763 return REFRESH_NEEDED;
00764 }
00765
00766 {
00767 std::string scheme( url.getScheme() );
00768 if ( scheme == "cd" || scheme == "dvd" )
00769 {
00770 MIL << "never refresh CD/DVD" << endl;
00771 return REPO_UP_TO_DATE;
00772 }
00773 }
00774
00775
00776 if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
00777 {
00778
00779 double diff = difftime(
00780 (Date::ValueType)Date::now(),
00781 (Date::ValueType)oldstatus.timestamp()) / 60;
00782
00783 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
00784 DBG << "current time: " << (Date::ValueType)Date::now() << endl;
00785 DBG << "last refresh = " << diff << " minutes ago" << endl;
00786
00787 if ( diff < ZConfig::instance().repo_refresh_delay() )
00788 {
00789 if ( diff < 0 )
00790 {
00791 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl;
00792 }
00793 else
00794 {
00795 MIL << "Repository '" << info.alias()
00796 << "' has been refreshed less than repo.refresh.delay ("
00797 << ZConfig::instance().repo_refresh_delay()
00798 << ") minutes ago. Advising to skip refresh" << endl;
00799 return REPO_CHECK_DELAYED;
00800 }
00801 }
00802 }
00803
00804
00805 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
00806
00807 repo::RepoType repokind = info.type();
00808
00809 switch ( repokind.toEnum() )
00810 {
00811 case RepoType::NONE_e:
00812
00813 repokind = probe( url, info.path() );
00814 break;
00815 default:
00816 break;
00817 }
00818
00819 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
00820 ( repokind.toEnum() == RepoType::YAST2_e ) )
00821 {
00822 MediaSetAccess media(url);
00823 shared_ptr<repo::Downloader> downloader_ptr;
00824
00825 if ( repokind.toEnum() == RepoType::RPMMD_e )
00826 downloader_ptr.reset(new yum::Downloader(info));
00827 else
00828 downloader_ptr.reset( new susetags::Downloader(info));
00829
00830 RepoStatus newstatus = downloader_ptr->status(media);
00831 bool refresh = false;
00832 if ( oldstatus.checksum() == newstatus.checksum() )
00833 {
00834 MIL << "repo has not changed" << endl;
00835 if ( policy == RefreshForced )
00836 {
00837 MIL << "refresh set to forced" << endl;
00838 refresh = true;
00839 }
00840 }
00841 else
00842 {
00843 MIL << "repo has changed, going to refresh" << endl;
00844 refresh = true;
00845 }
00846
00847 if (!refresh)
00848 touchIndexFile(info);
00849
00850 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
00851 }
00852 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
00853 {
00854 MediaMounter media( url );
00855 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
00856 bool refresh = false;
00857 if ( oldstatus.checksum() == newstatus.checksum() )
00858 {
00859 MIL << "repo has not changed" << endl;
00860 if ( policy == RefreshForced )
00861 {
00862 MIL << "refresh set to forced" << endl;
00863 refresh = true;
00864 }
00865 }
00866 else
00867 {
00868 MIL << "repo has changed, going to refresh" << endl;
00869 refresh = true;
00870 }
00871
00872 if (!refresh)
00873 touchIndexFile(info);
00874
00875 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
00876 }
00877 else
00878 {
00879 ZYPP_THROW(RepoUnknownTypeException(info));
00880 }
00881 }
00882 catch ( const Exception &e )
00883 {
00884 ZYPP_CAUGHT(e);
00885 ERR << "refresh check failed for " << url << endl;
00886 ZYPP_RETHROW(e);
00887 }
00888
00889 return REFRESH_NEEDED;
00890 }
00891
00892 void RepoManager::refreshMetadata( const RepoInfo &info,
00893 RawMetadataRefreshPolicy policy,
00894 const ProgressData::ReceiverFnc & progress )
00895 {
00896 assert_alias(info);
00897 assert_urls(info);
00898
00899
00900 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
00901
00902
00903 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
00904 {
00905 try
00906 {
00907 Url url(*it);
00908
00909
00910
00911 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
00912 return;
00913
00914 MIL << "Going to refresh metadata from " << url << endl;
00915
00916 repo::RepoType repokind = info.type();
00917
00918
00919 switch ( repokind.toEnum() )
00920 {
00921 case RepoType::NONE_e:
00922
00923 repokind = probe( *it, info.path() );
00924
00925 if (repokind.toEnum() != RepoType::NONE_e)
00926 {
00927
00928 info.setProbedType( repokind );
00929
00930 for_( it, repoBegin(), repoEnd() )
00931 {
00932 if ( info.alias() == (*it).alias() )
00933 {
00934 RepoInfo modifiedrepo = info;
00935 modifiedrepo.setType( repokind );
00936 modifyRepository( info.alias(), modifiedrepo );
00937 break;
00938 }
00939 }
00940 }
00941 break;
00942 default:
00943 break;
00944 }
00945
00946 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
00947 filesystem::assert_dir(mediarootpath);
00948
00949
00950 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
00951
00952 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
00953 ( repokind.toEnum() == RepoType::YAST2_e ) )
00954 {
00955 MediaSetAccess media(url);
00956 shared_ptr<repo::Downloader> downloader_ptr;
00957
00958 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
00959
00960 if ( repokind.toEnum() == RepoType::RPMMD_e )
00961 downloader_ptr.reset(new yum::Downloader(info));
00962 else
00963 downloader_ptr.reset( new susetags::Downloader(info) );
00964
00971 for_( it, repoBegin(), repoEnd() )
00972 {
00973 Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
00974 if ( PathInfo(cachepath).isExist() )
00975 downloader_ptr->addCachePath(cachepath);
00976 }
00977
00978 downloader_ptr->download( media, tmpdir.path() );
00979 }
00980 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
00981 {
00982 MediaMounter media( url );
00983 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) );
00984
00985 Pathname productpath( tmpdir.path() / info.path() );
00986 filesystem::assert_dir( productpath );
00987 std::ofstream file( (productpath/"cookie").c_str() );
00988 if ( !file )
00989 {
00990
00991 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), (productpath/"cookie").c_str() )));
00992 }
00993 file << url;
00994 if ( ! info.path().empty() && info.path() != "/" )
00995 file << " (" << info.path() << ")";
00996 file << endl;
00997 file << newstatus.checksum() << endl;
00998
00999 file.close();
01000 }
01001 else
01002 {
01003 ZYPP_THROW(RepoUnknownTypeException());
01004 }
01005
01006
01007
01008 filesystem::exchange( tmpdir.path(), mediarootpath );
01009
01010
01011 return;
01012 }
01013 catch ( const Exception &e )
01014 {
01015 ZYPP_CAUGHT(e);
01016 ERR << "Trying another url..." << endl;
01017
01018
01019
01020
01021 if (it == info.baseUrlsBegin())
01022 rexception.remember(e);
01023 }
01024 }
01025 ERR << "No more urls..." << endl;
01026 ZYPP_THROW(rexception);
01027 }
01028
01030
01031 void RepoManager::cleanMetadata( const RepoInfo &info,
01032 const ProgressData::ReceiverFnc & progressfnc )
01033 {
01034 ProgressData progress(100);
01035 progress.sendTo(progressfnc);
01036
01037 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
01038 progress.toMax();
01039 }
01040
01041 void RepoManager::cleanPackages( const RepoInfo &info,
01042 const ProgressData::ReceiverFnc & progressfnc )
01043 {
01044 ProgressData progress(100);
01045 progress.sendTo(progressfnc);
01046
01047 filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info));
01048 progress.toMax();
01049 }
01050
01051 void RepoManager::buildCache( const RepoInfo &info,
01052 CacheBuildPolicy policy,
01053 const ProgressData::ReceiverFnc & progressrcv )
01054 {
01055 assert_alias(info);
01056 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info );
01057 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info );
01058
01059 filesystem::assert_dir(_pimpl->options.repoCachePath);
01060 RepoStatus raw_metadata_status = metadataStatus(info);
01061 if ( raw_metadata_status.empty() )
01062 {
01063
01064
01065
01066 refreshMetadata(info, RefreshIfNeeded, progressrcv );
01067 raw_metadata_status = metadataStatus(info);
01068 }
01069
01070 bool needs_cleaning = false;
01071 if ( isCached( info ) )
01072 {
01073 MIL << info.alias() << " is already cached." << endl;
01074 RepoStatus cache_status = cacheStatus(info);
01075
01076 if ( cache_status.checksum() == raw_metadata_status.checksum() )
01077 {
01078 MIL << info.alias() << " cache is up to date with metadata." << endl;
01079 if ( policy == BuildIfNeeded ) {
01080 return;
01081 }
01082 else {
01083 MIL << info.alias() << " cache rebuild is forced" << endl;
01084 }
01085 }
01086
01087 needs_cleaning = true;
01088 }
01089
01090 ProgressData progress(100);
01091 callback::SendReport<ProgressReport> report;
01092 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
01093 progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str()));
01094 progress.toMin();
01095
01096 if (needs_cleaning)
01097 {
01098 cleanCache(info);
01099 }
01100
01101 MIL << info.alias() << " building cache..." << info.type() << endl;
01102
01103 Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
01104 filesystem::assert_dir(base);
01105 Pathname solvfile = base / "solv";
01106
01107
01108 repo::RepoType repokind = info.type();
01109
01110
01111 switch ( repokind.toEnum() )
01112 {
01113 case RepoType::NONE_e:
01114
01115 repokind = probe( productdatapath.asUrl() );
01116 break;
01117 default:
01118 break;
01119 }
01120
01121 MIL << "repo type is " << repokind << endl;
01122
01123 switch ( repokind.toEnum() )
01124 {
01125 case RepoType::RPMMD_e :
01126 case RepoType::YAST2_e :
01127 case RepoType::RPMPLAINDIR_e :
01128 {
01129
01130 ManagedFile guard( solvfile, filesystem::unlink );
01131 scoped_ptr<MediaMounter> forPlainDirs;
01132
01133 ExternalProgram::Arguments cmd;
01134 cmd.push_back( "repo2solv.sh" );
01135
01136
01137 cmd.push_back( "-o" );
01138 cmd.push_back( solvfile.asString() );
01139
01140 if ( repokind == RepoType::RPMPLAINDIR )
01141 {
01142 forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) );
01143
01144 cmd.push_back( "-R" );
01145
01146 cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() );
01147 }
01148 else
01149 cmd.push_back( productdatapath.asString() );
01150
01151 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
01152 std::string errdetail;
01153
01154 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
01155 WAR << " " << output;
01156 if ( errdetail.empty() ) {
01157 errdetail = prog.command();
01158 errdetail += '\n';
01159 }
01160 errdetail += output;
01161 }
01162
01163 int ret = prog.close();
01164 if ( ret != 0 )
01165 {
01166 RepoException ex(str::form( _("Failed to cache repo (%d)."), ret ));
01167 ex.remember( errdetail );
01168 ZYPP_THROW(ex);
01169 }
01170
01171
01172 guard.resetDispose();
01173 }
01174 break;
01175 default:
01176 ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") ));
01177 break;
01178 }
01179
01180 setCacheStatus(info, raw_metadata_status);
01181 MIL << "Commit cache.." << endl;
01182 progress.toMax();
01183 }
01184
01186
01187 repo::RepoType RepoManager::probe( const Url & url ) const
01188 { return probe( url, Pathname() ); }
01189
01190 repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const
01191 {
01192 MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
01193
01194 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() )
01195 {
01196
01197
01198 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
01199 return repo::RepoType::NONE;
01200 }
01201
01202
01203
01204
01205
01206
01207
01208
01209 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
01210 bool gotMediaException = false;
01211 try
01212 {
01213 MediaSetAccess access(url);
01214 try
01215 {
01216 if ( access.doesFileExist(path/"/repodata/repomd.xml") )
01217 {
01218 MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl;
01219 return repo::RepoType::RPMMD;
01220 }
01221 }
01222 catch ( const media::MediaException &e )
01223 {
01224 ZYPP_CAUGHT(e);
01225 DBG << "problem checking for repodata/repomd.xml file" << endl;
01226 enew.remember(e);
01227 gotMediaException = true;
01228 }
01229
01230 try
01231 {
01232 if ( access.doesFileExist(path/"/content") )
01233 {
01234 MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl;
01235 return repo::RepoType::YAST2;
01236 }
01237 }
01238 catch ( const media::MediaException &e )
01239 {
01240 ZYPP_CAUGHT(e);
01241 DBG << "problem checking for content file" << endl;
01242 enew.remember(e);
01243 gotMediaException = true;
01244 }
01245
01246
01247 if ( ! url.schemeIsDownloading() )
01248 {
01249 MediaMounter media( url );
01250 if ( PathInfo(media.getPathName()/path).isDir() )
01251 {
01252
01253 MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl;
01254 return repo::RepoType::RPMPLAINDIR;
01255 }
01256 }
01257 }
01258 catch ( const Exception &e )
01259 {
01260 ZYPP_CAUGHT(e);
01261
01262 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
01263 enew.remember(e);
01264 ZYPP_THROW(enew);
01265 }
01266
01267 if (gotMediaException)
01268 ZYPP_THROW(enew);
01269
01270 MIL << "Probed type NONE at " << url << " (" << path << ")" << endl;
01271 return repo::RepoType::NONE;
01272 }
01273
01275
01276 void RepoManager::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
01277 {
01278 MIL << "Going to clean up garbage in cache dirs" << endl;
01279
01280 ProgressData progress(300);
01281 progress.sendTo(progressrcv);
01282 progress.toMin();
01283
01284 std::list<Pathname> cachedirs;
01285 cachedirs.push_back(_pimpl->options.repoRawCachePath);
01286 cachedirs.push_back(_pimpl->options.repoPackagesCachePath);
01287 cachedirs.push_back(_pimpl->options.repoSolvCachePath);
01288
01289 for_( dir, cachedirs.begin(), cachedirs.end() )
01290 {
01291 if ( PathInfo(*dir).isExist() )
01292 {
01293 std::list<Pathname> entries;
01294 if ( filesystem::readdir( entries, *dir, false ) != 0 )
01295
01296 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
01297
01298 unsigned sdircount = entries.size();
01299 unsigned sdircurrent = 1;
01300 for_( subdir, entries.begin(), entries.end() )
01301 {
01302
01303 bool found = false;
01304 for_( r, repoBegin(), repoEnd() )
01305 if ( subdir->basename() == r->escaped_alias() )
01306 { found = true; break; }
01307
01308 if ( ! found )
01309 filesystem::recursive_rmdir( *subdir );
01310
01311 progress.set( progress.val() + sdircurrent * 100 / sdircount );
01312 ++sdircurrent;
01313 }
01314 }
01315 else
01316 progress.set( progress.val() + 100 );
01317 }
01318 progress.toMax();
01319 }
01320
01322
01323 void RepoManager::cleanCache( const RepoInfo &info,
01324 const ProgressData::ReceiverFnc & progressrcv )
01325 {
01326 ProgressData progress(100);
01327 progress.sendTo(progressrcv);
01328 progress.toMin();
01329
01330 MIL << "Removing raw metadata cache for " << info.alias() << endl;
01331 filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
01332
01333 progress.toMax();
01334 }
01335
01337
01338 bool RepoManager::isCached( const RepoInfo &info ) const
01339 {
01340 return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
01341 }
01342
01343 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
01344 {
01345
01346 Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
01347
01348 return RepoStatus::fromCookieFile(cookiefile);
01349 }
01350
01351 void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
01352 {
01353 Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
01354 filesystem::assert_dir(base);
01355 Pathname cookiefile = base / "cookie";
01356
01357 status.saveToCookieFile(cookiefile);
01358 }
01359
01360 void RepoManager::loadFromCache( const RepoInfo & info,
01361 const ProgressData::ReceiverFnc & progressrcv )
01362 {
01363 assert_alias(info);
01364 Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
01365
01366 if ( ! PathInfo(solvfile).isExist() )
01367 ZYPP_THROW(RepoNotCachedException(info));
01368
01369 try
01370 {
01371 Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
01372
01373
01374
01375
01376
01377
01378
01379 sat::LookupRepoAttr toolversion( sat::SolvAttr::repositoryToolVersion, repo );
01380 if ( toolversion.begin().asString().empty() )
01381 {
01382 repo.eraseFromPool();
01383 ZYPP_THROW(Exception("Solv-file was created by old parser."));
01384 }
01385
01386 }
01387 catch ( const Exception & exp )
01388 {
01389 ZYPP_CAUGHT( exp );
01390 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
01391 cleanCache( info, progressrcv );
01392 buildCache( info, BuildIfNeeded, progressrcv );
01393
01394 sat::Pool::instance().addRepoSolv( solvfile, info );
01395 }
01396 }
01397
01399
01400 void RepoManager::addRepository( const RepoInfo &info,
01401 const ProgressData::ReceiverFnc & progressrcv )
01402 {
01403 assert_alias(info);
01404
01405 ProgressData progress(100);
01406 callback::SendReport<ProgressReport> report;
01407 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
01408 progress.name(str::form(_("Adding repository '%s'"), info.label().c_str()));
01409 progress.toMin();
01410
01411 MIL << "Try adding repo " << info << endl;
01412
01413 RepoInfo tosave = info;
01414 if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
01415 ZYPP_THROW(RepoAlreadyExistsException(info));
01416
01417
01418 if ( _pimpl->options.probe )
01419 {
01420 DBG << "unknown repository type, probing" << endl;
01421
01422 RepoType probedtype;
01423 probedtype = probe( *tosave.baseUrlsBegin(), info.path() );
01424 if ( tosave.baseUrlsSize() > 0 )
01425 {
01426 if ( probedtype == RepoType::NONE )
01427 ZYPP_THROW(RepoUnknownTypeException());
01428 else
01429 tosave.setType(probedtype);
01430 }
01431 }
01432
01433 progress.set(50);
01434
01435
01436 filesystem::assert_dir(_pimpl->options.knownReposPath);
01437
01438 Pathname repofile = _pimpl->generateNonExistingName(
01439 _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
01440
01441 MIL << "Saving repo in " << repofile << endl;
01442
01443 std::ofstream file(repofile.c_str());
01444 if (!file)
01445 {
01446
01447 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
01448 }
01449
01450 tosave.dumpAsIniOn(file);
01451 tosave.setFilepath(repofile);
01452 tosave.setMetadataPath( metadataPath( tosave ) );
01453 tosave.setPackagesPath( packagesPath( tosave ) );
01454 {
01455
01456
01457 RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
01458 oinfo.setMetadataPath( metadataPath( tosave ) );
01459 oinfo.setPackagesPath( packagesPath( tosave ) );
01460 }
01461 _pimpl->repos.insert(tosave);
01462
01463 progress.set(90);
01464
01465
01466 bool havePasswords = false;
01467 for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() )
01468 if ( urlit->hasCredentialsInAuthority() )
01469 {
01470 havePasswords = true;
01471 break;
01472 }
01473
01474 if ( havePasswords )
01475 {
01476 media::CredentialManager cm(
01477 media::CredManagerOptions(_pimpl->options.rootDir) );
01478
01479 for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd())
01480 if (urlit->hasCredentialsInAuthority())
01482 cm.saveInUser(media::AuthData(*urlit));
01483 }
01484
01485 HistoryLog().addRepository(tosave);
01486
01487 progress.toMax();
01488 MIL << "done" << endl;
01489 }
01490
01491 void RepoManager::addRepositories( const Url &url,
01492 const ProgressData::ReceiverFnc & progressrcv )
01493 {
01494 std::list<RepoInfo> repos = readRepoFile(url);
01495 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
01496 it != repos.end();
01497 ++it )
01498 {
01499
01500 for_ ( kit, repoBegin(), repoEnd() )
01501 {
01502 if ( (*it).alias() == (*kit).alias() )
01503 {
01504 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
01505 ZYPP_THROW(RepoAlreadyExistsException(*it));
01506 }
01507 }
01508 }
01509
01510 std::string filename = Pathname(url.getPathName()).basename();
01511
01512 if ( filename == Pathname() )
01513 {
01514
01515 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
01516 }
01517
01518
01519 filesystem::assert_dir(_pimpl->options.knownReposPath);
01520
01521 Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
01522
01523 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
01524
01525 std::ofstream file(repofile.c_str());
01526 if (!file)
01527 {
01528
01529 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
01530 }
01531
01532 for ( std::list<RepoInfo>::iterator it = repos.begin();
01533 it != repos.end();
01534 ++it )
01535 {
01536 MIL << "Saving " << (*it).alias() << endl;
01537 it->setFilepath(repofile.asString());
01538 it->dumpAsIniOn(file);
01539 _pimpl->repos.insert(*it);
01540
01541 HistoryLog(_pimpl->options.rootDir).addRepository(*it);
01542 }
01543
01544 MIL << "done" << endl;
01545 }
01546
01548
01549 void RepoManager::removeRepository( const RepoInfo & info,
01550 const ProgressData::ReceiverFnc & progressrcv)
01551 {
01552 ProgressData progress;
01553 callback::SendReport<ProgressReport> report;
01554 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
01555 progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
01556
01557 MIL << "Going to delete repo " << info.alias() << endl;
01558
01559 for_( it, repoBegin(), repoEnd() )
01560 {
01561
01562
01563
01564 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
01565 continue;
01566
01567
01568
01569
01570
01571 RepoInfo todelete = *it;
01572 if (todelete.filepath().empty())
01573 {
01574 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
01575 }
01576 else
01577 {
01578
01579 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
01580 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
01581 {
01582
01583 if ( filesystem::unlink(todelete.filepath()) != 0 )
01584 {
01585
01586 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
01587 }
01588 MIL << todelete.alias() << " sucessfully deleted." << endl;
01589 }
01590 else
01591 {
01592
01593
01594
01595
01596
01597
01598 filesystem::assert_dir(todelete.filepath().dirname());
01599
01600 std::ofstream file(todelete.filepath().c_str());
01601 if (!file)
01602 {
01603
01604 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
01605 }
01606 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
01607 fit != filerepos.end();
01608 ++fit )
01609 {
01610 if ( (*fit).alias() != todelete.alias() )
01611 (*fit).dumpAsIniOn(file);
01612 }
01613 }
01614
01615 CombinedProgressData subprogrcv(progress, 70);
01616 CombinedProgressData cleansubprogrcv(progress, 30);
01617
01618 if ( isCached(todelete) )
01619 cleanCache( todelete, subprogrcv);
01620
01621 cleanMetadata( todelete, cleansubprogrcv);
01622 _pimpl->repos.erase(todelete);
01623 MIL << todelete.alias() << " sucessfully deleted." << endl;
01624 HistoryLog(_pimpl->options.rootDir).removeRepository(todelete);
01625 return;
01626 }
01627
01628 }
01629
01630 ZYPP_THROW(RepoNotFoundException(info));
01631 }
01632
01634
01635 void RepoManager::modifyRepository( const std::string &alias,
01636 const RepoInfo & newinfo_r,
01637 const ProgressData::ReceiverFnc & progressrcv )
01638 {
01639 RepoInfo toedit = getRepositoryInfo(alias);
01640 RepoInfo newinfo( newinfo_r );
01641
01642
01643 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
01644 {
01645 ZYPP_THROW(RepoAlreadyExistsException(newinfo));
01646 }
01647
01648 if (toedit.filepath().empty())
01649 {
01650 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") ));
01651 }
01652 else
01653 {
01654
01655 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
01656
01657
01658
01659
01660
01661
01662
01663 filesystem::assert_dir(toedit.filepath().dirname());
01664
01665 std::ofstream file(toedit.filepath().c_str());
01666 if (!file)
01667 {
01668
01669 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
01670 }
01671 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
01672 fit != filerepos.end();
01673 ++fit )
01674 {
01675
01676
01677 if ( (*fit).alias() != toedit.alias() )
01678 (*fit).dumpAsIniOn(file);
01679 else
01680 newinfo.dumpAsIniOn(file);
01681 }
01682
01683 newinfo.setFilepath(toedit.filepath());
01684 _pimpl->repos.erase(toedit);
01685 _pimpl->repos.insert(newinfo);
01686 HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo);
01687 MIL << "repo " << alias << " modified" << endl;
01688 }
01689 }
01690
01692
01693 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
01694 const ProgressData::ReceiverFnc & progressrcv )
01695 {
01696 RepoInfo info;
01697 info.setAlias(alias);
01698 RepoConstIterator it = _pimpl->repos.find( info );
01699 if( it == repoEnd() )
01700 ZYPP_THROW(RepoNotFoundException(info));
01701 else
01702 return *it;
01703 }
01704
01706
01707 RepoInfo RepoManager::getRepositoryInfo( const Url & url,
01708 const url::ViewOption & urlview,
01709 const ProgressData::ReceiverFnc & progressrcv )
01710 {
01711 for_( it, repoBegin(), repoEnd() )
01712 {
01713 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
01714 urlit != (*it).baseUrlsEnd();
01715 ++urlit)
01716 {
01717 if ((*urlit).asString(urlview) == url.asString(urlview))
01718 return *it;
01719 }
01720 }
01721 RepoInfo info;
01722 info.setBaseUrl(url);
01723 ZYPP_THROW(RepoNotFoundException(info));
01724 }
01725
01727
01728
01729
01731
01732 bool RepoManager::serviceEmpty() const
01733 { return _pimpl->services.empty(); }
01734
01735 RepoManager::ServiceSizeType RepoManager::serviceSize() const
01736 { return _pimpl->services.size(); }
01737
01738 RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
01739 { return _pimpl->services.begin(); }
01740
01741 RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
01742 { return _pimpl->services.end(); }
01743
01744 ServiceInfo RepoManager::getService( const std::string & alias ) const
01745 {
01746 for_( it, serviceBegin(), serviceEnd() )
01747 if ( it->alias() == alias )
01748 return *it;
01749 return ServiceInfo::noService;
01750 }
01751
01752 bool RepoManager::hasService( const std::string & alias ) const
01753 {
01754 for_( it, serviceBegin(), serviceEnd() )
01755 if ( it->alias() == alias )
01756 return true;
01757 return false;
01758 }
01759
01761
01762 void RepoManager::addService( const std::string & alias, const Url & url )
01763 {
01764 addService( ServiceInfo(alias, url) );
01765 }
01766
01767 void RepoManager::addService( const ServiceInfo & service )
01768 {
01769 assert_alias( service );
01770
01771
01772 if ( hasService( service.alias() ) )
01773 ZYPP_THROW( ServiceAlreadyExistsException( service ) );
01774
01775
01776
01777 ServiceInfo toSave( service );
01778 _pimpl->saveService( toSave );
01779 _pimpl->services.insert( toSave );
01780
01781
01782 if ( toSave.url().hasCredentialsInAuthority() )
01783 {
01784 media::CredentialManager cm(
01785 media::CredManagerOptions(_pimpl->options.rootDir) );
01786
01788 cm.saveInUser(media::AuthData(toSave.url()));
01789 }
01790
01791 MIL << "added service " << toSave.alias() << endl;
01792 }
01793
01795
01796 void RepoManager::removeService( const std::string & alias )
01797 {
01798 MIL << "Going to delete repo " << alias << endl;
01799
01800 const ServiceInfo & service = getService( alias );
01801
01802 Pathname location = service.filepath();
01803 if( location.empty() )
01804 {
01805 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
01806 }
01807
01808 ServiceSet tmpSet;
01809 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
01810
01811
01812 if ( tmpSet.size() == 1 )
01813 {
01814 if ( filesystem::unlink(location) != 0 )
01815 {
01816
01817 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), location.c_str() )));
01818 }
01819 MIL << alias << " sucessfully deleted." << endl;
01820 }
01821 else
01822 {
01823 filesystem::assert_dir(location.dirname());
01824
01825 std::ofstream file(location.c_str());
01826 if( !file )
01827 {
01828
01829 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
01830 }
01831
01832 for_(it, tmpSet.begin(), tmpSet.end())
01833 {
01834 if( it->alias() != alias )
01835 it->dumpAsIniOn(file);
01836 }
01837
01838 MIL << alias << " sucessfully deleted from file " << location << endl;
01839 }
01840
01841
01842 RepoCollector rcollector;
01843 getRepositoriesInService( alias,
01844 boost::make_function_output_iterator(
01845 bind( &RepoCollector::collect, &rcollector, _1 ) ) );
01846
01847 for_(rit, rcollector.repos.begin(), rcollector.repos.end())
01848 removeRepository(*rit);
01849 }
01850
01851 void RepoManager::removeService( const ServiceInfo & service )
01852 { removeService(service.alias()); }
01853
01855
01856 void RepoManager::refreshServices()
01857 {
01858
01859
01860 ServiceSet services( serviceBegin(), serviceEnd() );
01861 for_( it, services.begin(), services.end() )
01862 {
01863 if ( !it->enabled() )
01864 continue;
01865
01866 try {
01867 refreshService(*it);
01868 }
01869 catch ( const repo::ServicePluginInformalException & e )
01870 { ; }
01871 }
01872 }
01873
01874 void RepoManager::refreshService( const ServiceInfo & service )
01875 { refreshService( service.alias() ); }
01876
01877 void RepoManager::refreshService( const std::string & alias )
01878 {
01879 ServiceInfo service( getService( alias ) );
01880 assert_alias( service );
01881 assert_url( service );
01882
01883
01884
01885 bool serviceModified = false;
01886 MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
01887
01889
01890
01891 if ( service.type() == repo::ServiceType::NONE )
01892 {
01893 repo::ServiceType type = probeService( service.url() );
01894 if ( type != ServiceType::NONE )
01895 {
01896 service.setProbedType( type );
01897 serviceModified = true;
01898 }
01899 }
01900
01901
01902 std::string servicesTargetDistro = _pimpl->options.servicesTargetDistro;
01903 if ( servicesTargetDistro.empty() )
01904 {
01905 servicesTargetDistro = Target::targetDistribution( Pathname() );
01906 }
01907 DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl;
01908
01909
01910 RepoCollector collector(servicesTargetDistro);
01911
01912
01913
01914
01915 std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
01916 try {
01917 ServiceRepos repos(service, bind( &RepoCollector::collect, &collector, _1 ));
01918 }
01919 catch ( const repo::ServicePluginInformalException & e )
01920 {
01921
01922 uglyHack.first = true;
01923 uglyHack.second = e;
01924 }
01925
01926
01927 for_( it, collector.repos.begin(), collector.repos.end() )
01928 {
01929
01930 Url url;
01931
01932 if ( it->baseUrlsEmpty() )
01933 url = service.url();
01934 else
01935 {
01936
01937 url = *it->baseUrlsBegin();
01938 }
01939
01940
01941
01942 if ( !it->path().empty() )
01943 {
01944 Pathname path(url.getPathName());
01945 path /= it->path();
01946 url.setPathName( path.asString() );
01947 it->setPath("");
01948 }
01949
01950
01951 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
01952
01953
01954 it->setBaseUrl( url );
01955
01956 it->setService( service.alias() );
01957 }
01958
01960
01961
01962 RepoInfoList oldRepos;
01963 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
01964
01965
01966 for_( it, oldRepos.begin(), oldRepos.end() )
01967 {
01968 if ( ! foundAliasIn( it->alias(), collector.repos ) )
01969 {
01970 if ( it->enabled() && ! service.repoToDisableFind( it->alias() ) )
01971 {
01972 DBG << "Service removes enabled repo " << it->alias() << endl;
01973 service.addRepoToEnable( it->alias() );
01974 serviceModified = true;
01975 }
01976 else
01977 {
01978 DBG << "Service removes disabled repo " << it->alias() << endl;
01979 }
01980 removeRepository( *it );
01981 }
01982 }
01983
01985
01986 for_( it, collector.repos.begin(), collector.repos.end() )
01987 {
01988
01989
01990
01991 bool beEnabled = service.repoToEnableFind( it->alias() );
01992 bool beDisabled = service.repoToDisableFind( it->alias() );
01993
01994
01995
01996 if ( beEnabled ) it->setEnabled(true);
01997 if ( beDisabled ) it->setEnabled(false);
01998
01999 if ( beEnabled )
02000 {
02001
02002
02003
02004 service.delRepoToEnable( it->alias() );
02005 serviceModified = true;
02006 }
02007
02008 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
02009 if ( oldRepo == oldRepos.end() )
02010 {
02011
02012
02013
02014
02015
02016 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
02017 addRepository( *it );
02018
02019
02020
02021 }
02022 else
02023 {
02024
02025 bool oldRepoModified = false;
02026
02027
02028 if ( beEnabled )
02029 {
02030 if ( ! oldRepo->enabled() )
02031 {
02032 DBG << "Service repo " << it->alias() << " gets enabled" << endl;
02033 oldRepo->setEnabled( true );
02034 oldRepoModified = true;
02035 }
02036 else
02037 {
02038 DBG << "Service repo " << it->alias() << " stays enabled" << endl;
02039 }
02040 }
02041 else if ( beDisabled )
02042 {
02043 if ( oldRepo->enabled() )
02044 {
02045 DBG << "Service repo " << it->alias() << " gets disabled" << endl;
02046 oldRepo->setEnabled( false );
02047 oldRepoModified = true;
02048 }
02049 else
02050 {
02051 DBG << "Service repo " << it->alias() << " stays disabled" << endl;
02052 }
02053 }
02054 else
02055 {
02056 DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
02057 }
02058
02059
02060
02061 if ( oldRepo->url() != it->url() )
02062 {
02063 DBG << "Service repo " << it->alias() << " gets new URL " << it->url() << endl;
02064 oldRepo->setBaseUrl( it->url() );
02065 oldRepoModified = true;
02066 }
02067
02068
02069 if ( oldRepoModified )
02070 {
02071 modifyRepository( oldRepo->alias(), *oldRepo );
02072 }
02073 }
02074 }
02075
02076
02077 if ( ! service.reposToDisableEmpty() )
02078 {
02079 service.clearReposToDisable();
02080 serviceModified = true;
02081 }
02082
02084
02085 if ( serviceModified )
02086 {
02087
02088 modifyService( service.alias(), service );
02089 }
02090
02091 if ( uglyHack.first )
02092 {
02093 throw( uglyHack.second );
02094 }
02095 }
02096
02098
02099 void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & newService)
02100 {
02101 MIL << "Going to modify service " << oldAlias << endl;
02102
02103
02104
02105 ServiceInfo service(newService);
02106
02107 if ( service.type() == ServiceType::PLUGIN )
02108 {
02109 MIL << "Not modifying plugin service '" << oldAlias << "'" << endl;
02110 return;
02111 }
02112
02113 const ServiceInfo & oldService = getService(oldAlias);
02114
02115 Pathname location = oldService.filepath();
02116 if( location.empty() )
02117 {
02118 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") ));
02119 }
02120
02121
02122 ServiceSet tmpSet;
02123 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
02124
02125 filesystem::assert_dir(location.dirname());
02126 std::ofstream file(location.c_str());
02127 for_(it, tmpSet.begin(), tmpSet.end())
02128 {
02129 if( *it != oldAlias )
02130 it->dumpAsIniOn(file);
02131 }
02132 service.dumpAsIniOn(file);
02133 file.close();
02134 service.setFilepath(location);
02135
02136 _pimpl->services.erase(oldAlias);
02137 _pimpl->services.insert(service);
02138
02139
02140 if( oldAlias != service.alias()
02141 || oldService.enabled() != service.enabled()
02142 )
02143 {
02144 std::vector<RepoInfo> toModify;
02145 getRepositoriesInService(oldAlias, std::back_inserter(toModify));
02146 for_( it, toModify.begin(), toModify.end() )
02147 {
02148 if (oldService.enabled() && !service.enabled())
02149 it->setEnabled(false);
02150 else if (!oldService.enabled() && service.enabled())
02151 {
02154 }
02155 else
02156 it->setService(service.alias());
02157 modifyRepository(it->alias(), *it);
02158 }
02159 }
02160
02162 }
02163
02165
02166 repo::ServiceType RepoManager::probeService( const Url &url ) const
02167 {
02168 try
02169 {
02170 MediaSetAccess access(url);
02171 if ( access.doesFileExist("/repo/repoindex.xml") )
02172 return repo::ServiceType::RIS;
02173 }
02174 catch ( const media::MediaException &e )
02175 {
02176 ZYPP_CAUGHT(e);
02177
02178 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
02179 enew.remember(e);
02180 ZYPP_THROW(enew);
02181 }
02182 catch ( const Exception &e )
02183 {
02184 ZYPP_CAUGHT(e);
02185
02186 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
02187 enew.remember(e);
02188 ZYPP_THROW(enew);
02189 }
02190
02191 return repo::ServiceType::NONE;
02192 }
02193
02195
02196 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
02197 {
02198 return str << *obj._pimpl;
02199 }
02200
02202 }