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 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
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
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
00262
00263 }
00264 }
00265 return repos;
00266 }
00267
00269
00270 std::list<RepoInfo> readRepoFile(const Url & repo_file)
00271 {
00272
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
00292
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
00303
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
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
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
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
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
00537 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
00538 }
00539
00540
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
00564 Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
00565 (*it).setMetadataPath(metadata_path);
00566
00567
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
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
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
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
00711
00712
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
00725 repokind = probe( productdatapath.asUrl() );
00726
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
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
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
00789 if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
00790 {
00791
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
00818 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
00819
00820 repo::RepoType repokind = info.type();
00821
00822 switch ( repokind.toEnum() )
00823 {
00824 case RepoType::NONE_e:
00825
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;
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
00913 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
00914
00915
00916 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
00917 {
00918 try
00919 {
00920 Url url(*it);
00921
00922
00923
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
00932 switch ( repokind.toEnum() )
00933 {
00934 case RepoType::NONE_e:
00935
00936 repokind = probe( *it, info.path() );
00937
00938 if (repokind.toEnum() != RepoType::NONE_e)
00939 {
00940
00941 info.setProbedType( repokind );
00942
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
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
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
01029
01030 filesystem::exchange( tmpdir.path(), mediarootpath );
01031
01032
01033 return;
01034 }
01035 catch ( const Exception &e )
01036 {
01037 ZYPP_CAUGHT(e);
01038 ERR << "Trying another url..." << endl;
01039
01040
01041
01042
01043 if (it == info.baseUrlsBegin())
01044 rexception.remember(e);
01045 }
01046 }
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
01090
01091
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
01145 repo::RepoType repokind = info.type();
01146
01147
01148 switch ( repokind.toEnum() )
01149 {
01150 case RepoType::NONE_e:
01151
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
01167 ManagedFile guard( solvfile, filesystem::unlink );
01168 scoped_ptr<MediaMounter> forPlainDirs;
01169
01170 ExternalProgram::Arguments cmd;
01171 cmd.push_back( "repo2solv.sh" );
01172
01173
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
01181 cmd.push_back( "-R" );
01182
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
01209 guard.resetDispose();
01210 }
01211 break;
01212 default:
01213 ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") ));
01214 break;
01215 }
01216
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
01234
01235 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
01236 return repo::RepoType::NONE;
01237 }
01238
01239
01240
01241
01242
01243
01244
01245
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
01284 if ( ! url.schemeIsDownloading() )
01285 {
01286 MediaMounter media( url );
01287 if ( PathInfo(media.getPathName()/path).isDir() )
01288 {
01289
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
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
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
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
01410
01411
01412
01413
01414
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
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
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
01473 filesystem::assert_dir(_pimpl->options.knownReposPath);
01474
01475 Pathname repofile = _pimpl->generateNonExistingName(
01476 _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
01477
01478 MIL << "Saving repo in " << repofile << endl;
01479
01480 std::ofstream file(repofile.c_str());
01481 if (!file)
01482 {
01483
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
01493
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
01503 bool havePasswords = false;
01504 for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() )
01505 if ( urlit->hasCredentialsInAuthority() )
01506 {
01507 havePasswords = true;
01508 break;
01509 }
01510
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
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
01552 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
01553 }
01554
01555
01556 filesystem::assert_dir(_pimpl->options.knownReposPath);
01557
01558 Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
01559
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
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
01599
01600
01601 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
01602 continue;
01603
01604
01605
01606
01607
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
01616 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
01617 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
01618 {
01619
01620 if ( filesystem::unlink(todelete.filepath()) != 0 )
01621 {
01622
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
01630
01631
01632
01633
01634
01635 filesystem::assert_dir(todelete.filepath().dirname());
01636
01637 std::ofstream file(todelete.filepath().c_str());
01638 if (!file)
01639 {
01640
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
01655 if ( isCached(todelete) )
01656 cleanCache( todelete, subprogrcv);
01657
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 }
01664
01665 }
01666
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 );
01678
01679
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
01692 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
01693
01694
01695
01696
01697
01698
01699
01700 filesystem::assert_dir(toedit.filepath().dirname());
01701
01702 std::ofstream file(toedit.filepath().c_str());
01703 if (!file)
01704 {
01705
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
01713
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
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
01809 if ( hasService( service.alias() ) )
01810 ZYPP_THROW( ServiceAlreadyExistsException( service ) );
01811
01812
01813
01814 ServiceInfo toSave( service );
01815 _pimpl->saveService( toSave );
01816 _pimpl->services.insert( toSave );
01817
01818
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
01849 if ( tmpSet.size() == 1 )
01850 {
01851 if ( filesystem::unlink(location) != 0 )
01852 {
01853
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
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
01879 RepoCollector rcollector;
01880 getRepositoriesInService( alias,
01881 boost::make_function_output_iterator(
01882 bind( &RepoCollector::collect, &rcollector, _1 ) ) );
01883
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
01896
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 { ; }
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
01920
01921
01922 bool serviceModified = false;
01923 MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
01924
01926
01927
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 );
01934 serviceModified = true;
01935 }
01936 }
01937
01938
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
01947 RepoCollector collector(servicesTargetDistro);
01948
01949
01950
01951
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
01959 uglyHack.first = true;
01960 uglyHack.second = e;
01961 }
01962
01963
01964 for_( it, collector.repos.begin(), collector.repos.end() )
01965 {
01966
01967 Url url;
01968
01969 if ( it->baseUrlsEmpty() )
01970 url = service.url();
01971 else
01972 {
01973
01974 url = *it->baseUrlsBegin();
01975 }
01976
01977
01978
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
01988 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
01989
01990
01991 it->setBaseUrl( url );
01992
01993 it->setService( service.alias() );
01994 }
01995
01997
01998
01999 RepoInfoList oldRepos;
02000 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
02001
02002
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
02023 for_( it, collector.repos.begin(), collector.repos.end() )
02024 {
02025
02026
02027
02028 bool beEnabled = service.repoToEnableFind( it->alias() );
02029 bool beDisabled = service.repoToDisableFind( it->alias() );
02030
02031
02032
02033 if ( beEnabled ) it->setEnabled(true);
02034 if ( beDisabled ) it->setEnabled(false);
02035
02036 if ( beEnabled )
02037 {
02038
02039
02040
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
02049
02050
02051
02052
02053 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
02054 addRepository( *it );
02055
02056
02057
02058 }
02059 else
02060 {
02061
02062 bool oldRepoModified = false;
02063
02064
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
02097
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
02106 if ( oldRepoModified )
02107 {
02108 modifyRepository( oldRepo->alias(), *oldRepo );
02109 }
02110 }
02111 }
02112
02113
02114 if ( ! service.reposToDisableEmpty() )
02115 {
02116 service.clearReposToDisable();
02117 serviceModified = true;
02118 }
02119
02121
02122 if ( serviceModified )
02123 {
02124
02125 modifyService( service.alias(), service );
02126 }
02127
02128 if ( uglyHack.first )
02129 {
02130 throw( uglyHack.second );
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
02141
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
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
02177 if( oldAlias != service.alias()
02178 || oldService.enabled() != service.enabled()
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
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
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 }