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