libzypp
10.5.0
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 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" // for Target::targetDistribution() for repo index services 00048 #include "zypp/ZYppFactory.h" // to get the Target from ZYpp instance 00049 #include "zypp/HistoryLog.h" // to write history :O) 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 // CLASS NAME : RepoManagerOptions 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 pluginsPath = Pathname::assertprefix( root_r, ZConfig::instance().pluginsPath() ); 00149 probe = ZConfig::instance().repo_add_probe(); 00150 00151 rootDir = root_r; 00152 } 00153 00154 RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r ) 00155 { 00156 RepoManagerOptions ret; 00157 ret.repoCachePath = root_r; 00158 ret.repoRawCachePath = root_r/"raw"; 00159 ret.repoSolvCachePath = root_r/"solv"; 00160 ret.repoPackagesCachePath = root_r/"packages"; 00161 ret.knownReposPath = root_r/"repos.d"; 00162 ret.knownServicesPath = root_r/"services.d"; 00163 ret.pluginsPath = root_r/"plugins"; 00164 ret.rootDir = root_r; 00165 return ret; 00166 } 00167 00169 00185 struct RepoCollector : private base::NonCopyable 00186 { 00187 RepoCollector() 00188 {} 00189 00190 RepoCollector(const std::string & targetDistro_) 00191 : targetDistro(targetDistro_) 00192 {} 00193 00194 bool collect( const RepoInfo &repo ) 00195 { 00196 // skip repositories meant for other distros than specified 00197 if (!targetDistro.empty() 00198 && !repo.targetDistribution().empty() 00199 && repo.targetDistribution() != targetDistro) 00200 { 00201 MIL 00202 << "Skipping repository meant for '" << targetDistro 00203 << "' distribution (current distro is '" 00204 << repo.targetDistribution() << "')." << endl; 00205 00206 return true; 00207 } 00208 00209 repos.push_back(repo); 00210 return true; 00211 } 00212 00213 RepoInfoList repos; 00214 std::string targetDistro; 00215 }; 00216 00218 00224 static std::list<RepoInfo> repositories_in_file( const Pathname & file ) 00225 { 00226 MIL << "repo file: " << file << endl; 00227 RepoCollector collector; 00228 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) ); 00229 return collector.repos; 00230 } 00231 00233 00242 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir ) 00243 { 00244 MIL << "directory " << dir << endl; 00245 std::list<RepoInfo> repos; 00246 std::list<Pathname> entries; 00247 if ( filesystem::readdir( entries, dir, false ) != 0 ) 00248 { 00249 // TranslatorExplanation '%s' is a pathname 00250 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str()))); 00251 } 00252 00253 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$"); 00254 for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it ) 00255 { 00256 if (str::regex_match(it->extension(), allowedRepoExt)) 00257 { 00258 std::list<RepoInfo> tmp = repositories_in_file( *it ); 00259 repos.insert( repos.end(), tmp.begin(), tmp.end() ); 00260 00261 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos)); 00262 //MIL << "ok" << endl; 00263 } 00264 } 00265 return repos; 00266 } 00267 00269 00270 std::list<RepoInfo> readRepoFile(const Url & repo_file) 00271 { 00272 // no interface to download a specific file, using workaround: 00274 Url url(repo_file); 00275 Pathname path(url.getPathName()); 00276 url.setPathName ("/"); 00277 MediaSetAccess access(url); 00278 Pathname local = access.provideFile(path); 00279 00280 DBG << "reading repo file " << repo_file << ", local path: " << local << endl; 00281 00282 return repositories_in_file(local); 00283 } 00284 00286 00287 inline void assert_alias( const RepoInfo & info ) 00288 { 00289 if ( info.alias().empty() ) 00290 ZYPP_THROW( RepoNoAliasException() ); 00291 // bnc #473834. Maybe we can match the alias against a regex to define 00292 // and check for valid aliases 00293 if ( info.alias()[0] == '.') 00294 ZYPP_THROW(RepoInvalidAliasException( 00295 info, _("Repository alias cannot start with dot."))); 00296 } 00297 00298 inline void assert_alias( const ServiceInfo & info ) 00299 { 00300 if ( info.alias().empty() ) 00301 ZYPP_THROW( ServiceNoAliasException() ); 00302 // bnc #473834. Maybe we can match the alias against a regex to define 00303 // and check for valid aliases 00304 if ( info.alias()[0] == '.') 00305 ZYPP_THROW(ServiceInvalidAliasException( 00306 info, _("Service alias cannot start with dot."))); 00307 } 00308 00310 00311 inline void assert_urls( const RepoInfo & info ) 00312 { 00313 if ( info.baseUrlsEmpty() ) 00314 ZYPP_THROW( RepoNoUrlException( info ) ); 00315 } 00316 00317 inline void assert_url( const ServiceInfo & info ) 00318 { 00319 if ( ! info.url().isValid() ) 00320 ZYPP_THROW( ServiceNoUrlException( info ) ); 00321 } 00322 00324 00329 inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00330 { 00331 assert_alias(info); 00332 return opt.repoRawCachePath / info.escaped_alias(); 00333 } 00334 00343 inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00344 { 00345 assert_alias(info); 00346 return opt.repoRawCachePath / info.escaped_alias() / info.path(); 00347 } 00348 00349 00353 inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00354 { 00355 assert_alias(info); 00356 return opt.repoPackagesCachePath / info.escaped_alias(); 00357 } 00358 00362 inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info) 00363 { 00364 assert_alias(info); 00365 return opt.repoSolvCachePath / info.escaped_alias(); 00366 } 00367 00369 00371 class ServiceCollector 00372 { 00373 public: 00374 typedef std::set<ServiceInfo> ServiceSet; 00375 00376 ServiceCollector( ServiceSet & services_r ) 00377 : _services( services_r ) 00378 {} 00379 00380 bool operator()( const ServiceInfo & service_r ) const 00381 { 00382 _services.insert( service_r ); 00383 return true; 00384 } 00385 00386 private: 00387 ServiceSet & _services; 00388 }; 00389 00391 00393 // 00394 // CLASS NAME : RepoManager::Impl 00395 // 00397 00401 struct RepoManager::Impl 00402 { 00403 Impl( const RepoManagerOptions &opt ) 00404 : options(opt) 00405 { 00406 init_knownServices(); 00407 init_knownRepositories(); 00408 } 00409 00410 00411 RepoManagerOptions options; 00412 00413 RepoSet repos; 00414 00415 ServiceSet services; 00416 00417 public: 00418 00419 void saveService( ServiceInfo & service ) const; 00420 00421 Pathname generateNonExistingName( const Pathname &dir, 00422 const std::string &basefilename ) const; 00423 00424 std::string generateFilename( const RepoInfo & info ) const; 00425 std::string generateFilename( const ServiceInfo & info ) const; 00426 00427 00428 private: 00429 void init_knownServices(); 00430 void init_knownRepositories(); 00431 00432 private: 00433 friend Impl * rwcowClone<Impl>( const Impl * rhs ); 00435 Impl * clone() const 00436 { return new Impl( *this ); } 00437 }; 00438 00440 00442 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj ) 00443 { 00444 return str << "RepoManager::Impl"; 00445 } 00446 00448 00449 void RepoManager::Impl::saveService( ServiceInfo & service ) const 00450 { 00451 filesystem::assert_dir( options.knownServicesPath ); 00452 Pathname servfile = generateNonExistingName( options.knownServicesPath, 00453 generateFilename( service ) ); 00454 service.setFilepath( servfile ); 00455 00456 MIL << "saving service in " << servfile << endl; 00457 00458 std::ofstream file( servfile.c_str() ); 00459 if ( !file ) 00460 { 00461 // TranslatorExplanation '%s' is a filename 00462 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() ))); 00463 } 00464 service.dumpAsIniOn( file ); 00465 MIL << "done" << endl; 00466 } 00467 00483 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir, 00484 const std::string & basefilename ) const 00485 { 00486 std::string final_filename = basefilename; 00487 int counter = 1; 00488 while ( PathInfo(dir + final_filename).isExist() ) 00489 { 00490 final_filename = basefilename + "_" + str::numstring(counter); 00491 counter++; 00492 } 00493 return dir + Pathname(final_filename); 00494 } 00495 00497 00505 std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const 00506 { 00507 std::string filename = info.alias(); 00508 // replace slashes with underscores 00509 str::replaceAll( filename, "/", "_" ); 00510 00511 filename = Pathname(filename).extend(".repo").asString(); 00512 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl; 00513 return filename; 00514 } 00515 00516 std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const 00517 { 00518 std::string filename = info.alias(); 00519 // replace slashes with underscores 00520 str::replaceAll( filename, "/", "_" ); 00521 00522 filename = Pathname(filename).extend(".service").asString(); 00523 MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl; 00524 return filename; 00525 } 00526 00527 00528 void RepoManager::Impl::init_knownServices() 00529 { 00530 Pathname dir = options.knownServicesPath; 00531 std::list<Pathname> entries; 00532 if (PathInfo(dir).isExist()) 00533 { 00534 if ( filesystem::readdir( entries, dir, false ) != 0 ) 00535 { 00536 // TranslatorExplanation '%s' is a pathname 00537 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str()))); 00538 } 00539 00540 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$"); 00541 for_(it, entries.begin(), entries.end() ) 00542 { 00543 parser::ServiceFileReader(*it, ServiceCollector(services)); 00544 } 00545 } 00546 00547 repo::PluginServices(options.pluginsPath/"services", ServiceCollector(services)); 00548 } 00549 00550 void RepoManager::Impl::init_knownRepositories() 00551 { 00552 MIL << "start construct known repos" << endl; 00553 00554 if ( PathInfo(options.knownReposPath).isExist() ) 00555 { 00556 RepoInfoList repol = repositories_in_dir(options.knownReposPath); 00557 std::list<string> repo_esc_aliases; 00558 std::list<string> entries; 00559 for ( RepoInfoList::iterator it = repol.begin(); 00560 it != repol.end(); 00561 ++it ) 00562 { 00563 // set the metadata path for the repo 00564 Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it)); 00565 (*it).setMetadataPath(metadata_path); 00566 00567 // set the downloaded packages path for the repo 00568 Pathname packages_path = packagescache_path_for_repoinfo(options, (*it)); 00569 (*it).setPackagesPath(packages_path); 00570 00571 repos.insert(*it); 00572 repo_esc_aliases.push_back(it->escaped_alias()); 00573 } 00574 00575 // delete metadata folders without corresponding repo (e.g. old tmp directories) 00576 if ( filesystem::readdir( entries, options.repoRawCachePath, false ) == 0 ) 00577 { 00578 std::set<string> oldfiles; 00579 repo_esc_aliases.sort(); 00580 entries.sort(); 00581 set_difference(entries.begin(), entries.end(), repo_esc_aliases.begin(), repo_esc_aliases.end(), std::inserter(oldfiles, oldfiles.end())); 00582 for_(it, oldfiles.begin(), oldfiles.end()) 00583 { 00584 filesystem::recursive_rmdir(options.repoRawCachePath / *it); 00585 } 00586 } 00587 } 00588 00589 MIL << "end construct known repos" << endl; 00590 } 00591 00593 // 00594 // CLASS NAME : RepoManager 00595 // 00597 00598 RepoManager::RepoManager( const RepoManagerOptions &opt ) 00599 : _pimpl( new Impl(opt) ) 00600 {} 00601 00603 00604 RepoManager::~RepoManager() 00605 {} 00606 00608 00609 bool RepoManager::repoEmpty() const 00610 { return _pimpl->repos.empty(); } 00611 00612 RepoManager::RepoSizeType RepoManager::repoSize() const 00613 { return _pimpl->repos.size(); } 00614 00615 RepoManager::RepoConstIterator RepoManager::repoBegin() const 00616 { return _pimpl->repos.begin(); } 00617 00618 RepoManager::RepoConstIterator RepoManager::repoEnd() const 00619 { return _pimpl->repos.end(); } 00620 00621 RepoInfo RepoManager::getRepo( const std::string & alias ) const 00622 { 00623 for_( it, repoBegin(), repoEnd() ) 00624 if ( it->alias() == alias ) 00625 return *it; 00626 return RepoInfo::noRepo; 00627 } 00628 00629 bool RepoManager::hasRepo( const std::string & alias ) const 00630 { 00631 for_( it, repoBegin(), repoEnd() ) 00632 if ( it->alias() == alias ) 00633 return true; 00634 return false; 00635 } 00636 00637 std::string RepoManager::makeStupidAlias( const Url & url_r ) 00638 { 00639 std::string ret( url_r.getScheme() ); 00640 if ( ret.empty() ) 00641 ret = "repo-"; 00642 else 00643 ret += "-"; 00644 00645 std::string host( url_r.getHost() ); 00646 if ( ! host.empty() ) 00647 { 00648 ret += host; 00649 ret += "-"; 00650 } 00651 00652 static Date::ValueType serial = Date::now(); 00653 ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8); 00654 return ret; 00655 } 00656 00658 00659 Pathname RepoManager::metadataPath( const RepoInfo &info ) const 00660 { 00661 return rawcache_path_for_repoinfo(_pimpl->options, info ); 00662 } 00663 00664 Pathname RepoManager::packagesPath( const RepoInfo &info ) const 00665 { 00666 return packagescache_path_for_repoinfo(_pimpl->options, info ); 00667 } 00668 00670 00671 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const 00672 { 00673 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 00674 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 00675 RepoType repokind = info.type(); 00676 RepoStatus status; 00677 00678 switch ( repokind.toEnum() ) 00679 { 00680 case RepoType::NONE_e: 00681 // unknown, probe the local metadata 00682 repokind = probe( productdatapath.asUrl() ); 00683 break; 00684 default: 00685 break; 00686 } 00687 00688 switch ( repokind.toEnum() ) 00689 { 00690 case RepoType::RPMMD_e : 00691 { 00692 status = RepoStatus( productdatapath + "/repodata/repomd.xml"); 00693 } 00694 break; 00695 00696 case RepoType::YAST2_e : 00697 { 00698 status = RepoStatus( productdatapath + "/content") && (RepoStatus( mediarootpath + "/media.1/media")); 00699 } 00700 break; 00701 00702 case RepoType::RPMPLAINDIR_e : 00703 { 00704 if ( PathInfo(Pathname(productdatapath + "/cookie")).isExist() ) 00705 status = RepoStatus( productdatapath + "/cookie"); 00706 } 00707 break; 00708 00709 case RepoType::NONE_e : 00710 // Return default RepoStatus in case of RepoType::NONE 00711 // indicating it should be created? 00712 // ZYPP_THROW(RepoUnknownTypeException()); 00713 break; 00714 } 00715 return status; 00716 } 00717 00718 void RepoManager::touchIndexFile(const RepoInfo & info) 00719 { 00720 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 00721 00722 RepoType repokind = info.type(); 00723 if ( repokind.toEnum() == RepoType::NONE_e ) 00724 // unknown, probe the local metadata 00725 repokind = probe( productdatapath.asUrl() ); 00726 // if still unknown, just return 00727 if (repokind == RepoType::NONE_e) 00728 return; 00729 00730 Pathname p; 00731 switch ( repokind.toEnum() ) 00732 { 00733 case RepoType::RPMMD_e : 00734 p = Pathname(productdatapath + "/repodata/repomd.xml"); 00735 break; 00736 00737 case RepoType::YAST2_e : 00738 p = Pathname(productdatapath + "/content"); 00739 break; 00740 00741 case RepoType::RPMPLAINDIR_e : 00742 p = Pathname(productdatapath + "/cookie"); 00743 break; 00744 00745 case RepoType::NONE_e : 00746 default: 00747 break; 00748 } 00749 00750 // touch the file, ignore error (they are logged anyway) 00751 filesystem::touch(p); 00752 } 00753 00754 RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata( 00755 const RepoInfo &info, 00756 const Url &url, 00757 RawMetadataRefreshPolicy policy ) 00758 { 00759 assert_alias(info); 00760 00761 RepoStatus oldstatus; 00762 RepoStatus newstatus; 00763 00764 try 00765 { 00766 MIL << "Going to try to check whether refresh is needed for " << url << endl; 00767 00768 // first check old (cached) metadata 00769 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 00770 filesystem::assert_dir(mediarootpath); 00771 oldstatus = metadataStatus(info); 00772 00773 if ( oldstatus.empty() ) 00774 { 00775 MIL << "No cached metadata, going to refresh" << endl; 00776 return REFRESH_NEEDED; 00777 } 00778 00779 { 00780 std::string scheme( url.getScheme() ); 00781 if ( scheme == "cd" || scheme == "dvd" ) 00782 { 00783 MIL << "never refresh CD/DVD" << endl; 00784 return REPO_UP_TO_DATE; 00785 } 00786 } 00787 00788 // now we've got the old (cached) status, we can decide repo.refresh.delay 00789 if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay) 00790 { 00791 // difference in seconds 00792 double diff = difftime( 00793 (Date::ValueType)Date::now(), 00794 (Date::ValueType)oldstatus.timestamp()) / 60; 00795 00796 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl; 00797 DBG << "current time: " << (Date::ValueType)Date::now() << endl; 00798 DBG << "last refresh = " << diff << " minutes ago" << endl; 00799 00800 if ( diff < ZConfig::instance().repo_refresh_delay() ) 00801 { 00802 if ( diff < 0 ) 00803 { 00804 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl; 00805 } 00806 else 00807 { 00808 MIL << "Repository '" << info.alias() 00809 << "' has been refreshed less than repo.refresh.delay (" 00810 << ZConfig::instance().repo_refresh_delay() 00811 << ") minutes ago. Advising to skip refresh" << endl; 00812 return REPO_CHECK_DELAYED; 00813 } 00814 } 00815 } 00816 00817 // To test the new matadta create temp dir as sibling of mediarootpath 00818 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) ); 00819 00820 repo::RepoType repokind = info.type(); 00821 // if the type is unknown, try probing. 00822 switch ( repokind.toEnum() ) 00823 { 00824 case RepoType::NONE_e: 00825 // unknown, probe it \todo respect productdir 00826 repokind = probe( url, info.path() ); 00827 break; 00828 default: 00829 break; 00830 } 00831 00832 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) || 00833 ( repokind.toEnum() == RepoType::YAST2_e ) ) 00834 { 00835 MediaSetAccess media(url); 00836 shared_ptr<repo::Downloader> downloader_ptr; 00837 00838 if ( repokind.toEnum() == RepoType::RPMMD_e ) 00839 downloader_ptr.reset(new yum::Downloader(info, mediarootpath)); 00840 else 00841 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath)); 00842 00843 RepoStatus newstatus = downloader_ptr->status(media); 00844 bool refresh = false; 00845 if ( oldstatus.checksum() == newstatus.checksum() ) 00846 { 00847 MIL << "repo has not changed" << endl; 00848 if ( policy == RefreshForced ) 00849 { 00850 MIL << "refresh set to forced" << endl; 00851 refresh = true; 00852 } 00853 } 00854 else 00855 { 00856 MIL << "repo has changed, going to refresh" << endl; 00857 refresh = true; 00858 } 00859 00860 if (!refresh) 00861 touchIndexFile(info); 00862 00863 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE; 00864 } 00865 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e ) 00866 { 00867 MediaMounter media( url ); 00868 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) ); 00869 bool refresh = false; 00870 if ( oldstatus.checksum() == newstatus.checksum() ) 00871 { 00872 MIL << "repo has not changed" << endl; 00873 if ( policy == RefreshForced ) 00874 { 00875 MIL << "refresh set to forced" << endl; 00876 refresh = true; 00877 } 00878 } 00879 else 00880 { 00881 MIL << "repo has changed, going to refresh" << endl; 00882 refresh = true; 00883 } 00884 00885 if (!refresh) 00886 touchIndexFile(info); 00887 00888 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE; 00889 } 00890 else 00891 { 00892 ZYPP_THROW(RepoUnknownTypeException(info)); 00893 } 00894 } 00895 catch ( const Exception &e ) 00896 { 00897 ZYPP_CAUGHT(e); 00898 ERR << "refresh check failed for " << url << endl; 00899 ZYPP_RETHROW(e); 00900 } 00901 00902 return REFRESH_NEEDED; // default 00903 } 00904 00905 void RepoManager::refreshMetadata( const RepoInfo &info, 00906 RawMetadataRefreshPolicy policy, 00907 const ProgressData::ReceiverFnc & progress ) 00908 { 00909 assert_alias(info); 00910 assert_urls(info); 00911 00912 // we will throw this later if no URL checks out fine 00913 RepoException rexception(_("Valid metadata not found at specified URL(s)")); 00914 00915 // try urls one by one 00916 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it ) 00917 { 00918 try 00919 { 00920 Url url(*it); 00921 00922 // check whether to refresh metadata 00923 // if the check fails for this url, it throws, so another url will be checked 00924 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED) 00925 return; 00926 00927 MIL << "Going to refresh metadata from " << url << endl; 00928 00929 repo::RepoType repokind = info.type(); 00930 00931 // if the type is unknown, try probing. 00932 switch ( repokind.toEnum() ) 00933 { 00934 case RepoType::NONE_e: 00935 // unknown, probe it 00936 repokind = probe( *it, info.path() ); 00937 00938 if (repokind.toEnum() != RepoType::NONE_e) 00939 { 00940 // Adjust the probed type in RepoInfo 00941 info.setProbedType( repokind ); // lazy init! 00942 //save probed type only for repos in system 00943 for_( it, repoBegin(), repoEnd() ) 00944 { 00945 if ( info.alias() == (*it).alias() ) 00946 { 00947 RepoInfo modifiedrepo = info; 00948 modifiedrepo.setType( repokind ); 00949 modifyRepository( info.alias(), modifiedrepo ); 00950 break; 00951 } 00952 } 00953 } 00954 break; 00955 default: 00956 break; 00957 } 00958 00959 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 00960 if( filesystem::assert_dir(mediarootpath) ) 00961 { 00962 Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) ); 00963 ZYPP_THROW(ex); 00964 } 00965 00966 // create temp dir as sibling of mediarootpath 00967 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) ); 00968 if( tmpdir.path().empty() ) 00969 { 00970 Exception ex(_("Can't create metadata cache directory.")); 00971 ZYPP_THROW(ex); 00972 } 00973 00974 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) || 00975 ( repokind.toEnum() == RepoType::YAST2_e ) ) 00976 { 00977 MediaSetAccess media(url); 00978 shared_ptr<repo::Downloader> downloader_ptr; 00979 00980 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl; 00981 00982 if ( repokind.toEnum() == RepoType::RPMMD_e ) 00983 downloader_ptr.reset(new yum::Downloader(info, mediarootpath)); 00984 else 00985 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) ); 00986 00993 for_( it, repoBegin(), repoEnd() ) 00994 { 00995 Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it )); 00996 if ( PathInfo(cachepath).isExist() ) 00997 downloader_ptr->addCachePath(cachepath); 00998 } 00999 01000 downloader_ptr->download( media, tmpdir.path() ); 01001 } 01002 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e ) 01003 { 01004 MediaMounter media( url ); 01005 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) ); 01006 01007 Pathname productpath( tmpdir.path() / info.path() ); 01008 filesystem::assert_dir( productpath ); 01009 std::ofstream file( (productpath/"cookie").c_str() ); 01010 if ( !file ) 01011 { 01012 // TranslatorExplanation '%s' is a filename 01013 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), (productpath/"cookie").c_str() ))); 01014 } 01015 file << url; 01016 if ( ! info.path().empty() && info.path() != "/" ) 01017 file << " (" << info.path() << ")"; 01018 file << endl; 01019 file << newstatus.checksum() << endl; 01020 01021 file.close(); 01022 } 01023 else 01024 { 01025 ZYPP_THROW(RepoUnknownTypeException()); 01026 } 01027 01028 // ok we have the metadata, now exchange 01029 // the contents 01030 filesystem::exchange( tmpdir.path(), mediarootpath ); 01031 01032 // we are done. 01033 return; 01034 } 01035 catch ( const Exception &e ) 01036 { 01037 ZYPP_CAUGHT(e); 01038 ERR << "Trying another url..." << endl; 01039 01040 // remember the exception caught for the *first URL* 01041 // if all other URLs fail, the rexception will be thrown with the 01042 // cause of the problem of the first URL remembered 01043 if (it == info.baseUrlsBegin()) 01044 rexception.remember(e); 01045 } 01046 } // for every url 01047 ERR << "No more urls..." << endl; 01048 ZYPP_THROW(rexception); 01049 } 01050 01052 01053 void RepoManager::cleanMetadata( const RepoInfo &info, 01054 const ProgressData::ReceiverFnc & progressfnc ) 01055 { 01056 ProgressData progress(100); 01057 progress.sendTo(progressfnc); 01058 01059 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info)); 01060 progress.toMax(); 01061 } 01062 01063 void RepoManager::cleanPackages( const RepoInfo &info, 01064 const ProgressData::ReceiverFnc & progressfnc ) 01065 { 01066 ProgressData progress(100); 01067 progress.sendTo(progressfnc); 01068 01069 filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info)); 01070 progress.toMax(); 01071 } 01072 01073 void RepoManager::buildCache( const RepoInfo &info, 01074 CacheBuildPolicy policy, 01075 const ProgressData::ReceiverFnc & progressrcv ) 01076 { 01077 assert_alias(info); 01078 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 01079 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 01080 01081 if( filesystem::assert_dir(_pimpl->options.repoCachePath) ) 01082 { 01083 Exception ex(str::form( _("Can't create %s"), _pimpl->options.repoCachePath.c_str()) ); 01084 ZYPP_THROW(ex); 01085 } 01086 RepoStatus raw_metadata_status = metadataStatus(info); 01087 if ( raw_metadata_status.empty() ) 01088 { 01089 /* if there is no cache at this point, we refresh the raw 01090 in case this is the first time - if it's !autorefresh, 01091 we may still refresh */ 01092 refreshMetadata(info, RefreshIfNeeded, progressrcv ); 01093 raw_metadata_status = metadataStatus(info); 01094 } 01095 01096 bool needs_cleaning = false; 01097 if ( isCached( info ) ) 01098 { 01099 MIL << info.alias() << " is already cached." << endl; 01100 RepoStatus cache_status = cacheStatus(info); 01101 01102 if ( cache_status.checksum() == raw_metadata_status.checksum() ) 01103 { 01104 MIL << info.alias() << " cache is up to date with metadata." << endl; 01105 if ( policy == BuildIfNeeded ) { 01106 return; 01107 } 01108 else { 01109 MIL << info.alias() << " cache rebuild is forced" << endl; 01110 } 01111 } 01112 01113 needs_cleaning = true; 01114 } 01115 01116 ProgressData progress(100); 01117 callback::SendReport<ProgressReport> report; 01118 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01119 progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str())); 01120 progress.toMin(); 01121 01122 if (needs_cleaning) 01123 { 01124 cleanCache(info); 01125 } 01126 01127 MIL << info.alias() << " building cache..." << info.type() << endl; 01128 01129 Pathname base = solv_path_for_repoinfo( _pimpl->options, info); 01130 01131 if( filesystem::assert_dir(base) ) 01132 { 01133 Exception ex(str::form( _("Can't create %s"), base.c_str()) ); 01134 ZYPP_THROW(ex); 01135 } 01136 01137 if( ! PathInfo(base).userMayW() ) 01138 { 01139 Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) ); 01140 ZYPP_THROW(ex); 01141 } 01142 Pathname solvfile = base / "solv"; 01143 01144 // do we have type? 01145 repo::RepoType repokind = info.type(); 01146 01147 // if the type is unknown, try probing. 01148 switch ( repokind.toEnum() ) 01149 { 01150 case RepoType::NONE_e: 01151 // unknown, probe the local metadata 01152 repokind = probe( productdatapath.asUrl() ); 01153 break; 01154 default: 01155 break; 01156 } 01157 01158 MIL << "repo type is " << repokind << endl; 01159 01160 switch ( repokind.toEnum() ) 01161 { 01162 case RepoType::RPMMD_e : 01163 case RepoType::YAST2_e : 01164 case RepoType::RPMPLAINDIR_e : 01165 { 01166 // Take care we unlink the solvfile on exception 01167 ManagedFile guard( solvfile, filesystem::unlink ); 01168 scoped_ptr<MediaMounter> forPlainDirs; 01169 01170 ExternalProgram::Arguments cmd; 01171 cmd.push_back( "repo2solv.sh" ); 01172 01173 // repo2solv expects -o as 1st arg! 01174 cmd.push_back( "-o" ); 01175 cmd.push_back( solvfile.asString() ); 01176 01177 if ( repokind == RepoType::RPMPLAINDIR ) 01178 { 01179 forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) ); 01180 // recusive for plaindir as 2nd arg! 01181 cmd.push_back( "-R" ); 01182 // FIXME this does only work form dir: URLs 01183 cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() ); 01184 } 01185 else 01186 cmd.push_back( productdatapath.asString() ); 01187 01188 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout ); 01189 std::string errdetail; 01190 01191 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) { 01192 WAR << " " << output; 01193 if ( errdetail.empty() ) { 01194 errdetail = prog.command(); 01195 errdetail += '\n'; 01196 } 01197 errdetail += output; 01198 } 01199 01200 int ret = prog.close(); 01201 if ( ret != 0 ) 01202 { 01203 RepoException ex(str::form( _("Failed to cache repo (%d)."), ret )); 01204 ex.remember( errdetail ); 01205 ZYPP_THROW(ex); 01206 } 01207 01208 // We keep it. 01209 guard.resetDispose(); 01210 } 01211 break; 01212 default: 01213 ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") )); 01214 break; 01215 } 01216 // update timestamp and checksum 01217 setCacheStatus(info, raw_metadata_status); 01218 MIL << "Commit cache.." << endl; 01219 progress.toMax(); 01220 } 01221 01223 01224 repo::RepoType RepoManager::probe( const Url & url ) const 01225 { return probe( url, Pathname() ); } 01226 01227 repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const 01228 { 01229 MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl; 01230 01231 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() ) 01232 { 01233 // Handle non existing local directory in advance, as 01234 // MediaSetAccess does not support it. 01235 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl; 01236 return repo::RepoType::NONE; 01237 } 01238 01239 // prepare exception to be thrown if the type could not be determined 01240 // due to a media exception. We can't throw right away, because of some 01241 // problems with proxy servers returning an incorrect error 01242 // on ftp file-not-found(bnc #335906). Instead we'll check another types 01243 // before throwing. 01244 01245 // TranslatorExplanation '%s' is an URL 01246 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() )); 01247 bool gotMediaException = false; 01248 try 01249 { 01250 MediaSetAccess access(url); 01251 try 01252 { 01253 if ( access.doesFileExist(path/"/repodata/repomd.xml") ) 01254 { 01255 MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl; 01256 return repo::RepoType::RPMMD; 01257 } 01258 } 01259 catch ( const media::MediaException &e ) 01260 { 01261 ZYPP_CAUGHT(e); 01262 DBG << "problem checking for repodata/repomd.xml file" << endl; 01263 enew.remember(e); 01264 gotMediaException = true; 01265 } 01266 01267 try 01268 { 01269 if ( access.doesFileExist(path/"/content") ) 01270 { 01271 MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl; 01272 return repo::RepoType::YAST2; 01273 } 01274 } 01275 catch ( const media::MediaException &e ) 01276 { 01277 ZYPP_CAUGHT(e); 01278 DBG << "problem checking for content file" << endl; 01279 enew.remember(e); 01280 gotMediaException = true; 01281 } 01282 01283 // if it is a non-downloading URL denoting a directory 01284 if ( ! url.schemeIsDownloading() ) 01285 { 01286 MediaMounter media( url ); 01287 if ( PathInfo(media.getPathName()/path).isDir() ) 01288 { 01289 // allow empty dirs for now 01290 MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl; 01291 return repo::RepoType::RPMPLAINDIR; 01292 } 01293 } 01294 } 01295 catch ( const Exception &e ) 01296 { 01297 ZYPP_CAUGHT(e); 01298 // TranslatorExplanation '%s' is an URL 01299 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() )); 01300 enew.remember(e); 01301 ZYPP_THROW(enew); 01302 } 01303 01304 if (gotMediaException) 01305 ZYPP_THROW(enew); 01306 01307 MIL << "Probed type NONE at " << url << " (" << path << ")" << endl; 01308 return repo::RepoType::NONE; 01309 } 01310 01312 01313 void RepoManager::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv ) 01314 { 01315 MIL << "Going to clean up garbage in cache dirs" << endl; 01316 01317 ProgressData progress(300); 01318 progress.sendTo(progressrcv); 01319 progress.toMin(); 01320 01321 std::list<Pathname> cachedirs; 01322 cachedirs.push_back(_pimpl->options.repoRawCachePath); 01323 cachedirs.push_back(_pimpl->options.repoPackagesCachePath); 01324 cachedirs.push_back(_pimpl->options.repoSolvCachePath); 01325 01326 for_( dir, cachedirs.begin(), cachedirs.end() ) 01327 { 01328 if ( PathInfo(*dir).isExist() ) 01329 { 01330 std::list<Pathname> entries; 01331 if ( filesystem::readdir( entries, *dir, false ) != 0 ) 01332 // TranslatorExplanation '%s' is a pathname 01333 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str()))); 01334 01335 unsigned sdircount = entries.size(); 01336 unsigned sdircurrent = 1; 01337 for_( subdir, entries.begin(), entries.end() ) 01338 { 01339 // if it does not belong known repo, make it disappear 01340 bool found = false; 01341 for_( r, repoBegin(), repoEnd() ) 01342 if ( subdir->basename() == r->escaped_alias() ) 01343 { found = true; break; } 01344 01345 if ( ! found ) 01346 filesystem::recursive_rmdir( *subdir ); 01347 01348 progress.set( progress.val() + sdircurrent * 100 / sdircount ); 01349 ++sdircurrent; 01350 } 01351 } 01352 else 01353 progress.set( progress.val() + 100 ); 01354 } 01355 progress.toMax(); 01356 } 01357 01359 01360 void RepoManager::cleanCache( const RepoInfo &info, 01361 const ProgressData::ReceiverFnc & progressrcv ) 01362 { 01363 ProgressData progress(100); 01364 progress.sendTo(progressrcv); 01365 progress.toMin(); 01366 01367 MIL << "Removing raw metadata cache for " << info.alias() << endl; 01368 filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info)); 01369 01370 progress.toMax(); 01371 } 01372 01374 01375 bool RepoManager::isCached( const RepoInfo &info ) const 01376 { 01377 return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist(); 01378 } 01379 01380 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const 01381 { 01382 01383 Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie"; 01384 01385 return RepoStatus::fromCookieFile(cookiefile); 01386 } 01387 01388 void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status ) 01389 { 01390 Pathname base = solv_path_for_repoinfo(_pimpl->options, info); 01391 filesystem::assert_dir(base); 01392 Pathname cookiefile = base / "cookie"; 01393 01394 status.saveToCookieFile(cookiefile); 01395 } 01396 01397 void RepoManager::loadFromCache( const RepoInfo & info, 01398 const ProgressData::ReceiverFnc & progressrcv ) 01399 { 01400 assert_alias(info); 01401 Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv"; 01402 01403 if ( ! PathInfo(solvfile).isExist() ) 01404 ZYPP_THROW(RepoNotCachedException(info)); 01405 01406 try 01407 { 01408 Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info ); 01409 // test toolversion in order to rebuild solv file in case 01410 // it was written by an old libsolv-tool parser. 01411 // 01412 // Known version strings used: 01413 // - <no string> 01414 // - "1.0" 01415 // 01416 sat::LookupRepoAttr toolversion( sat::SolvAttr::repositoryToolVersion, repo ); 01417 if ( toolversion.begin().asString().empty() ) 01418 { 01419 repo.eraseFromPool(); 01420 ZYPP_THROW(Exception("Solv-file was created by old parser.")); 01421 } 01422 // else: up-to-date (or even newer). 01423 } 01424 catch ( const Exception & exp ) 01425 { 01426 ZYPP_CAUGHT( exp ); 01427 MIL << "Try to handle exception by rebuilding the solv-file" << endl; 01428 cleanCache( info, progressrcv ); 01429 buildCache( info, BuildIfNeeded, progressrcv ); 01430 01431 sat::Pool::instance().addRepoSolv( solvfile, info ); 01432 } 01433 } 01434 01436 01437 void RepoManager::addRepository( const RepoInfo &info, 01438 const ProgressData::ReceiverFnc & progressrcv ) 01439 { 01440 assert_alias(info); 01441 01442 ProgressData progress(100); 01443 callback::SendReport<ProgressReport> report; 01444 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01445 progress.name(str::form(_("Adding repository '%s'"), info.label().c_str())); 01446 progress.toMin(); 01447 01448 MIL << "Try adding repo " << info << endl; 01449 01450 RepoInfo tosave = info; 01451 if(_pimpl->repos.find(tosave)!= _pimpl->repos.end()) 01452 ZYPP_THROW(RepoAlreadyExistsException(info)); 01453 01454 // check the first url for now 01455 if ( _pimpl->options.probe ) 01456 { 01457 DBG << "unknown repository type, probing" << endl; 01458 01459 RepoType probedtype; 01460 probedtype = probe( *tosave.baseUrlsBegin(), info.path() ); 01461 if ( tosave.baseUrlsSize() > 0 ) 01462 { 01463 if ( probedtype == RepoType::NONE ) 01464 ZYPP_THROW(RepoUnknownTypeException()); 01465 else 01466 tosave.setType(probedtype); 01467 } 01468 } 01469 01470 progress.set(50); 01471 01472 // assert the directory exists 01473 filesystem::assert_dir(_pimpl->options.knownReposPath); 01474 01475 Pathname repofile = _pimpl->generateNonExistingName( 01476 _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave)); 01477 // now we have a filename that does not exists 01478 MIL << "Saving repo in " << repofile << endl; 01479 01480 std::ofstream file(repofile.c_str()); 01481 if (!file) 01482 { 01483 // TranslatorExplanation '%s' is a filename 01484 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))); 01485 } 01486 01487 tosave.dumpAsIniOn(file); 01488 tosave.setFilepath(repofile); 01489 tosave.setMetadataPath( metadataPath( tosave ) ); 01490 tosave.setPackagesPath( packagesPath( tosave ) ); 01491 { 01492 // We chould fix the API as we must injet those paths 01493 // into the repoinfo in order to keep it usable. 01494 RepoInfo & oinfo( const_cast<RepoInfo &>(info) ); 01495 oinfo.setMetadataPath( metadataPath( tosave ) ); 01496 oinfo.setPackagesPath( packagesPath( tosave ) ); 01497 } 01498 _pimpl->repos.insert(tosave); 01499 01500 progress.set(90); 01501 01502 // check for credentials in Urls 01503 bool havePasswords = false; 01504 for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() ) 01505 if ( urlit->hasCredentialsInAuthority() ) 01506 { 01507 havePasswords = true; 01508 break; 01509 } 01510 // save the credentials 01511 if ( havePasswords ) 01512 { 01513 media::CredentialManager cm( 01514 media::CredManagerOptions(_pimpl->options.rootDir) ); 01515 01516 for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd()) 01517 if (urlit->hasCredentialsInAuthority()) 01519 cm.saveInUser(media::AuthData(*urlit)); 01520 } 01521 01522 HistoryLog().addRepository(tosave); 01523 01524 progress.toMax(); 01525 MIL << "done" << endl; 01526 } 01527 01528 void RepoManager::addRepositories( const Url &url, 01529 const ProgressData::ReceiverFnc & progressrcv ) 01530 { 01531 std::list<RepoInfo> repos = readRepoFile(url); 01532 for ( std::list<RepoInfo>::const_iterator it = repos.begin(); 01533 it != repos.end(); 01534 ++it ) 01535 { 01536 // look if the alias is in the known repos. 01537 for_ ( kit, repoBegin(), repoEnd() ) 01538 { 01539 if ( (*it).alias() == (*kit).alias() ) 01540 { 01541 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl; 01542 ZYPP_THROW(RepoAlreadyExistsException(*it)); 01543 } 01544 } 01545 } 01546 01547 std::string filename = Pathname(url.getPathName()).basename(); 01548 01549 if ( filename == Pathname() ) 01550 { 01551 // TranslatorExplanation '%s' is an URL 01552 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() ))); 01553 } 01554 01555 // assert the directory exists 01556 filesystem::assert_dir(_pimpl->options.knownReposPath); 01557 01558 Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename); 01559 // now we have a filename that does not exists 01560 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl; 01561 01562 std::ofstream file(repofile.c_str()); 01563 if (!file) 01564 { 01565 // TranslatorExplanation '%s' is a filename 01566 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))); 01567 } 01568 01569 for ( std::list<RepoInfo>::iterator it = repos.begin(); 01570 it != repos.end(); 01571 ++it ) 01572 { 01573 MIL << "Saving " << (*it).alias() << endl; 01574 it->setFilepath(repofile.asString()); 01575 it->dumpAsIniOn(file); 01576 _pimpl->repos.insert(*it); 01577 01578 HistoryLog(_pimpl->options.rootDir).addRepository(*it); 01579 } 01580 01581 MIL << "done" << endl; 01582 } 01583 01585 01586 void RepoManager::removeRepository( const RepoInfo & info, 01587 const ProgressData::ReceiverFnc & progressrcv) 01588 { 01589 ProgressData progress; 01590 callback::SendReport<ProgressReport> report; 01591 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01592 progress.name(str::form(_("Removing repository '%s'"), info.label().c_str())); 01593 01594 MIL << "Going to delete repo " << info.alias() << endl; 01595 01596 for_( it, repoBegin(), repoEnd() ) 01597 { 01598 // they can be the same only if the provided is empty, that means 01599 // the provided repo has no alias 01600 // then skip 01601 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) ) 01602 continue; 01603 01604 // TODO match by url 01605 01606 // we have a matcing repository, now we need to know 01607 // where it does come from. 01608 RepoInfo todelete = *it; 01609 if (todelete.filepath().empty()) 01610 { 01611 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") )); 01612 } 01613 else 01614 { 01615 // figure how many repos are there in the file: 01616 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath()); 01617 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) ) 01618 { 01619 // easy, only this one, just delete the file 01620 if ( filesystem::unlink(todelete.filepath()) != 0 ) 01621 { 01622 // TranslatorExplanation '%s' is a filename 01623 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), todelete.filepath().c_str() ))); 01624 } 01625 MIL << todelete.alias() << " sucessfully deleted." << endl; 01626 } 01627 else 01628 { 01629 // there are more repos in the same file 01630 // write them back except the deleted one. 01631 //TmpFile tmp; 01632 //std::ofstream file(tmp.path().c_str()); 01633 01634 // assert the directory exists 01635 filesystem::assert_dir(todelete.filepath().dirname()); 01636 01637 std::ofstream file(todelete.filepath().c_str()); 01638 if (!file) 01639 { 01640 // TranslatorExplanation '%s' is a filename 01641 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() ))); 01642 } 01643 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin(); 01644 fit != filerepos.end(); 01645 ++fit ) 01646 { 01647 if ( (*fit).alias() != todelete.alias() ) 01648 (*fit).dumpAsIniOn(file); 01649 } 01650 } 01651 01652 CombinedProgressData subprogrcv(progress, 70); 01653 CombinedProgressData cleansubprogrcv(progress, 30); 01654 // now delete it from cache 01655 if ( isCached(todelete) ) 01656 cleanCache( todelete, subprogrcv); 01657 // now delete metadata (#301037) 01658 cleanMetadata( todelete, cleansubprogrcv); 01659 _pimpl->repos.erase(todelete); 01660 MIL << todelete.alias() << " sucessfully deleted." << endl; 01661 HistoryLog(_pimpl->options.rootDir).removeRepository(todelete); 01662 return; 01663 } // else filepath is empty 01664 01665 } 01666 // should not be reached on a sucess workflow 01667 ZYPP_THROW(RepoNotFoundException(info)); 01668 } 01669 01671 01672 void RepoManager::modifyRepository( const std::string &alias, 01673 const RepoInfo & newinfo_r, 01674 const ProgressData::ReceiverFnc & progressrcv ) 01675 { 01676 RepoInfo toedit = getRepositoryInfo(alias); 01677 RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data 01678 01679 // check if the new alias already exists when renaming the repo 01680 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) ) 01681 { 01682 ZYPP_THROW(RepoAlreadyExistsException(newinfo)); 01683 } 01684 01685 if (toedit.filepath().empty()) 01686 { 01687 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") )); 01688 } 01689 else 01690 { 01691 // figure how many repos are there in the file: 01692 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath()); 01693 01694 // there are more repos in the same file 01695 // write them back except the deleted one. 01696 //TmpFile tmp; 01697 //std::ofstream file(tmp.path().c_str()); 01698 01699 // assert the directory exists 01700 filesystem::assert_dir(toedit.filepath().dirname()); 01701 01702 std::ofstream file(toedit.filepath().c_str()); 01703 if (!file) 01704 { 01705 // TranslatorExplanation '%s' is a filename 01706 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() ))); 01707 } 01708 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin(); 01709 fit != filerepos.end(); 01710 ++fit ) 01711 { 01712 // if the alias is different, dump the original 01713 // if it is the same, dump the provided one 01714 if ( (*fit).alias() != toedit.alias() ) 01715 (*fit).dumpAsIniOn(file); 01716 else 01717 newinfo.dumpAsIniOn(file); 01718 } 01719 01720 newinfo.setFilepath(toedit.filepath()); 01721 _pimpl->repos.erase(toedit); 01722 _pimpl->repos.insert(newinfo); 01723 HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo); 01724 MIL << "repo " << alias << " modified" << endl; 01725 } 01726 } 01727 01729 01730 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias, 01731 const ProgressData::ReceiverFnc & progressrcv ) 01732 { 01733 RepoInfo info; 01734 info.setAlias(alias); 01735 RepoConstIterator it = _pimpl->repos.find( info ); 01736 if( it == repoEnd() ) 01737 ZYPP_THROW(RepoNotFoundException(info)); 01738 else 01739 return *it; 01740 } 01741 01743 01744 RepoInfo RepoManager::getRepositoryInfo( const Url & url, 01745 const url::ViewOption & urlview, 01746 const ProgressData::ReceiverFnc & progressrcv ) 01747 { 01748 for_( it, repoBegin(), repoEnd() ) 01749 { 01750 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin(); 01751 urlit != (*it).baseUrlsEnd(); 01752 ++urlit) 01753 { 01754 if ((*urlit).asString(urlview) == url.asString(urlview)) 01755 return *it; 01756 } 01757 } 01758 RepoInfo info; 01759 info.setBaseUrl(url); 01760 ZYPP_THROW(RepoNotFoundException(info)); 01761 } 01762 01764 // 01765 // Services 01766 // 01768 01769 bool RepoManager::serviceEmpty() const 01770 { return _pimpl->services.empty(); } 01771 01772 RepoManager::ServiceSizeType RepoManager::serviceSize() const 01773 { return _pimpl->services.size(); } 01774 01775 RepoManager::ServiceConstIterator RepoManager::serviceBegin() const 01776 { return _pimpl->services.begin(); } 01777 01778 RepoManager::ServiceConstIterator RepoManager::serviceEnd() const 01779 { return _pimpl->services.end(); } 01780 01781 ServiceInfo RepoManager::getService( const std::string & alias ) const 01782 { 01783 for_( it, serviceBegin(), serviceEnd() ) 01784 if ( it->alias() == alias ) 01785 return *it; 01786 return ServiceInfo::noService; 01787 } 01788 01789 bool RepoManager::hasService( const std::string & alias ) const 01790 { 01791 for_( it, serviceBegin(), serviceEnd() ) 01792 if ( it->alias() == alias ) 01793 return true; 01794 return false; 01795 } 01796 01798 01799 void RepoManager::addService( const std::string & alias, const Url & url ) 01800 { 01801 addService( ServiceInfo(alias, url) ); 01802 } 01803 01804 void RepoManager::addService( const ServiceInfo & service ) 01805 { 01806 assert_alias( service ); 01807 01808 // check if service already exists 01809 if ( hasService( service.alias() ) ) 01810 ZYPP_THROW( ServiceAlreadyExistsException( service ) ); 01811 01812 // Writable ServiceInfo is needed to save the location 01813 // of the .service file. Finaly insert into the service list. 01814 ServiceInfo toSave( service ); 01815 _pimpl->saveService( toSave ); 01816 _pimpl->services.insert( toSave ); 01817 01818 // check for credentials in Url (username:password, not ?credentials param) 01819 if ( toSave.url().hasCredentialsInAuthority() ) 01820 { 01821 media::CredentialManager cm( 01822 media::CredManagerOptions(_pimpl->options.rootDir) ); 01823 01825 cm.saveInUser(media::AuthData(toSave.url())); 01826 } 01827 01828 MIL << "added service " << toSave.alias() << endl; 01829 } 01830 01832 01833 void RepoManager::removeService( const std::string & alias ) 01834 { 01835 MIL << "Going to delete repo " << alias << endl; 01836 01837 const ServiceInfo & service = getService( alias ); 01838 01839 Pathname location = service.filepath(); 01840 if( location.empty() ) 01841 { 01842 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") )); 01843 } 01844 01845 ServiceSet tmpSet; 01846 parser::ServiceFileReader( location, ServiceCollector(tmpSet) ); 01847 01848 // only one service definition in the file 01849 if ( tmpSet.size() == 1 ) 01850 { 01851 if ( filesystem::unlink(location) != 0 ) 01852 { 01853 // TranslatorExplanation '%s' is a filename 01854 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), location.c_str() ))); 01855 } 01856 MIL << alias << " sucessfully deleted." << endl; 01857 } 01858 else 01859 { 01860 filesystem::assert_dir(location.dirname()); 01861 01862 std::ofstream file(location.c_str()); 01863 if( !file ) 01864 { 01865 // TranslatorExplanation '%s' is a filename 01866 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() ))); 01867 } 01868 01869 for_(it, tmpSet.begin(), tmpSet.end()) 01870 { 01871 if( it->alias() != alias ) 01872 it->dumpAsIniOn(file); 01873 } 01874 01875 MIL << alias << " sucessfully deleted from file " << location << endl; 01876 } 01877 01878 // now remove all repositories added by this service 01879 RepoCollector rcollector; 01880 getRepositoriesInService( alias, 01881 boost::make_function_output_iterator( 01882 bind( &RepoCollector::collect, &rcollector, _1 ) ) ); 01883 // cannot do this directly in getRepositoriesInService - would invalidate iterators 01884 for_(rit, rcollector.repos.begin(), rcollector.repos.end()) 01885 removeRepository(*rit); 01886 } 01887 01888 void RepoManager::removeService( const ServiceInfo & service ) 01889 { removeService(service.alias()); } 01890 01892 01893 void RepoManager::refreshServices() 01894 { 01895 // copy the set of services since refreshService 01896 // can eventually invalidate the iterator 01897 ServiceSet services( serviceBegin(), serviceEnd() ); 01898 for_( it, services.begin(), services.end() ) 01899 { 01900 if ( !it->enabled() ) 01901 continue; 01902 01903 try { 01904 refreshService(*it); 01905 } 01906 catch ( const repo::ServicePluginInformalException & e ) 01907 { ;/* ignore ServicePluginInformalException */ } 01908 } 01909 } 01910 01911 void RepoManager::refreshService( const ServiceInfo & service ) 01912 { refreshService( service.alias() ); } 01913 01914 void RepoManager::refreshService( const std::string & alias ) 01915 { 01916 ServiceInfo service( getService( alias ) ); 01917 assert_alias( service ); 01918 assert_url( service ); 01919 // NOTE: It might be necessary to modify and rewrite the service info. 01920 // Either when probing the type, or when adjusting the repositories 01921 // enable/disable state.: 01922 bool serviceModified = false; 01923 MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl; 01924 01926 01927 // if the type is unknown, try probing. 01928 if ( service.type() == repo::ServiceType::NONE ) 01929 { 01930 repo::ServiceType type = probeService( service.url() ); 01931 if ( type != ServiceType::NONE ) 01932 { 01933 service.setProbedType( type ); // lazy init! 01934 serviceModified = true; 01935 } 01936 } 01937 01938 // get target distro identifier 01939 std::string servicesTargetDistro = _pimpl->options.servicesTargetDistro; 01940 if ( servicesTargetDistro.empty() ) 01941 { 01942 servicesTargetDistro = Target::targetDistribution( Pathname() ); 01943 } 01944 DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl; 01945 01946 // parse it 01947 RepoCollector collector(servicesTargetDistro); 01948 // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException 01949 // which is actually a notification. Using an exception for this 01950 // instead of signal/callback is bad. Needs to be fixed here, in refreshServices() 01951 // and in zypper. 01952 std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack; 01953 try { 01954 ServiceRepos repos(service, bind( &RepoCollector::collect, &collector, _1 )); 01955 } 01956 catch ( const repo::ServicePluginInformalException & e ) 01957 { 01958 /* ignore ServicePluginInformalException and throw later */ 01959 uglyHack.first = true; 01960 uglyHack.second = e; 01961 } 01962 01963 // set service alias and base url for all collected repositories 01964 for_( it, collector.repos.begin(), collector.repos.end() ) 01965 { 01966 // if the repo url was not set by the repoindex parser, set service's url 01967 Url url; 01968 01969 if ( it->baseUrlsEmpty() ) 01970 url = service.url(); 01971 else 01972 { 01973 // service repo can contain only one URL now, so no need to iterate. 01974 url = *it->baseUrlsBegin(); 01975 } 01976 01977 // libzypp currently has problem with separate url + path handling 01978 // so just append the path to the baseurl 01979 if ( !it->path().empty() ) 01980 { 01981 Pathname path(url.getPathName()); 01982 path /= it->path(); 01983 url.setPathName( path.asString() ); 01984 it->setPath(""); 01985 } 01986 01987 // Prepend service alias: 01988 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) ); 01989 01990 // save the url 01991 it->setBaseUrl( url ); 01992 // set refrence to the parent service 01993 it->setService( service.alias() ); 01994 } 01995 01997 // Now compare collected repos with the ones in the system... 01998 // 01999 RepoInfoList oldRepos; 02000 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) ); 02001 02002 // find old repositories to remove... 02003 for_( it, oldRepos.begin(), oldRepos.end() ) 02004 { 02005 if ( ! foundAliasIn( it->alias(), collector.repos ) ) 02006 { 02007 if ( it->enabled() && ! service.repoToDisableFind( it->alias() ) ) 02008 { 02009 DBG << "Service removes enabled repo " << it->alias() << endl; 02010 service.addRepoToEnable( it->alias() ); 02011 serviceModified = true; 02012 } 02013 else 02014 { 02015 DBG << "Service removes disabled repo " << it->alias() << endl; 02016 } 02017 removeRepository( *it ); 02018 } 02019 } 02020 02022 // create missing repositories and modify exising ones if needed... 02023 for_( it, collector.repos.begin(), collector.repos.end() ) 02024 { 02025 // Service explicitly requests the repo being enabled? 02026 // Service explicitly requests the repo being disabled? 02027 // And hopefully not both ;) If so, enable wins. 02028 bool beEnabled = service.repoToEnableFind( it->alias() ); 02029 bool beDisabled = service.repoToDisableFind( it->alias() ); 02030 02031 // Make sure the service repo is created with the 02032 // appropriate enable 02033 if ( beEnabled ) it->setEnabled(true); 02034 if ( beDisabled ) it->setEnabled(false); 02035 02036 if ( beEnabled ) 02037 { 02038 // Remove from enable request list. 02039 // NOTE: repoToDisable is handled differently. 02040 // It gets cleared on each refresh. 02041 service.delRepoToEnable( it->alias() ); 02042 serviceModified = true; 02043 } 02044 02045 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) ); 02046 if ( oldRepo == oldRepos.end() ) 02047 { 02048 // Not found in oldRepos ==> a new repo to add 02049 02050 // At that point check whether a repo with the same alias 02051 // exists outside this service. Maybe forcefully re-alias 02052 // the existing repo? 02053 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl; 02054 addRepository( *it ); 02055 02056 // save repo credentials 02057 // ma@: task for modifyRepository? 02058 } 02059 else 02060 { 02061 // ==> an exising repo to check 02062 bool oldRepoModified = false; 02063 02064 // changed enable? 02065 if ( beEnabled ) 02066 { 02067 if ( ! oldRepo->enabled() ) 02068 { 02069 DBG << "Service repo " << it->alias() << " gets enabled" << endl; 02070 oldRepo->setEnabled( true ); 02071 oldRepoModified = true; 02072 } 02073 else 02074 { 02075 DBG << "Service repo " << it->alias() << " stays enabled" << endl; 02076 } 02077 } 02078 else if ( beDisabled ) 02079 { 02080 if ( oldRepo->enabled() ) 02081 { 02082 DBG << "Service repo " << it->alias() << " gets disabled" << endl; 02083 oldRepo->setEnabled( false ); 02084 oldRepoModified = true; 02085 } 02086 else 02087 { 02088 DBG << "Service repo " << it->alias() << " stays disabled" << endl; 02089 } 02090 } 02091 else 02092 { 02093 DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl; 02094 } 02095 02096 // changed url? 02097 // service repo can contain only one URL now, so no need to iterate. 02098 if ( oldRepo->url() != it->url() ) 02099 { 02100 DBG << "Service repo " << it->alias() << " gets new URL " << it->url() << endl; 02101 oldRepo->setBaseUrl( it->url() ); 02102 oldRepoModified = true; 02103 } 02104 02105 // save if modified: 02106 if ( oldRepoModified ) 02107 { 02108 modifyRepository( oldRepo->alias(), *oldRepo ); 02109 } 02110 } 02111 } 02112 02113 // Unlike reposToEnable, reposToDisable is always cleared after refresh. 02114 if ( ! service.reposToDisableEmpty() ) 02115 { 02116 service.clearReposToDisable(); 02117 serviceModified = true; 02118 } 02119 02121 // save service if modified: 02122 if ( serviceModified ) 02123 { 02124 // write out modified service file. 02125 modifyService( service.alias(), service ); 02126 } 02127 02128 if ( uglyHack.first ) 02129 { 02130 throw( uglyHack.second ); // intentionally not ZYPP_THROW 02131 } 02132 } 02133 02135 02136 void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & newService) 02137 { 02138 MIL << "Going to modify service " << oldAlias << endl; 02139 02140 // we need a writable copy to link it to the file where 02141 // it is saved if we modify it 02142 ServiceInfo service(newService); 02143 02144 if ( service.type() == ServiceType::PLUGIN ) 02145 { 02146 MIL << "Not modifying plugin service '" << oldAlias << "'" << endl; 02147 return; 02148 } 02149 02150 const ServiceInfo & oldService = getService(oldAlias); 02151 02152 Pathname location = oldService.filepath(); 02153 if( location.empty() ) 02154 { 02155 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") )); 02156 } 02157 02158 // remember: there may multiple services being defined in one file: 02159 ServiceSet tmpSet; 02160 parser::ServiceFileReader( location, ServiceCollector(tmpSet) ); 02161 02162 filesystem::assert_dir(location.dirname()); 02163 std::ofstream file(location.c_str()); 02164 for_(it, tmpSet.begin(), tmpSet.end()) 02165 { 02166 if( *it != oldAlias ) 02167 it->dumpAsIniOn(file); 02168 } 02169 service.dumpAsIniOn(file); 02170 file.close(); 02171 service.setFilepath(location); 02172 02173 _pimpl->services.erase(oldAlias); 02174 _pimpl->services.insert(service); 02175 02176 // changed properties affecting also repositories 02177 if( oldAlias != service.alias() // changed alias 02178 || oldService.enabled() != service.enabled() // changed enabled status 02179 ) 02180 { 02181 std::vector<RepoInfo> toModify; 02182 getRepositoriesInService(oldAlias, std::back_inserter(toModify)); 02183 for_( it, toModify.begin(), toModify.end() ) 02184 { 02185 if (oldService.enabled() && !service.enabled()) 02186 it->setEnabled(false); 02187 else if (!oldService.enabled() && service.enabled()) 02188 { 02191 } 02192 else 02193 it->setService(service.alias()); 02194 modifyRepository(it->alias(), *it); 02195 } 02196 } 02197 02199 } 02200 02202 02203 repo::ServiceType RepoManager::probeService( const Url &url ) const 02204 { 02205 try 02206 { 02207 MediaSetAccess access(url); 02208 if ( access.doesFileExist("/repo/repoindex.xml") ) 02209 return repo::ServiceType::RIS; 02210 } 02211 catch ( const media::MediaException &e ) 02212 { 02213 ZYPP_CAUGHT(e); 02214 // TranslatorExplanation '%s' is an URL 02215 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() )); 02216 enew.remember(e); 02217 ZYPP_THROW(enew); 02218 } 02219 catch ( const Exception &e ) 02220 { 02221 ZYPP_CAUGHT(e); 02222 // TranslatorExplanation '%s' is an URL 02223 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() )); 02224 enew.remember(e); 02225 ZYPP_THROW(enew); 02226 } 02227 02228 return repo::ServiceType::NONE; 02229 } 02230 02232 02233 std::ostream & operator<<( std::ostream & str, const RepoManager & obj ) 02234 { 02235 return str << *obj._pimpl; 02236 } 02237 02239 } // namespace zypp