#include <algorithm>
#include "zypp/base/InputStream.h"
-#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
#include "zypp/base/Gettext.h"
+#include "zypp/base/DefaultIntegral.h"
#include "zypp/base/Function.h"
#include "zypp/base/Regex.h"
#include "zypp/PathInfo.h"
#include "zypp/parser/RepoFileReader.h"
#include "zypp/parser/ServiceFileReader.h"
-#include "zypp/parser/RepoindexFileReader.h"
+#include "zypp/repo/ServiceRepos.h"
#include "zypp/repo/yum/Downloader.h"
#include "zypp/repo/susetags/Downloader.h"
-#include "zypp/parser/plaindir/RepoParser.h"
+#include "zypp/repo/PluginServices.h"
#include "zypp/Target.h" // for Target::targetDistribution() for repo index services
#include "zypp/ZYppFactory.h" // to get the Target from ZYpp instance
#include "zypp/ZYppCallbacks.h"
#include "sat/Pool.h"
-#include "satsolver/pool.h"
-#include "satsolver/repo.h"
-#include "satsolver/repo_solv.h"
using std::endl;
+using std::string;
using namespace zypp::repo;
+#define OPT_PROGRESS const ProgressData::ReceiverFnc & = ProgressData::ReceiverFnc()
+
///////////////////////////////////////////////////////////////////
namespace zypp
-{ /////////////////////////////////////////////////////////////////
-
+{
+ ///////////////////////////////////////////////////////////////////
namespace
{
+ /** Simple media mounter to access non-downloading URLs e.g. for non-local plaindir repos.
+ * \ingroup g_RAII
+ */
+ class MediaMounter
+ {
+ public:
+ /** Ctor provides media access. */
+ MediaMounter( const Url & url_r )
+ {
+ media::MediaManager mediamanager;
+ _mid = mediamanager.open( url_r );
+ mediamanager.attach( _mid );
+ }
+
+ /** Ctor releases the media. */
+ ~MediaMounter()
+ {
+ media::MediaManager mediamanager;
+ mediamanager.release( _mid );
+ mediamanager.close( _mid );
+ }
+
+ /** Convert a path relative to the media into an absolute path.
+ *
+ * Called without argument it returns the path to the medias root directory.
+ */
+ Pathname getPathName( const Pathname & path_r = Pathname() ) const
+ {
+ media::MediaManager mediamanager;
+ return mediamanager.localPath( _mid, path_r );
+ }
+
+ private:
+ media::MediaAccessId _mid;
+ };
+ ///////////////////////////////////////////////////////////////////
+
/** Check if alias_r is present in repo/service container. */
template <class Iterator>
inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r )
template <class Container>
inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r )
{ return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
- }
- ///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : RepoManagerOptions
- //
- ///////////////////////////////////////////////////////////////////
-
- RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
- {
- repoCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
- repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
- repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
- repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
- knownReposPath = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
- knownServicesPath = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
- probe = ZConfig::instance().repo_add_probe();
- if ( getZYpp()->getTarget() )
- {
- servicesTargetDistro = getZYpp()->target()->targetDistribution();
- }
- else
+ /** \short Generate a related filename from a repo/service infos alias */
+ inline std::string filenameFromAlias( const std::string & alias_r, const std::string & stem_r )
{
- DBG << "Target not initialized, using an empty servicesTargetDistro." << endl;
- }
-
- rootDir = root_r;
- }
-
- RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
- {
- RepoManagerOptions ret;
- ret.repoCachePath = root_r;
- ret.repoRawCachePath = root_r/"raw";
- ret.repoSolvCachePath = root_r/"solv";
- ret.repoPackagesCachePath = root_r/"packages";
- ret.knownReposPath = root_r/"repos.d";
- ret.knownServicesPath = root_r/"services.d";
- ret.rootDir = root_r;
- return ret;
- }
+ std::string filename( alias_r );
+ // replace slashes with underscores
+ str::replaceAll( filename, "/", "_" );
- ////////////////////////////////////////////////////////////////////////////
+ filename = Pathname(filename).extend("."+stem_r).asString();
+ MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << endl;
+ return filename;
+ }
- /**
- * \short Simple callback to collect the results
- *
- * Classes like RepoFileParser call the callback
- * once per each repo in a file.
- *
- * Passing this functor as callback, you can collect
- * all results at the end, without dealing with async
- * code.
- *
- * If targetDistro is set, all repos with non-empty RepoInfo::targetDistribution()
- * will be skipped.
- *
- * \todo do this through a separate filter
- */
+ /**
+ * \short Simple callback to collect the results
+ *
+ * Classes like RepoFileReader call the callback
+ * once per each repo in a file.
+ *
+ * Passing this functor as callback, you can collect
+ * all results at the end, without dealing with async
+ * code.
+ *
+ * If targetDistro is set, all repos with non-empty RepoInfo::targetDistribution()
+ * will be skipped.
+ *
+ * \todo do this through a separate filter
+ */
struct RepoCollector : private base::NonCopyable
{
RepoCollector()
&& repo.targetDistribution() != targetDistro)
{
MIL
- << "Skipping repository meant for '" << targetDistro
+ << "Skipping repository meant for '" << repo.targetDistribution()
<< "' distribution (current distro is '"
- << repo.targetDistribution() << "')." << endl;
+ << targetDistro << "')." << endl;
return true;
}
RepoInfoList repos;
std::string targetDistro;
};
+ ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////
-
- /**
- * Reads RepoInfo's from a repo file.
- *
- * \param file pathname of the file to read.
- */
- static std::list<RepoInfo> repositories_in_file( const Pathname & file )
- {
- MIL << "repo file: " << file << endl;
- RepoCollector collector;
- parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
- return collector.repos;
- }
-
- ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Reads RepoInfo's from a repo file.
+ *
+ * \param file pathname of the file to read.
+ */
+ std::list<RepoInfo> repositories_in_file( const Pathname & file )
+ {
+ MIL << "repo file: " << file << endl;
+ RepoCollector collector;
+ parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
+ return std::move(collector.repos);
+ }
- /**
- * \short List of RepoInfo's from a directory
- *
- * Goes trough every file ending with ".repo" in a directory and adds all
- * RepoInfo's contained in that file.
- *
- * \param dir pathname of the directory to read.
- */
- static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
- {
- MIL << "directory " << dir << endl;
- std::list<RepoInfo> repos;
- std::list<Pathname> entries;
- if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
- ZYPP_THROW(Exception("failed to read directory"));
+ ////////////////////////////////////////////////////////////////////////////
- str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
- for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+ /**
+ * \short List of RepoInfo's from a directory
+ *
+ * Goes trough every file ending with ".repo" in a directory and adds all
+ * RepoInfo's contained in that file.
+ *
+ * \param dir pathname of the directory to read.
+ */
+ std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
{
- if (str::regex_match(it->extension(), allowedRepoExt))
+ MIL << "directory " << dir << endl;
+ std::list<RepoInfo> repos;
+ bool nonroot( geteuid() != 0 );
+ if ( nonroot && ! PathInfo(dir).userMayRX() )
{
- std::list<RepoInfo> tmp = repositories_in_file( *it );
- repos.insert( repos.end(), tmp.begin(), tmp.end() );
-
- //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
- //MIL << "ok" << endl;
+ JobReport::warning( formatNAC(_("Cannot read repo directory '%1%': Permission denied")) % dir );
+ }
+ else
+ {
+ std::list<Pathname> entries;
+ if ( filesystem::readdir( entries, dir, false ) != 0 )
+ {
+ // TranslatorExplanation '%s' is a pathname
+ ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+ }
+
+ str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
+ for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
+ {
+ if ( str::regex_match(it->extension(), allowedRepoExt) )
+ {
+ if ( nonroot && ! PathInfo(*it).userMayR() )
+ {
+ JobReport::warning( formatNAC(_("Cannot read repo file '%1%': Permission denied")) % *it );
+ }
+ else
+ {
+ const std::list<RepoInfo> & tmp( repositories_in_file( *it ) );
+ repos.insert( repos.end(), tmp.begin(), tmp.end() );
+ }
+ }
+ }
}
+ return repos;
}
- return repos;
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- std::list<RepoInfo> readRepoFile(const Url & repo_file)
- {
- // no interface to download a specific file, using workaround:
- //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
- Url url(repo_file);
- Pathname path(url.getPathName());
- url.setPathName ("/");
- MediaSetAccess access(url);
- Pathname local = access.provideFile(path);
-
- DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
- return repositories_in_file(local);
- }
+ ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////
+ inline void assert_alias( const RepoInfo & info )
+ {
+ if ( info.alias().empty() )
+ ZYPP_THROW( RepoNoAliasException( info ) );
+ // bnc #473834. Maybe we can match the alias against a regex to define
+ // and check for valid aliases
+ if ( info.alias()[0] == '.')
+ ZYPP_THROW(RepoInvalidAliasException(
+ info, _("Repository alias cannot start with dot.")));
+ }
- inline void assert_alias( const RepoInfo & info )
- {
- if ( info.alias().empty() )
- ZYPP_THROW( RepoNoAliasException() );
- }
+ inline void assert_alias( const ServiceInfo & info )
+ {
+ if ( info.alias().empty() )
+ ZYPP_THROW( ServiceNoAliasException( info ) );
+ // bnc #473834. Maybe we can match the alias against a regex to define
+ // and check for valid aliases
+ if ( info.alias()[0] == '.')
+ ZYPP_THROW(ServiceInvalidAliasException(
+ info, _("Service alias cannot start with dot.")));
+ }
- inline void assert_alias( const ServiceInfo & info )
- {
- if ( info.alias().empty() )
- ZYPP_THROW( ServiceNoAliasException() );
- }
+ ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////
+ inline void assert_urls( const RepoInfo & info )
+ {
+ if ( info.baseUrlsEmpty() )
+ ZYPP_THROW( RepoNoUrlException( info ) );
+ }
- inline void assert_urls( const RepoInfo & info )
- {
- if ( info.baseUrlsEmpty() )
- ZYPP_THROW( RepoNoUrlException( info ) );
- }
+ inline void assert_url( const ServiceInfo & info )
+ {
+ if ( ! info.url().isValid() )
+ ZYPP_THROW( ServiceNoUrlException( info ) );
+ }
- inline void assert_url( const ServiceInfo & info )
- {
- if ( ! info.url().isValid() )
- ZYPP_THROW( ServiceNoUrlException( info ) );
- }
+ ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////
+ /**
+ * \short Calculates the raw cache path for a repository, this is usually
+ * /var/cache/zypp/alias
+ */
+ inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+ {
+ assert_alias(info);
+ return opt.repoRawCachePath / info.escaped_alias();
+ }
- /**
- * \short Calculates the raw cache path for a repository
- */
- inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
- {
- assert_alias(info);
- return opt.repoRawCachePath / info.escaped_alias();
- }
+ /**
+ * \short Calculates the raw product metadata path for a repository, this is
+ * inside the raw cache dir, plus an optional path where the metadata is.
+ *
+ * It should be different only for repositories that are not in the root of
+ * the media.
+ * for example /var/cache/zypp/alias/addondir
+ */
+ inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+ {
+ assert_alias(info);
+ return opt.repoRawCachePath / info.escaped_alias() / info.path();
+ }
- /**
- * \short Calculates the packages cache path for a repository
- */
- inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
- {
- assert_alias(info);
- return opt.repoPackagesCachePath / info.escaped_alias();
- }
+ /**
+ * \short Calculates the packages cache path for a repository
+ */
+ inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
+ {
+ assert_alias(info);
+ return opt.repoPackagesCachePath / info.escaped_alias();
+ }
- /**
- * \short Calculates the solv cache path for a repository
- */
- inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
- {
- assert_alias(info);
- return opt.repoSolvCachePath / info.escaped_alias();
- }
+ /**
+ * \short Calculates the solv cache path for a repository
+ */
+ inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
+ {
+ assert_alias(info);
+ return opt.repoSolvCachePath / info.escaped_alias();
+ }
- ////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////
- /** Functor collecting ServiceInfos into a ServiceSet. */
- class ServiceCollector
- {
+ /** Functor collecting ServiceInfos into a ServiceSet. */
+ class ServiceCollector
+ {
public:
typedef std::set<ServiceInfo> ServiceSet;
bool operator()( const ServiceInfo & service_r ) const
{
- _services.insert( service_r );
- return true;
+ _services.insert( service_r );
+ return true;
}
private:
ServiceSet & _services;
- };
+ };
+ ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ std::list<RepoInfo> readRepoFile( const Url & repo_file )
+ {
+ // no interface to download a specific file, using workaround:
+ //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
+ Url url(repo_file);
+ Pathname path(url.getPathName());
+ url.setPathName ("/");
+ MediaSetAccess access(url);
+ Pathname local = access.provideFile(path);
+
+ DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
+
+ return repositories_in_file(local);
+ }
///////////////////////////////////////////////////////////////////
//
- // CLASS NAME : RepoManager::Impl
+ // class RepoManagerOptions
//
- ///////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////
- /**
- * \short RepoManager implementation.
- */
+ RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
+ {
+ repoCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
+ repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
+ repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
+ repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
+ knownReposPath = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
+ knownServicesPath = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
+ pluginsPath = Pathname::assertprefix( root_r, ZConfig::instance().pluginsPath() );
+ probe = ZConfig::instance().repo_add_probe();
+
+ rootDir = root_r;
+ }
+
+ RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
+ {
+ RepoManagerOptions ret;
+ ret.repoCachePath = root_r;
+ ret.repoRawCachePath = root_r/"raw";
+ ret.repoSolvCachePath = root_r/"solv";
+ ret.repoPackagesCachePath = root_r/"packages";
+ ret.knownReposPath = root_r/"repos.d";
+ ret.knownServicesPath = root_r/"services.d";
+ ret.pluginsPath = root_r/"plugins";
+ ret.rootDir = root_r;
+ return ret;
+ }
+
+ std:: ostream & operator<<( std::ostream & str, const RepoManagerOptions & obj )
+ {
+#define OUTS(X) str << " " #X "\t" << obj.X << endl
+ str << "RepoManagerOptions (" << obj.rootDir << ") {" << endl;
+ OUTS( repoRawCachePath );
+ OUTS( repoSolvCachePath );
+ OUTS( repoPackagesCachePath );
+ OUTS( knownReposPath );
+ OUTS( knownServicesPath );
+ OUTS( pluginsPath );
+ str << "}" << endl;
+#undef OUTS
+ return str;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /// \class RepoManager::Impl
+ /// \brief RepoManager implementation.
+ ///
+ ///////////////////////////////////////////////////////////////////
struct RepoManager::Impl
{
+ public:
Impl( const RepoManagerOptions &opt )
- : options(opt)
+ : _options(opt)
{
init_knownServices();
init_knownRepositories();
}
- RepoManagerOptions options;
+ ~Impl()
+ {
+ // trigger appdata refresh if some repos change
+ if ( _reposDirty && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
+ {
+ try {
+ std::list<Pathname> entries;
+ filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
+ if ( ! entries.empty() )
+ {
+ ExternalProgram::Arguments cmd;
+ cmd.push_back( "<" ); // discard stdin
+ cmd.push_back( ">" ); // discard stdout
+ cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
+ for ( const auto & rinfo : repos() )
+ {
+ if ( ! rinfo.enabled() )
+ continue;
+ cmd.push_back( "-R" );
+ cmd.push_back( rinfo.alias() );
+ cmd.push_back( "-t" );
+ cmd.push_back( rinfo.type().asString() );
+ cmd.push_back( "-p" );
+ cmd.push_back( rinfo.metadataPath().asString() );
+ }
+
+ for_( it, entries.begin(), entries.end() )
+ {
+ PathInfo pi( *it );
+ //DBG << "/tmp/xx ->" << pi << endl;
+ if ( pi.isFile() && pi.userMayRX() )
+ {
+ // trigger plugin
+ cmd[2] = pi.asString(); // [2] - PROGRAM
+ ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+ }
+ }
+ }
+ }
+ catch (...) {} // no throw in dtor
+ }
+ }
+
+ public:
+ bool repoEmpty() const { return repos().empty(); }
+ RepoSizeType repoSize() const { return repos().size(); }
+ RepoConstIterator repoBegin() const { return repos().begin(); }
+ RepoConstIterator repoEnd() const { return repos().end(); }
+
+ bool hasRepo( const std::string & alias ) const
+ { return foundAliasIn( alias, repos() ); }
+
+ RepoInfo getRepo( const std::string & alias ) const
+ {
+ RepoConstIterator it( findAlias( alias, repos() ) );
+ return it == repos().end() ? RepoInfo::noRepo : *it;
+ }
+
+ public:
+ Pathname metadataPath( const RepoInfo & info ) const
+ { return rawcache_path_for_repoinfo( _options, info ); }
+
+ Pathname packagesPath( const RepoInfo & info ) const
+ { return packagescache_path_for_repoinfo( _options, info ); }
+
+ RepoStatus metadataStatus( const RepoInfo & info ) const;
+
+ RefreshCheckStatus checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy );
+
+ void refreshMetadata( const RepoInfo & info, RawMetadataRefreshPolicy policy, OPT_PROGRESS );
+
+ void cleanMetadata( const RepoInfo & info, OPT_PROGRESS );
+
+ void cleanPackages( const RepoInfo & info, OPT_PROGRESS );
+
+ void buildCache( const RepoInfo & info, CacheBuildPolicy policy, OPT_PROGRESS );
+
+ repo::RepoType probe( const Url & url, const Pathname & path = Pathname() ) const;
+
+ void cleanCacheDirGarbage( OPT_PROGRESS );
+
+ void cleanCache( const RepoInfo & info, OPT_PROGRESS );
+
+ bool isCached( const RepoInfo & info ) const
+ { return PathInfo(solv_path_for_repoinfo( _options, info ) / "solv").isExist(); }
+
+ RepoStatus cacheStatus( const RepoInfo & info ) const
+ { return RepoStatus::fromCookieFile(solv_path_for_repoinfo(_options, info) / "cookie"); }
+
+ void loadFromCache( const RepoInfo & info, OPT_PROGRESS );
+
+ void addRepository( const RepoInfo & info, OPT_PROGRESS );
+
+ void addRepositories( const Url & url, OPT_PROGRESS );
+
+ void removeRepository( const RepoInfo & info, OPT_PROGRESS );
+
+ void modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, OPT_PROGRESS );
+
+ RepoInfo getRepositoryInfo( const std::string & alias, OPT_PROGRESS );
+ RepoInfo getRepositoryInfo( const Url & url, const url::ViewOption & urlview, OPT_PROGRESS );
- RepoSet repos;
+ public:
+ bool serviceEmpty() const { return _services.empty(); }
+ ServiceSizeType serviceSize() const { return _services.size(); }
+ ServiceConstIterator serviceBegin() const { return _services.begin(); }
+ ServiceConstIterator serviceEnd() const { return _services.end(); }
+
+ bool hasService( const std::string & alias ) const
+ { return foundAliasIn( alias, _services ); }
- ServiceSet services;
+ ServiceInfo getService( const std::string & alias ) const
+ {
+ ServiceConstIterator it( findAlias( alias, _services ) );
+ return it == _services.end() ? ServiceInfo::noService : *it;
+ }
public:
+ void addService( const ServiceInfo & service );
+ void addService( const std::string & alias, const Url & url )
+ { addService( ServiceInfo( alias, url ) ); }
+
+ void removeService( const std::string & alias );
+ void removeService( const ServiceInfo & service )
+ { removeService( service.alias() ); }
+
+ void refreshServices( const RefreshServiceOptions & options_r );
+ void refreshService( const std::string & alias, const RefreshServiceOptions & options_r );
+ void refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
+ { refreshService( service.alias(), options_r ); }
+
+ void modifyService( const std::string & oldAlias, const ServiceInfo & newService );
+
+ repo::ServiceType probeService( const Url & url ) const;
+
+ private:
void saveService( ServiceInfo & service ) const;
- Pathname generateNonExistingName( const Pathname &dir,
- const std::string &basefilename ) const;
+ Pathname generateNonExistingName( const Pathname & dir, const std::string & basefilename ) const;
- std::string generateFilename( const RepoInfo & info ) const;
- std::string generateFilename( const ServiceInfo & info ) const;
+ std::string generateFilename( const RepoInfo & info ) const
+ { return filenameFromAlias( info.alias(), "repo" ); }
+ std::string generateFilename( const ServiceInfo & info ) const
+ { return filenameFromAlias( info.alias(), "service" ); }
+
+ void setCacheStatus( const RepoInfo & info, const RepoStatus & status )
+ {
+ Pathname base = solv_path_for_repoinfo( _options, info );
+ filesystem::assert_dir(base);
+ status.saveToCookieFile( base / "cookie" );
+ }
+
+ void touchIndexFile( const RepoInfo & info );
+
+ template<typename OutputIterator>
+ void getRepositoriesInService( const std::string & alias, OutputIterator out ) const
+ {
+ MatchServiceAlias filter( alias );
+ std::copy( boost::make_filter_iterator( filter, repos().begin(), repos().end() ),
+ boost::make_filter_iterator( filter, repos().end(), repos().end() ),
+ out);
+ }
private:
void init_knownServices();
void init_knownRepositories();
+ const RepoSet & repos() const { return _reposX; }
+ RepoSet & reposManip() { if ( ! _reposDirty ) _reposDirty = true; return _reposX; }
+
+ private:
+ RepoManagerOptions _options;
+ RepoSet _reposX;
+ ServiceSet _services;
+
+ DefaultIntegral<bool,false> _reposDirty;
+
private:
friend Impl * rwcowClone<Impl>( const Impl * rhs );
/** clone for RWCOW_pointer */
Impl * clone() const
{ return new Impl( *this ); }
};
-
///////////////////////////////////////////////////////////////////
/** \relates RepoManager::Impl Stream output */
inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
- {
- return str << "RepoManager::Impl";
- }
+ { return str << "RepoManager::Impl"; }
///////////////////////////////////////////////////////////////////
void RepoManager::Impl::saveService( ServiceInfo & service ) const
{
- filesystem::assert_dir( options.knownServicesPath );
- Pathname servfile = generateNonExistingName( options.knownServicesPath,
+ filesystem::assert_dir( _options.knownServicesPath );
+ Pathname servfile = generateNonExistingName( _options.knownServicesPath,
generateFilename( service ) );
service.setFilepath( servfile );
std::ofstream file( servfile.c_str() );
if ( !file )
{
- ZYPP_THROW( Exception( "Can't open " + servfile.asString() ) );
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
}
service.dumpAsIniOn( file );
MIL << "done" << endl;
while ( PathInfo(dir + final_filename).isExist() )
{
final_filename = basefilename + "_" + str::numstring(counter);
- counter++;
+ ++counter;
}
return dir + Pathname(final_filename);
}
////////////////////////////////////////////////////////////////////////////
- /**
- * \short Generate a related filename from a repo info
- *
- * From a repo info, it will try to use the alias as a filename
- * escaping it if necessary. Other fallbacks can be added to
- * this function in case there is no way to use the alias
- */
- std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const
- {
- std::string filename = info.alias();
- // replace slashes with underscores
- str::replaceAll( filename, "/", "_" );
-
- filename = Pathname(filename).extend(".repo").asString();
- MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
- return filename;
- }
-
- std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const
- {
- std::string filename = info.alias();
- // replace slashes with underscores
- str::replaceAll( filename, "/", "_" );
-
- filename = Pathname(filename).extend(".service").asString();
- MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl;
- return filename;
- }
-
-
void RepoManager::Impl::init_knownServices()
{
- Pathname dir = options.knownServicesPath;
+ Pathname dir = _options.knownServicesPath;
std::list<Pathname> entries;
if (PathInfo(dir).isExist())
{
- if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
- ZYPP_THROW(Exception("failed to read directory"));
+ if ( filesystem::readdir( entries, dir, false ) != 0 )
+ {
+ // TranslatorExplanation '%s' is a pathname
+ ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
+ }
//str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
for_(it, entries.begin(), entries.end() )
{
- parser::ServiceFileReader(*it, ServiceCollector(services));
+ parser::ServiceFileReader(*it, ServiceCollector(_services));
}
}
+
+ repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
}
+ ///////////////////////////////////////////////////////////////////
+ namespace {
+ /** Delete \a cachePath_r subdirs not matching known aliases in \a repoEscAliases_r (must be sorted!)
+ * \note bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
+ * we'd need some magic file to identify zypp cache directories. Without this
+ * we may easily remove user data (zypper --pkg-cache-dir . download ...)
+ */
+ inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
+ const Pathname & defaultCachePath_r,
+ const std::list<std::string> & repoEscAliases_r )
+ {
+ if ( cachePath_r != defaultCachePath_r )
+ return;
+
+ std::list<std::string> entries;
+ if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
+ {
+ entries.sort();
+ std::set<std::string> oldfiles;
+ set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
+ std::inserter( oldfiles, oldfiles.end() ) );
+ for ( const std::string & old : oldfiles )
+ {
+ if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
+ continue;
+ filesystem::recursive_rmdir( cachePath_r / old );
+ }
+ }
+ }
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
void RepoManager::Impl::init_knownRepositories()
{
MIL << "start construct known repos" << endl;
- if ( PathInfo(options.knownReposPath).isExist() )
+ if ( PathInfo(_options.knownReposPath).isExist() )
{
- RepoInfoList repol = repositories_in_dir(options.knownReposPath);
- for ( RepoInfoList::iterator it = repol.begin();
- it != repol.end();
- ++it )
+ std::list<std::string> repoEscAliases;
+ std::list<RepoInfo> orphanedRepos;
+ for ( RepoInfo & repoInfo : repositories_in_dir(_options.knownReposPath) )
{
// set the metadata path for the repo
- Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
- (*it).setMetadataPath(metadata_path);
-
+ repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
// set the downloaded packages path for the repo
- Pathname packages_path = packagescache_path_for_repoinfo(options, (*it));
- (*it).setPackagesPath(packages_path);
+ repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
+ // remember it
+ _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
+
+ // detect orphaned repos belonging to a deleted service
+ const std::string & serviceAlias( repoInfo.service() );
+ if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
+ {
+ WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
+ orphanedRepos.push_back( repoInfo );
+ continue; // don't remember it in repoEscAliases
+ }
+
+ repoEscAliases.push_back(repoInfo.escaped_alias());
+ }
- repos.insert(*it);
+ // Cleanup orphanded service repos:
+ if ( ! orphanedRepos.empty() )
+ {
+ for ( auto & repoInfo : orphanedRepos )
+ {
+ MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
+ // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
+ // %1% = service name
+ // %2% = repository name
+ JobReport::warning( formatNAC(_("Unknown service '%1%': Removing orphaned service repository '%2%'" ))
+ % repoInfo.service()
+ % repoInfo.alias() );
+ try {
+ removeRepository( repoInfo );
+ }
+ catch ( const Exception & caugth )
+ {
+ JobReport::error( caugth.asUserHistory() );
+ }
+ }
}
- }
+ // delete metadata folders without corresponding repo (e.g. old tmp directories)
+ //
+ // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
+ // we'd need somemagic file to identify zypp cache directories. Without this
+ // we may easily remove user data (zypper --pkg-cache-dir . download ...)
+ repoEscAliases.sort();
+ RepoManagerOptions defaultCache( _options.rootDir );
+ cleanupNonRepoMetadtaFolders( _options.repoRawCachePath, defaultCache.repoRawCachePath, repoEscAliases );
+ cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath, defaultCache.repoSolvCachePath, repoEscAliases );
+ cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath, defaultCache.repoPackagesCachePath, repoEscAliases );
+ }
MIL << "end construct known repos" << endl;
}
///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : RepoManager
- //
- ///////////////////////////////////////////////////////////////////
-
- RepoManager::RepoManager( const RepoManagerOptions &opt )
- : _pimpl( new Impl(opt) )
- {}
-
- ////////////////////////////////////////////////////////////////////////////
-
- RepoManager::~RepoManager()
- {}
-
- ////////////////////////////////////////////////////////////////////////////
-
- bool RepoManager::repoEmpty() const
- { return _pimpl->repos.empty(); }
-
- RepoManager::RepoSizeType RepoManager::repoSize() const
- { return _pimpl->repos.size(); }
- RepoManager::RepoConstIterator RepoManager::repoBegin() const
- { return _pimpl->repos.begin(); }
-
- RepoManager::RepoConstIterator RepoManager::repoEnd() const
- { return _pimpl->repos.end(); }
-
- RepoInfo RepoManager::getRepo( const std::string & alias ) const
- {
- for_( it, repoBegin(), repoEnd() )
- if ( it->alias() == alias )
- return *it;
- return RepoInfo::noRepo;
- }
-
- bool RepoManager::hasRepo( const std::string & alias ) const
+ RepoStatus RepoManager::Impl::metadataStatus( const RepoInfo & info ) const
{
- for_( it, repoBegin(), repoEnd() )
- if ( it->alias() == alias )
- return true;
- return false;
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- Pathname RepoManager::metadataPath( const RepoInfo &info ) const
- {
- return rawcache_path_for_repoinfo(_pimpl->options, info );
- }
-
- Pathname RepoManager::packagesPath( const RepoInfo &info ) const
- {
- return packagescache_path_for_repoinfo(_pimpl->options, info );
- }
+ Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
+ Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
- ////////////////////////////////////////////////////////////////////////////
-
- RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
- {
- Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
RepoType repokind = info.type();
- RepoStatus status;
-
- switch ( repokind.toEnum() )
- {
- case RepoType::NONE_e:
- // unknown, probe the local metadata
- repokind = probe(rawpath.asUrl());
- break;
- default:
- break;
- }
+ // If unknown, probe the local metadata
+ if ( repokind == RepoType::NONE )
+ repokind = probe( productdatapath.asUrl() );
+ RepoStatus status;
switch ( repokind.toEnum() )
{
case RepoType::RPMMD_e :
- {
- status = RepoStatus( rawpath + "/repodata/repomd.xml");
- }
- break;
+ status = RepoStatus( productdatapath/"repodata/repomd.xml");
+ break;
case RepoType::YAST2_e :
- {
- status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
- }
- break;
+ status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
+ break;
case RepoType::RPMPLAINDIR_e :
- {
- if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
- status = RepoStatus( rawpath + "/cookie");
- }
- break;
+ status = RepoStatus::fromCookieFile( productdatapath/"cookie" );
+ break;
case RepoType::NONE_e :
// Return default RepoStatus in case of RepoType::NONE
return status;
}
- void RepoManager::touchIndexFile(const RepoInfo & info)
+
+ void RepoManager::Impl::touchIndexFile( const RepoInfo & info )
{
- Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
+ Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
RepoType repokind = info.type();
if ( repokind.toEnum() == RepoType::NONE_e )
// unknown, probe the local metadata
- repokind = probe(rawpath.asUrl());
+ repokind = probe( productdatapath.asUrl() );
// if still unknown, just return
if (repokind == RepoType::NONE_e)
return;
switch ( repokind.toEnum() )
{
case RepoType::RPMMD_e :
- p = Pathname(rawpath + "/repodata/repomd.xml");
+ p = Pathname(productdatapath + "/repodata/repomd.xml");
break;
case RepoType::YAST2_e :
- p = Pathname(rawpath + "/content");
+ p = Pathname(productdatapath + "/content");
break;
case RepoType::RPMPLAINDIR_e :
- p = Pathname(rawpath + "/cookie");
+ p = Pathname(productdatapath + "/cookie");
break;
case RepoType::NONE_e :
filesystem::touch(p);
}
- RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata(
- const RepoInfo &info,
- const Url &url,
- RawMetadataRefreshPolicy policy )
+
+ RepoManager::RefreshCheckStatus RepoManager::Impl::checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy )
{
assert_alias(info);
-
- RepoStatus oldstatus;
- RepoStatus newstatus;
-
try
{
MIL << "Going to try to check whether refresh is needed for " << url << endl;
- repo::RepoType repokind = info.type();
+ // first check old (cached) metadata
+ Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
+ filesystem::assert_dir( mediarootpath );
+ RepoStatus oldstatus = metadataStatus( info );
- // if the type is unknown, try probing.
- switch ( repokind.toEnum() )
+ if ( oldstatus.empty() )
{
- case RepoType::NONE_e:
- // unknown, probe it
- repokind = probe(url);
- break;
- default:
- break;
+ MIL << "No cached metadata, going to refresh" << endl;
+ return REFRESH_NEEDED;
}
- Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
- filesystem::assert_dir(rawpath);
- oldstatus = metadataStatus(info);
+ {
+ if ( url.schemeIsVolatile() )
+ {
+ MIL << "never refresh CD/DVD" << endl;
+ return REPO_UP_TO_DATE;
+ }
+ if ( url.schemeIsLocal() )
+ {
+ policy = RefreshIfNeededIgnoreDelay;
+ }
+ }
// now we've got the old (cached) status, we can decide repo.refresh.delay
if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
DBG << "current time: " << (Date::ValueType)Date::now() << endl;
DBG << "last refresh = " << diff << " minutes ago" << endl;
- if (diff < ZConfig::instance().repo_refresh_delay())
+ if ( diff < ZConfig::instance().repo_refresh_delay() )
{
- MIL << "Repository '" << info.alias()
- << "' has been refreshed less than repo.refresh.delay ("
- << ZConfig::instance().repo_refresh_delay()
- << ") minutes ago. Advising to skip refresh" << endl;
- return REPO_CHECK_DELAYED;
+ if ( diff < 0 )
+ {
+ WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl;
+ }
+ else
+ {
+ MIL << "Repository '" << info.alias()
+ << "' has been refreshed less than repo.refresh.delay ("
+ << ZConfig::instance().repo_refresh_delay()
+ << ") minutes ago. Advising to skip refresh" << endl;
+ return REPO_CHECK_DELAYED;
+ }
}
}
- // create temp dir as sibling of rawpath
- filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
+ repo::RepoType repokind = info.type();
+ // if unknown: probe it
+ if ( repokind == RepoType::NONE )
+ repokind = probe( url, info.path() );
- if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
- ( repokind.toEnum() == RepoType::YAST2_e ) )
+ // retrieve newstatus
+ RepoStatus newstatus;
+ switch ( repokind.toEnum() )
{
- MediaSetAccess media(url);
- shared_ptr<repo::Downloader> downloader_ptr;
-
- if ( repokind.toEnum() == RepoType::RPMMD_e )
- downloader_ptr.reset(new yum::Downloader(info));
- else
- downloader_ptr.reset( new susetags::Downloader(info));
+ case RepoType::RPMMD_e:
+ {
+ MediaSetAccess media( url );
+ newstatus = yum::Downloader( info, mediarootpath ).status( media );
+ }
+ break;
- RepoStatus newstatus = downloader_ptr->status(media);
- bool refresh = false;
- if ( oldstatus.checksum() == newstatus.checksum() )
- {
- MIL << "repo has not changed" << endl;
- if ( policy == RefreshForced )
- {
- MIL << "refresh set to forced" << endl;
- refresh = true;
- }
- }
- else
- {
- MIL << "repo has changed, going to refresh" << endl;
- refresh = true;
- }
+ case RepoType::YAST2_e:
+ {
+ MediaSetAccess media( url );
+ newstatus = susetags::Downloader( info, mediarootpath ).status( media );
+ }
+ break;
- if (!refresh)
- touchIndexFile(info);
+ case RepoType::RPMPLAINDIR_e:
+ newstatus = RepoStatus( MediaMounter(url).getPathName(info.path()) ); // dir status
+ break;
- return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+ default:
+ case RepoType::NONE_e:
+ ZYPP_THROW( RepoUnknownTypeException( info ) );
+ break;
}
- else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
- {
- RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
- bool refresh = false;
- if ( oldstatus.checksum() == newstatus.checksum() )
- {
- MIL << "repo has not changed" << endl;
- if ( policy == RefreshForced )
- {
- MIL << "refresh set to forced" << endl;
- refresh = true;
- }
- }
- else
- {
- MIL << "repo has changed, going to refresh" << endl;
- refresh = true;
- }
- if (!refresh)
- touchIndexFile(info);
-
- return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+ // check status
+ bool refresh = false;
+ if ( oldstatus == newstatus )
+ {
+ MIL << "repo has not changed" << endl;
+ if ( policy == RefreshForced )
+ {
+ MIL << "refresh set to forced" << endl;
+ refresh = true;
+ }
}
else
{
- ZYPP_THROW(RepoUnknownTypeException(info));
+ MIL << "repo has changed, going to refresh" << endl;
+ refresh = true;
}
+
+ if (!refresh)
+ touchIndexFile(info);
+
+ return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
+
}
catch ( const Exception &e )
{
return REFRESH_NEEDED; // default
}
- void RepoManager::refreshMetadata( const RepoInfo &info,
- RawMetadataRefreshPolicy policy,
- const ProgressData::ReceiverFnc & progress )
+
+ void RepoManager::Impl::refreshMetadata( const RepoInfo & info, RawMetadataRefreshPolicy policy, const ProgressData::ReceiverFnc & progress )
{
assert_alias(info);
assert_urls(info);
// we will throw this later if no URL checks out fine
- RepoException rexception(_("Valid metadata not found at specified URL(s)"));
+ RepoException rexception( info, _PL("Valid metadata not found at specified URL",
+ "Valid metadata not found at specified URLs",
+ info.baseUrlsSize() ) );
// try urls one by one
for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
repo::RepoType repokind = info.type();
// if the type is unknown, try probing.
- switch ( repokind.toEnum() )
+ if ( repokind == RepoType::NONE )
+ {
+ // unknown, probe it
+ repokind = probe( *it, info.path() );
+
+ if (repokind.toEnum() != RepoType::NONE_e)
+ {
+ // Adjust the probed type in RepoInfo
+ info.setProbedType( repokind ); // lazy init!
+ //save probed type only for repos in system
+ for_( it, repoBegin(), repoEnd() )
+ {
+ if ( info.alias() == (*it).alias() )
+ {
+ RepoInfo modifiedrepo = info;
+ modifiedrepo.setType( repokind );
+ modifyRepository( info.alias(), modifiedrepo );
+ break;
+ }
+ }
+ }
+ }
+
+ Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
+ if( filesystem::assert_dir(mediarootpath) )
{
- case RepoType::NONE_e:
- // unknown, probe it
- repokind = probe(*it);
-
- if (repokind.toEnum() != RepoType::NONE_e)
- {
- // Adjust the probed type in RepoInfo
- info.setProbedType( repokind ); // lazy init!
- //save probed type only for repos in system
- for_( it, repoBegin(), repoEnd() )
- {
- if ( info.alias() == (*it).alias() )
- {
- RepoInfo modifiedrepo = info;
- modifiedrepo.setType(repokind);
- modifyRepository(info.alias(),modifiedrepo);
- }
- }
- }
- break;
- default:
- break;
+ Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) );
+ ZYPP_THROW(ex);
}
- Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
- filesystem::assert_dir(rawpath);
-
- // create temp dir as sibling of rawpath
- filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
+ // create temp dir as sibling of mediarootpath
+ filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
+ if( tmpdir.path().empty() )
+ {
+ Exception ex(_("Can't create metadata cache directory."));
+ ZYPP_THROW(ex);
+ }
if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
( repokind.toEnum() == RepoType::YAST2_e ) )
MediaSetAccess media(url);
shared_ptr<repo::Downloader> downloader_ptr;
- MIL << "Creating downloader for [ " << info.name() << " ]" << endl;
+ MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
if ( repokind.toEnum() == RepoType::RPMMD_e )
- downloader_ptr.reset(new yum::Downloader(info));
+ downloader_ptr.reset(new yum::Downloader(info, mediarootpath));
else
- downloader_ptr.reset( new susetags::Downloader(info) );
+ downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) );
/**
* Given a downloader, sets the other repos raw metadata
*/
for_( it, repoBegin(), repoEnd() )
{
- Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
+ Pathname cachepath(rawcache_path_for_repoinfo( _options, *it ));
if ( PathInfo(cachepath).isExist() )
downloader_ptr->addCachePath(cachepath);
}
- downloader_ptr->download( media, tmpdir.path());
+ downloader_ptr->download( media, tmpdir.path() );
}
else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
{
- RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
+ MediaMounter media( url );
+ RepoStatus newstatus = RepoStatus( media.getPathName( info.path() ) ); // dir status
- std::ofstream file(( tmpdir.path() + "/cookie").c_str());
- if (!file) {
- ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
- }
- file << url << endl;
- file << newstatus.checksum() << endl;
-
- file.close();
+ Pathname productpath( tmpdir.path() / info.path() );
+ filesystem::assert_dir( productpath );
+ newstatus.saveToCookieFile( productpath/"cookie" );
}
else
{
- ZYPP_THROW(RepoUnknownTypeException());
+ ZYPP_THROW(RepoUnknownTypeException( info ));
}
// ok we have the metadata, now exchange
// the contents
-
- filesystem::TmpDir oldmetadata( filesystem::TmpDir::makeSibling( rawpath ) );
- filesystem::rename( rawpath, oldmetadata.path() );
- // move the just downloaded there
- filesystem::rename( tmpdir.path(), rawpath );
+ filesystem::exchange( tmpdir.path(), mediarootpath );
+ reposManip(); // remember to trigger appdata refresh
// we are done.
return;
////////////////////////////////////////////////////////////////////////////
- void RepoManager::cleanMetadata( const RepoInfo &info,
- const ProgressData::ReceiverFnc & progressfnc )
+ void RepoManager::Impl::cleanMetadata( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc )
{
ProgressData progress(100);
progress.sendTo(progressfnc);
- filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
+ filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_options, info));
progress.toMax();
}
- void RepoManager::cleanPackages( const RepoInfo &info,
- const ProgressData::ReceiverFnc & progressfnc )
+
+ void RepoManager::Impl::cleanPackages( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc )
{
ProgressData progress(100);
progress.sendTo(progressfnc);
- filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info));
+ filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_options, info));
progress.toMax();
}
- void RepoManager::buildCache( const RepoInfo &info,
- CacheBuildPolicy policy,
- const ProgressData::ReceiverFnc & progressrcv )
+
+ void RepoManager::Impl::buildCache( const RepoInfo & info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
{
assert_alias(info);
- Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
+ Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
+ Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
- filesystem::assert_dir(_pimpl->options.repoCachePath);
+ if( filesystem::assert_dir(_options.repoCachePath) )
+ {
+ Exception ex(str::form( _("Can't create %s"), _options.repoCachePath.c_str()) );
+ ZYPP_THROW(ex);
+ }
RepoStatus raw_metadata_status = metadataStatus(info);
if ( raw_metadata_status.empty() )
{
MIL << info.alias() << " is already cached." << endl;
RepoStatus cache_status = cacheStatus(info);
- if ( cache_status.checksum() == raw_metadata_status.checksum() )
+ if ( cache_status == raw_metadata_status )
{
MIL << info.alias() << " cache is up to date with metadata." << endl;
if ( policy == BuildIfNeeded ) {
ProgressData progress(100);
callback::SendReport<ProgressReport> report;
progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
- progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
+ progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str()));
progress.toMin();
if (needs_cleaning)
cleanCache(info);
}
- MIL << info.alias() << " building cache..." << endl;
+ MIL << info.alias() << " building cache..." << info.type() << endl;
+
+ Pathname base = solv_path_for_repoinfo( _options, info);
- Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
- filesystem::assert_dir(base);
+ if( filesystem::assert_dir(base) )
+ {
+ Exception ex(str::form( _("Can't create %s"), base.c_str()) );
+ ZYPP_THROW(ex);
+ }
+
+ if( ! PathInfo(base).userMayW() )
+ {
+ Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) );
+ ZYPP_THROW(ex);
+ }
Pathname solvfile = base / "solv";
// do we have type?
{
case RepoType::NONE_e:
// unknown, probe the local metadata
- repokind = probe(rawpath.asUrl());
+ repokind = probe( productdatapath.asUrl() );
break;
default:
break;
{
// Take care we unlink the solvfile on exception
ManagedFile guard( solvfile, filesystem::unlink );
+ scoped_ptr<MediaMounter> forPlainDirs;
+
+ ExternalProgram::Arguments cmd;
+ cmd.push_back( "repo2solv.sh" );
+ // repo2solv expects -o as 1st arg!
+ cmd.push_back( "-o" );
+ cmd.push_back( solvfile.asString() );
+ cmd.push_back( "-X" ); // autogenerate pattern from pattern-package
- std::ostringstream cmd;
- std::string toFile( str::gsub(solvfile.asString(),"\"","\\\"") );
- if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
+ if ( repokind == RepoType::RPMPLAINDIR )
{
+ forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) );
+ // recusive for plaindir as 2nd arg!
+ cmd.push_back( "-R" );
// FIXME this does only work form dir: URLs
- cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
- str::gsub( info.baseUrlsBegin()->getPathName(),"\"","\\\"" ).c_str(),
- toFile.c_str() );
+ cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() );
}
else
- {
- cmd << str::form( "repo2solv.sh \"%s\" > \"%s\"",
- str::gsub( rawpath.asString(),"\"","\\\"" ).c_str(),
- toFile.c_str() );
- }
- MIL << "Executing: " << cmd.str() << endl;
- ExternalProgram prog( cmd.str(), ExternalProgram::Stderr_To_Stdout );
+ cmd.push_back( productdatapath.asString() );
+
+ ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
+ std::string errdetail;
- cmd << endl;
for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
WAR << " " << output;
- cmd << " " << output;
+ if ( errdetail.empty() ) {
+ errdetail = prog.command();
+ errdetail += '\n';
+ }
+ errdetail += output;
}
int ret = prog.close();
if ( ret != 0 )
{
- RepoException ex(str::form("Failed to cache repo (%d).", ret));
- ex.remember( cmd.str() );
+ RepoException ex(str::form( _("Failed to cache repo (%d)."), ret ));
+ ex.remember( errdetail );
ZYPP_THROW(ex);
}
}
break;
default:
- ZYPP_THROW(RepoUnknownTypeException("Unhandled repository type"));
+ ZYPP_THROW(RepoUnknownTypeException( info, _("Unhandled repository type") ));
break;
}
// update timestamp and checksum
////////////////////////////////////////////////////////////////////////////
- repo::RepoType RepoManager::probe( const Url &url ) const
+ repo::RepoType RepoManager::Impl::probe( const Url & url, const Pathname & path ) const
{
- if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
+ MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
+
+ if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() )
{
// Handle non existing local directory in advance, as
// MediaSetAccess does not support it.
+ MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
return repo::RepoType::NONE;
}
+ // prepare exception to be thrown if the type could not be determined
+ // due to a media exception. We can't throw right away, because of some
+ // problems with proxy servers returning an incorrect error
+ // on ftp file-not-found(bnc #335906). Instead we'll check another types
+ // before throwing.
+
+ // TranslatorExplanation '%s' is an URL
+ RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
+ bool gotMediaException = false;
try
{
MediaSetAccess access(url);
- if ( access.doesFileExist("/repodata/repomd.xml") )
- return repo::RepoType::RPMMD;
- if ( access.doesFileExist("/content") )
- return repo::RepoType::YAST2;
+ try
+ {
+ if ( access.doesFileExist(path/"/repodata/repomd.xml") )
+ {
+ MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl;
+ return repo::RepoType::RPMMD;
+ }
+ }
+ catch ( const media::MediaException &e )
+ {
+ ZYPP_CAUGHT(e);
+ DBG << "problem checking for repodata/repomd.xml file" << endl;
+ enew.remember(e);
+ gotMediaException = true;
+ }
+
+ try
+ {
+ if ( access.doesFileExist(path/"/content") )
+ {
+ MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl;
+ return repo::RepoType::YAST2;
+ }
+ }
+ catch ( const media::MediaException &e )
+ {
+ ZYPP_CAUGHT(e);
+ DBG << "problem checking for content file" << endl;
+ enew.remember(e);
+ gotMediaException = true;
+ }
- // if it is a local url of type dir
- if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
+ // if it is a non-downloading URL denoting a directory
+ if ( ! url.schemeIsDownloading() )
{
- Pathname path = Pathname(url.getPathName());
- if ( PathInfo(path).isDir() )
+ MediaMounter media( url );
+ if ( PathInfo(media.getPathName()/path).isDir() )
{
// allow empty dirs for now
+ MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl;
return repo::RepoType::RPMPLAINDIR;
}
}
}
- catch ( const media::MediaException &e )
- {
- ZYPP_CAUGHT(e);
- RepoException enew("Error trying to read from " + url.asString());
- enew.remember(e);
- ZYPP_THROW(enew);
- }
catch ( const Exception &e )
{
ZYPP_CAUGHT(e);
- Exception enew("Unknown error reading from " + url.asString());
+ // TranslatorExplanation '%s' is an URL
+ Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
enew.remember(e);
ZYPP_THROW(enew);
}
+ if (gotMediaException)
+ ZYPP_THROW(enew);
+
+ MIL << "Probed type NONE at " << url << " (" << path << ")" << endl;
return repo::RepoType::NONE;
}
////////////////////////////////////////////////////////////////////////////
- void RepoManager::cleanCache( const RepoInfo &info,
- const ProgressData::ReceiverFnc & progressrcv )
+ void RepoManager::Impl::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
{
- ProgressData progress(100);
+ MIL << "Going to clean up garbage in cache dirs" << endl;
+
+ ProgressData progress(300);
progress.sendTo(progressrcv);
progress.toMin();
- filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
+ std::list<Pathname> cachedirs;
+ cachedirs.push_back(_options.repoRawCachePath);
+ cachedirs.push_back(_options.repoPackagesCachePath);
+ cachedirs.push_back(_options.repoSolvCachePath);
+
+ for_( dir, cachedirs.begin(), cachedirs.end() )
+ {
+ if ( PathInfo(*dir).isExist() )
+ {
+ std::list<Pathname> entries;
+ if ( filesystem::readdir( entries, *dir, false ) != 0 )
+ // TranslatorExplanation '%s' is a pathname
+ ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
+
+ unsigned sdircount = entries.size();
+ unsigned sdircurrent = 1;
+ for_( subdir, entries.begin(), entries.end() )
+ {
+ // if it does not belong known repo, make it disappear
+ bool found = false;
+ for_( r, repoBegin(), repoEnd() )
+ if ( subdir->basename() == r->escaped_alias() )
+ { found = true; break; }
+
+ if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
+ filesystem::recursive_rmdir( *subdir );
+ progress.set( progress.val() + sdircurrent * 100 / sdircount );
+ ++sdircurrent;
+ }
+ }
+ else
+ progress.set( progress.val() + 100 );
+ }
progress.toMax();
}
////////////////////////////////////////////////////////////////////////////
- bool RepoManager::isCached( const RepoInfo &info ) const
- {
- return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
- }
-
- RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
+ void RepoManager::Impl::cleanCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
{
+ ProgressData progress(100);
+ progress.sendTo(progressrcv);
+ progress.toMin();
- Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
+ MIL << "Removing raw metadata cache for " << info.alias() << endl;
+ filesystem::recursive_rmdir(solv_path_for_repoinfo(_options, info));
- return RepoStatus::fromCookieFile(cookiefile);
+ progress.toMax();
}
- void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
- {
- Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
- filesystem::assert_dir(base);
- Pathname cookiefile = base / "cookie";
-
- status.saveToCookieFile(cookiefile);
- }
+ ////////////////////////////////////////////////////////////////////////////
- void RepoManager::loadFromCache( const RepoInfo & info,
- const ProgressData::ReceiverFnc & progressrcv )
+ void RepoManager::Impl::loadFromCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
{
assert_alias(info);
- Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
+ Pathname solvfile = solv_path_for_repoinfo(_options, info) / "solv";
if ( ! PathInfo(solvfile).isExist() )
ZYPP_THROW(RepoNotCachedException(info));
+ sat::Pool::instance().reposErase( info.alias() );
try
{
- sat::Pool::instance().addRepoSolv( solvfile, info );
+ Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
+ // test toolversion in order to rebuild solv file in case
+ // it was written by an old libsolv-tool parser.
+ //
+ // Known version strings used:
+ // - <no string>
+ // - "1.0"
+ //
+ sat::LookupRepoAttr toolversion( sat::SolvAttr::repositoryToolVersion, repo );
+ if ( toolversion.begin().asString().empty() )
+ {
+ repo.eraseFromPool();
+ ZYPP_THROW(Exception("Solv-file was created by old parser."));
+ }
+ // else: up-to-date (or even newer).
}
catch ( const Exception & exp )
{
////////////////////////////////////////////////////////////////////////////
- void RepoManager::addRepository( const RepoInfo &info,
- const ProgressData::ReceiverFnc & progressrcv )
+ void RepoManager::Impl::addRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
{
assert_alias(info);
ProgressData progress(100);
callback::SendReport<ProgressReport> report;
progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
- progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
+ progress.name(str::form(_("Adding repository '%s'"), info.label().c_str()));
progress.toMin();
- RepoInfo tosave = info;
- if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
- ZYPP_THROW(RepoAlreadyExistsException(info));
+ MIL << "Try adding repo " << info << endl;
+ RepoInfo tosave = info;
+ if ( repos().find(tosave) != repos().end() )
+ ZYPP_THROW(RepoAlreadyExistsException(info));
// check the first url for now
- if ( _pimpl->options.probe )
+ if ( _options.probe )
{
DBG << "unknown repository type, probing" << endl;
RepoType probedtype;
- probedtype = probe(*tosave.baseUrlsBegin());
+ probedtype = probe( *tosave.baseUrlsBegin(), info.path() );
if ( tosave.baseUrlsSize() > 0 )
{
if ( probedtype == RepoType::NONE )
- ZYPP_THROW(RepoUnknownTypeException());
+ ZYPP_THROW(RepoUnknownTypeException(info));
else
tosave.setType(probedtype);
}
progress.set(50);
// assert the directory exists
- filesystem::assert_dir(_pimpl->options.knownReposPath);
+ filesystem::assert_dir(_options.knownReposPath);
- Pathname repofile = _pimpl->generateNonExistingName(
- _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
+ Pathname repofile = generateNonExistingName(
+ _options.knownReposPath, generateFilename(tosave));
// now we have a filename that does not exists
MIL << "Saving repo in " << repofile << endl;
std::ofstream file(repofile.c_str());
- if (!file) {
- ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
+ if (!file)
+ {
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
}
tosave.dumpAsIniOn(file);
tosave.setFilepath(repofile);
- _pimpl->repos.insert(tosave);
+ tosave.setMetadataPath( metadataPath( tosave ) );
+ tosave.setPackagesPath( packagesPath( tosave ) );
+ {
+ // We chould fix the API as we must injet those paths
+ // into the repoinfo in order to keep it usable.
+ RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
+ oinfo.setMetadataPath( metadataPath( tosave ) );
+ oinfo.setPackagesPath( packagesPath( tosave ) );
+ }
+ reposManip().insert(tosave);
progress.set(90);
if ( havePasswords )
{
media::CredentialManager cm(
- media::CredManagerOptions(_pimpl->options.rootDir) );
+ media::CredManagerOptions(_options.rootDir) );
for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd())
if (urlit->hasCredentialsInAuthority())
MIL << "done" << endl;
}
- void RepoManager::addRepositories( const Url &url,
- const ProgressData::ReceiverFnc & progressrcv )
+
+ void RepoManager::Impl::addRepositories( const Url & url, const ProgressData::ReceiverFnc & progressrcv )
{
std::list<RepoInfo> repos = readRepoFile(url);
for ( std::list<RepoInfo>::const_iterator it = repos.begin();
std::string filename = Pathname(url.getPathName()).basename();
if ( filename == Pathname() )
- ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
+ {
+ // TranslatorExplanation '%s' is an URL
+ ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
+ }
// assert the directory exists
- filesystem::assert_dir(_pimpl->options.knownReposPath);
+ filesystem::assert_dir(_options.knownReposPath);
- Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
+ Pathname repofile = generateNonExistingName(_options.knownReposPath, filename);
// now we have a filename that does not exists
MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
std::ofstream file(repofile.c_str());
- if (!file) {
- ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
+ if (!file)
+ {
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
}
for ( std::list<RepoInfo>::iterator it = repos.begin();
MIL << "Saving " << (*it).alias() << endl;
it->setFilepath(repofile.asString());
it->dumpAsIniOn(file);
- _pimpl->repos.insert(*it);
+ reposManip().insert(*it);
- HistoryLog(_pimpl->options.rootDir).addRepository(*it);
+ HistoryLog(_options.rootDir).addRepository(*it);
}
MIL << "done" << endl;
////////////////////////////////////////////////////////////////////////////
- void RepoManager::removeRepository( const RepoInfo & info,
- const ProgressData::ReceiverFnc & progressrcv)
+ void RepoManager::Impl::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
{
ProgressData progress;
callback::SendReport<ProgressReport> report;
progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
- progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
+ progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
MIL << "Going to delete repo " << info.alias() << endl;
RepoInfo todelete = *it;
if (todelete.filepath().empty())
{
- ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
+ ZYPP_THROW(RepoException( todelete, _("Can't figure out where the repo is stored.") ));
}
else
{
// easy, only this one, just delete the file
if ( filesystem::unlink(todelete.filepath()) != 0 )
{
- ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW(RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
}
- MIL << todelete.alias() << " sucessfully deleted." << endl;
+ MIL << todelete.alias() << " successfully deleted." << endl;
}
else
{
filesystem::assert_dir(todelete.filepath().dirname());
std::ofstream file(todelete.filepath().c_str());
- if (!file) {
- //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
- ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
+ if (!file)
+ {
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
}
for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
fit != filerepos.end();
}
}
- CombinedProgressData subprogrcv(progress, 70);
- CombinedProgressData cleansubprogrcv(progress, 30);
+ CombinedProgressData cSubprogrcv(progress, 20);
+ CombinedProgressData mSubprogrcv(progress, 40);
+ CombinedProgressData pSubprogrcv(progress, 40);
// now delete it from cache
if ( isCached(todelete) )
- cleanCache( todelete, subprogrcv);
+ cleanCache( todelete, cSubprogrcv);
// now delete metadata (#301037)
- cleanMetadata( todelete, cleansubprogrcv);
- _pimpl->repos.erase(todelete);
- MIL << todelete.alias() << " sucessfully deleted." << endl;
- HistoryLog(_pimpl->options.rootDir).removeRepository(todelete);
+ cleanMetadata( todelete, mSubprogrcv );
+ cleanPackages( todelete, pSubprogrcv );
+ reposManip().erase(todelete);
+ MIL << todelete.alias() << " successfully deleted." << endl;
+ HistoryLog(_options.rootDir).removeRepository(todelete);
return;
} // else filepath is empty
////////////////////////////////////////////////////////////////////////////
- void RepoManager::modifyRepository( const std::string &alias,
- const RepoInfo & newinfo,
- const ProgressData::ReceiverFnc & progressrcv )
+ void RepoManager::Impl::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, const ProgressData::ReceiverFnc & progressrcv )
{
RepoInfo toedit = getRepositoryInfo(alias);
+ RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
// check if the new alias already exists when renaming the repo
- if (alias != newinfo.alias())
+ if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
{
- for_( it, repoBegin(), repoEnd() )
- {
- if ( newinfo.alias() == (*it).alias() )
- ZYPP_THROW(RepoAlreadyExistsException(newinfo));
- }
+ ZYPP_THROW(RepoAlreadyExistsException(newinfo));
}
if (toedit.filepath().empty())
{
- ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
+ ZYPP_THROW(RepoException( toedit, _("Can't figure out where the repo is stored.") ));
}
else
{
filesystem::assert_dir(toedit.filepath().dirname());
std::ofstream file(toedit.filepath().c_str());
- if (!file) {
- //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
- ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
+ if (!file)
+ {
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
}
for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
fit != filerepos.end();
newinfo.dumpAsIniOn(file);
}
- _pimpl->repos.erase(toedit);
- _pimpl->repos.insert(newinfo);
- HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo);
+ newinfo.setFilepath(toedit.filepath());
+ reposManip().erase(toedit);
+ reposManip().insert(newinfo);
+ HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
+ MIL << "repo " << alias << " modified" << endl;
}
}
////////////////////////////////////////////////////////////////////////////
- RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
- const ProgressData::ReceiverFnc & progressrcv )
+ RepoInfo RepoManager::Impl::getRepositoryInfo( const std::string & alias, const ProgressData::ReceiverFnc & progressrcv )
{
- RepoInfo info;
- info.setAlias(alias);
- RepoConstIterator it = _pimpl->repos.find( info );
- if( it == repoEnd() )
- ZYPP_THROW(RepoNotFoundException(info));
- else
+ RepoConstIterator it( findAlias( alias, repos() ) );
+ if ( it != repos().end() )
return *it;
+ RepoInfo info;
+ info.setAlias( alias );
+ ZYPP_THROW( RepoNotFoundException(info) );
}
- ////////////////////////////////////////////////////////////////////////////
- RepoInfo RepoManager::getRepositoryInfo( const Url & url,
- const url::ViewOption & urlview,
- const ProgressData::ReceiverFnc & progressrcv )
+ RepoInfo RepoManager::Impl::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
{
for_( it, repoBegin(), repoEnd() )
{
- for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
- urlit != (*it).baseUrlsEnd();
- ++urlit)
+ for_( urlit, (*it).baseUrlsBegin(), (*it).baseUrlsEnd() )
{
- if ((*urlit).asString(urlview) == url.asString(urlview))
- return *it;
+ if ( (*urlit).asString(urlview) == url.asString(urlview) )
+ return *it;
}
}
RepoInfo info;
- info.setBaseUrl(url);
- ZYPP_THROW(RepoNotFoundException(info));
+ info.setBaseUrl( url );
+ ZYPP_THROW( RepoNotFoundException(info) );
}
////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////
- bool RepoManager::serviceEmpty() const
- { return _pimpl->services.empty(); }
-
- RepoManager::ServiceSizeType RepoManager::serviceSize() const
- { return _pimpl->services.size(); }
-
- RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
- { return _pimpl->services.begin(); }
-
- RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
- { return _pimpl->services.end(); }
-
- ServiceInfo RepoManager::getService( const std::string & alias ) const
- {
- for_( it, serviceBegin(), serviceEnd() )
- if ( it->alias() == alias )
- return *it;
- return ServiceInfo::noService;
- }
-
- bool RepoManager::hasService( const std::string & alias ) const
- {
- for_( it, serviceBegin(), serviceEnd() )
- if ( it->alias() == alias )
- return true;
- return false;
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void RepoManager::addService( const std::string & alias, const Url & url )
- {
- addService( ServiceInfo(alias, url) );
- }
-
- void RepoManager::addService( const ServiceInfo & service )
+ void RepoManager::Impl::addService( const ServiceInfo & service )
{
assert_alias( service );
// Writable ServiceInfo is needed to save the location
// of the .service file. Finaly insert into the service list.
ServiceInfo toSave( service );
- _pimpl->saveService( toSave );
- _pimpl->services.insert( toSave );
+ saveService( toSave );
+ _services.insert( toSave );
// check for credentials in Url (username:password, not ?credentials param)
if ( toSave.url().hasCredentialsInAuthority() )
{
media::CredentialManager cm(
- media::CredManagerOptions(_pimpl->options.rootDir) );
+ media::CredManagerOptions(_options.rootDir) );
//! \todo use a method calling UI callbacks to ask where to save creds?
cm.saveInUser(media::AuthData(toSave.url()));
////////////////////////////////////////////////////////////////////////////
- void RepoManager::removeService( const std::string & alias )
+ void RepoManager::Impl::removeService( const std::string & alias )
{
- MIL << "Going to delete repo " << alias << endl;
+ MIL << "Going to delete service " << alias << endl;
const ServiceInfo & service = getService( alias );
Pathname location = service.filepath();
if( location.empty() )
{
- ZYPP_THROW(RepoException("Can't figure where the service is stored"));
+ ZYPP_THROW(ServiceException( service, _("Can't figure out where the service is stored.") ));
}
ServiceSet tmpSet;
{
if ( filesystem::unlink(location) != 0 )
{
- ZYPP_THROW(RepoException("Can't delete " + location.asString()));
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW(ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
}
- MIL << alias << " sucessfully deleted." << endl;
+ MIL << alias << " successfully deleted." << endl;
}
else
{
filesystem::assert_dir(location.dirname());
std::ofstream file(location.c_str());
- if( file.fail() )
- ZYPP_THROW(Exception("failed open file to write"));
+ if( !file )
+ {
+ // TranslatorExplanation '%s' is a filename
+ ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
+ }
for_(it, tmpSet.begin(), tmpSet.end())
{
it->dumpAsIniOn(file);
}
- MIL << alias << " sucessfully deleted from file " << location << endl;
+ MIL << alias << " successfully deleted from file " << location << endl;
}
// now remove all repositories added by this service
RepoCollector rcollector;
getRepositoriesInService( alias,
- boost::make_function_output_iterator(
- bind( &RepoCollector::collect, &rcollector, _1 ) ) );
+ boost::make_function_output_iterator( bind( &RepoCollector::collect, &rcollector, _1 ) ) );
// cannot do this directly in getRepositoriesInService - would invalidate iterators
for_(rit, rcollector.repos.begin(), rcollector.repos.end())
removeRepository(*rit);
}
+ ////////////////////////////////////////////////////////////////////////////
- void RepoManager::removeService( const ServiceInfo & service )
- { removeService(service.alias()); }
-
- void RepoManager::refreshServices()
+ void RepoManager::Impl::refreshServices( const RefreshServiceOptions & options_r )
{
// copy the set of services since refreshService
// can eventually invalidate the iterator
if ( !it->enabled() )
continue;
- refreshService(*it);
+ try {
+ refreshService(*it, options_r);
+ }
+ catch ( const repo::ServicePluginInformalException & e )
+ { ;/* ignore ServicePluginInformalException */ }
}
}
- void RepoManager::refreshService( const ServiceInfo & dont_use_service_r )
+ void RepoManager::Impl::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
{
- assert_alias( dont_use_service_r );
- assert_url( dont_use_service_r );
-
+ ServiceInfo service( getService( alias ) );
+ assert_alias( service );
+ assert_url( service );
// NOTE: It might be necessary to modify and rewrite the service info.
// Either when probing the type, or when adjusting the repositories
- // enable/disable state. Thus 'dont_use_service_r' but 'service':
- ServiceInfo service( dont_use_service_r );
+ // enable/disable state.:
bool serviceModified = false;
- MIL << "going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
+ MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << ", opts: " << options_r << endl;
//! \todo add callbacks for apps (start, end, repo removed, repo added, repo changed)
}
}
- // download the repo index file
- media::MediaManager mediamanager;
- media::MediaAccessId mid = mediamanager.open( service.url() );
- mediamanager.attachDesiredMedia( mid );
- mediamanager.provideFile( mid, "repo/repoindex.xml" );
- Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
+ // get target distro identifier
+ std::string servicesTargetDistro = _options.servicesTargetDistro;
+ if ( servicesTargetDistro.empty() )
+ {
+ servicesTargetDistro = Target::targetDistribution( Pathname() );
+ }
+ DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl;
// parse it
- RepoCollector collector(_pimpl->options.servicesTargetDistro);
- parser::RepoindexFileReader reader( path, bind( &RepoCollector::collect, &collector, _1 ) );
- mediamanager.release( mid );
- mediamanager.close( mid );
+ RepoCollector collector(servicesTargetDistro);
+ // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException
+ // which is actually a notification. Using an exception for this
+ // instead of signal/callback is bad. Needs to be fixed here, in refreshServices()
+ // and in zypper.
+ std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
+ try {
+ ServiceRepos repos(service, bind( &RepoCollector::collect, &collector, _1 ));
+ }
+ catch ( const repo::ServicePluginInformalException & e )
+ {
+ /* ignore ServicePluginInformalException and throw later */
+ uglyHack.first = true;
+ uglyHack.second = e;
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ // On the fly remember the new repo states as defined the reopoindex.xml.
+ // Move into ServiceInfo later.
+ ServiceInfo::RepoStates newRepoStates;
// set service alias and base url for all collected repositories
for_( it, collector.repos.begin(), collector.repos.end() )
{
+ // First of all: Prepend service alias:
+ it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
+ // set refrence to the parent service
+ it->setService( service.alias() );
+
+ // remember the new parsed repo state
+ newRepoStates[it->alias()] = *it;
+
// if the repo url was not set by the repoindex parser, set service's url
Url url;
-
if ( it->baseUrlsEmpty() )
url = service.url();
else
it->setPath("");
}
- // Prepend service alias:
- it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
-
// save the url
it->setBaseUrl( url );
- // set refrence to the parent service
- it->setService( service.alias() );
}
////////////////////////////////////////////////////////////////////////////
RepoInfoList oldRepos;
getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
+ ////////////////////////////////////////////////////////////////////////////
// find old repositories to remove...
- for_( it, oldRepos.begin(), oldRepos.end() )
+ for_( oldRepo, oldRepos.begin(), oldRepos.end() )
{
- if ( ! foundAliasIn( it->alias(), collector.repos ) )
+ if ( ! foundAliasIn( oldRepo->alias(), collector.repos ) )
{
- removeRepository( *it );
+ if ( oldRepo->enabled() )
+ {
+ // Currently enabled. If this was a user modification remember the state.
+ const auto & last = service.repoStates().find( oldRepo->alias() );
+ if ( last != service.repoStates().end() && ! last->second.enabled )
+ {
+ DBG << "Service removes user enabled repo " << oldRepo->alias() << endl;
+ service.addRepoToEnable( oldRepo->alias() );
+ serviceModified = true;
+ }
+ else
+ DBG << "Service removes enabled repo " << oldRepo->alias() << endl;
+ }
+ else
+ DBG << "Service removes disabled repo " << oldRepo->alias() << endl;
+
+ removeRepository( *oldRepo );
}
}
// create missing repositories and modify exising ones if needed...
for_( it, collector.repos.begin(), collector.repos.end() )
{
- // Service explicitly requests the repo being enabled?
- // Service explicitly requests the repo being disabled?
+ // User explicitly requested the repo being enabled?
+ // User explicitly requested the repo being disabled?
// And hopefully not both ;) If so, enable wins.
- bool beEnabled = service.repoToEnableFind( it->alias() );
- bool beDisabled = service.repoToDisableFind( it->alias() );
- if ( beEnabled )
+ TriBool toBeEnabled( indeterminate ); // indeterminate - follow the service request
+ DBG << "Service request to " << (it->enabled()?"enable":"disable") << " service repo " << it->alias() << endl;
+
+ if ( options_r.testFlag( RefreshService_restoreStatus ) )
{
- // Remove from enable request list.
- // NOTE: repoToDisable is handled differently.
- // It gets cleared on each refresh.
- service.delRepoToEnable( it->alias() );
- serviceModified = true;
+ DBG << "Opt RefreshService_restoreStatus " << it->alias() << endl;
+ // this overrides any pending request!
+ // Remove from enable request list.
+ // NOTE: repoToDisable is handled differently.
+ // It gets cleared on each refresh.
+ service.delRepoToEnable( it->alias() );
+ // toBeEnabled stays indeterminate!
+ }
+ else
+ {
+ if ( service.repoToEnableFind( it->alias() ) )
+ {
+ DBG << "User request to enable service repo " << it->alias() << endl;
+ toBeEnabled = true;
+ // Remove from enable request list.
+ // NOTE: repoToDisable is handled differently.
+ // It gets cleared on each refresh.
+ service.delRepoToEnable( it->alias() );
+ serviceModified = true;
+ }
+ else if ( service.repoToDisableFind( it->alias() ) )
+ {
+ DBG << "User request to disable service repo " << it->alias() << endl;
+ toBeEnabled = false;
+ }
}
RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
{
// Not found in oldRepos ==> a new repo to add
- // Make sure the service repo is created with the
- // appropriate enable and autorefresh true.
- it->setEnabled( beEnabled );
- it->setAutorefresh( true );
+ // Make sure the service repo is created with the appropriate enablement
+ if ( ! indeterminate(toBeEnabled) )
+ it->setEnabled( toBeEnabled );
- // At that point check whether a repo with the same alias
- // exists outside this service. Maybe forcefully re-alias
- // the existing repo?
+ DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
addRepository( *it );
-
- // save repo credentials
- // ma@: task for modifyRepository?
}
else
{
// ==> an exising repo to check
bool oldRepoModified = false;
- if ( beEnabled )
+ if ( indeterminate(toBeEnabled) )
+ {
+ // No user request: check for an old user modificaton otherwise follow service request.
+ // NOTE: Assert toBeEnabled is boolean afterwards!
+ if ( oldRepo->enabled() == it->enabled() )
+ toBeEnabled = it->enabled(); // service requests no change to the system
+ else if (options_r.testFlag( RefreshService_restoreStatus ) )
+ {
+ toBeEnabled = it->enabled(); // RefreshService_restoreStatus forced
+ DBG << "Opt RefreshService_restoreStatus " << it->alias() << " forces " << (toBeEnabled?"enabled":"disabled") << endl;
+ }
+ else
+ {
+ const auto & last = service.repoStates().find( oldRepo->alias() );
+ if ( last == service.repoStates().end() || last->second.enabled != it->enabled() )
+ toBeEnabled = it->enabled(); // service request has changed since last refresh -> follow
+ else
+ {
+ toBeEnabled = oldRepo->enabled(); // service request unchaned since last refresh -> keep user modification
+ DBG << "User modified service repo " << it->alias() << " may stay " << (toBeEnabled?"enabled":"disabled") << endl;
+ }
+ }
+ }
+
+ // changed enable?
+ if ( toBeEnabled == oldRepo->enabled() )
+ {
+ DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
+ }
+ else if ( toBeEnabled )
+ {
+ DBG << "Service repo " << it->alias() << " gets enabled" << endl;
+ oldRepo->setEnabled( true );
+ oldRepoModified = true;
+ }
+ else
{
- if ( ! oldRepo->enabled() )
- {
- oldRepo->setEnabled( true );
- oldRepoModified = true;
- }
- }
- else if ( beDisabled )
+ DBG << "Service repo " << it->alias() << " gets disabled" << endl;
+ oldRepo->setEnabled( false );
+ oldRepoModified = true;
+ }
+
+ // all other attributes follow the service request:
+
+ // changed autorefresh
+ if ( oldRepo->autorefresh() != it->autorefresh() )
+ {
+ DBG << "Service repo " << it->alias() << " gets new AUTOREFRESH " << it->autorefresh() << endl;
+ oldRepo->setAutorefresh( it->autorefresh() );
+ oldRepoModified = true;
+ }
+
+ // changed priority?
+ if ( oldRepo->priority() != it->priority() )
+ {
+ DBG << "Service repo " << it->alias() << " gets new PRIORITY " << it->priority() << endl;
+ oldRepo->setPriority( it->priority() );
+ oldRepoModified = true;
+ }
+
+ // changed url?
+ // service repo can contain only one URL now, so no need to iterate.
+ if ( oldRepo->url() != it->url() )
{
- if ( oldRepo->enabled() )
- {
- oldRepo->setEnabled( false );
- oldRepoModified = true;
- }
+ DBG << "Service repo " << it->alias() << " gets new URL " << it->url() << endl;
+ oldRepo->setBaseUrl( it->url() );
+ oldRepoModified = true;
}
-#warning also check changed URL due to PATH/URL change in service, but ignore ?credentials param!
-// ma@: task for modifyRepository?
-
// save if modified:
if ( oldRepoModified )
{
serviceModified = true;
}
+ // Remember original service request for next refresh
+ if ( service.repoStates() != newRepoStates )
+ {
+ service.setRepoStates( std::move(newRepoStates) );
+ serviceModified = true;
+ }
+
////////////////////////////////////////////////////////////////////////////
- // save service if modified:
- if ( serviceModified )
+ // save service if modified: (unless a plugin service)
+ if ( serviceModified && service.type() != ServiceType::PLUGIN )
{
// write out modified service file.
modifyService( service.alias(), service );
}
+
+ if ( uglyHack.first )
+ {
+ throw( uglyHack.second ); // intentionally not ZYPP_THROW
+ }
}
+ ////////////////////////////////////////////////////////////////////////////
- void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & service)
+ void RepoManager::Impl::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
{
MIL << "Going to modify service " << oldAlias << endl;
+ // we need a writable copy to link it to the file where
+ // it is saved if we modify it
+ ServiceInfo service(newService);
+
+ if ( service.type() == ServiceType::PLUGIN )
+ {
+ ZYPP_THROW(ServicePluginImmutableException( service ));
+ }
+
const ServiceInfo & oldService = getService(oldAlias);
Pathname location = oldService.filepath();
if( location.empty() )
{
- ZYPP_THROW(RepoException(
- "Cannot figure out where the service file is stored."));
+ ZYPP_THROW(ServiceException( oldService, _("Can't figure out where the service is stored.") ));
}
// remember: there may multiple services being defined in one file:
}
service.dumpAsIniOn(file);
file.close();
+ service.setFilepath(location);
- _pimpl->services.erase(oldAlias);
- _pimpl->services.insert(service);
+ _services.erase(oldAlias);
+ _services.insert(service);
// changed properties affecting also repositories
- if( oldAlias != service.alias() // changed alias
- || oldService.enabled() != service.enabled() // changed enabled status
- )
+ if ( oldAlias != service.alias() // changed alias
+ || oldService.enabled() != service.enabled() ) // changed enabled status
{
std::vector<RepoInfo> toModify;
getRepositoriesInService(oldAlias, std::back_inserter(toModify));
for_( it, toModify.begin(), toModify.end() )
{
- if (oldService.enabled() && !service.enabled())
- it->setEnabled(false);
- else if (!oldService.enabled() && service.enabled())
- {
- //! \todo do nothing? the repos will be enabled on service refresh
- //! \todo how to know the service needs a (auto) refresh????
- }
- else
+ if ( oldService.enabled() != service.enabled() )
+ {
+ if ( service.enabled() )
+ {
+ // reset to last refreshs state
+ const auto & last = service.repoStates().find( it->alias() );
+ if ( last != service.repoStates().end() )
+ it->setEnabled( last->second.enabled );
+ }
+ else
+ it->setEnabled( false );
+ }
+
+ if ( oldAlias != service.alias() )
it->setService(service.alias());
+
modifyRepository(it->alias(), *it);
}
}
//! \todo refresh the service automatically if url is changed?
}
- repo::ServiceType RepoManager::probeService( const Url &url ) const
+ ////////////////////////////////////////////////////////////////////////////
+
+ repo::ServiceType RepoManager::Impl::probeService( const Url & url ) const
{
try
{
catch ( const media::MediaException &e )
{
ZYPP_CAUGHT(e);
- RepoException enew("Error trying to read from " + url.asString());
+ // TranslatorExplanation '%s' is an URL
+ RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
enew.remember(e);
ZYPP_THROW(enew);
}
catch ( const Exception &e )
{
ZYPP_CAUGHT(e);
- Exception enew("Unknown error reading from " + url.asString());
+ // TranslatorExplanation '%s' is an URL
+ Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
enew.remember(e);
ZYPP_THROW(enew);
}
return repo::ServiceType::NONE;
}
- ////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : RepoManager
+ //
+ ///////////////////////////////////////////////////////////////////
- std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
+ RepoManager::RepoManager( const RepoManagerOptions & opt )
+ : _pimpl( new Impl(opt) )
+ {}
+
+ RepoManager::~RepoManager()
+ {}
+
+ bool RepoManager::repoEmpty() const
+ { return _pimpl->repoEmpty(); }
+
+ RepoManager::RepoSizeType RepoManager::repoSize() const
+ { return _pimpl->repoSize(); }
+
+ RepoManager::RepoConstIterator RepoManager::repoBegin() const
+ { return _pimpl->repoBegin(); }
+
+ RepoManager::RepoConstIterator RepoManager::repoEnd() const
+ { return _pimpl->repoEnd(); }
+
+ RepoInfo RepoManager::getRepo( const std::string & alias ) const
+ { return _pimpl->getRepo( alias ); }
+
+ bool RepoManager::hasRepo( const std::string & alias ) const
+ { return _pimpl->hasRepo( alias ); }
+
+ std::string RepoManager::makeStupidAlias( const Url & url_r )
{
- return str << *obj._pimpl;
+ std::string ret( url_r.getScheme() );
+ if ( ret.empty() )
+ ret = "repo-";
+ else
+ ret += "-";
+
+ std::string host( url_r.getHost() );
+ if ( ! host.empty() )
+ {
+ ret += host;
+ ret += "-";
+ }
+
+ static Date::ValueType serial = Date::now();
+ ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8);
+ return ret;
}
+ RepoStatus RepoManager::metadataStatus( const RepoInfo & info ) const
+ { return _pimpl->metadataStatus( info ); }
+
+ RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata( const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy )
+ { return _pimpl->checkIfToRefreshMetadata( info, url, policy ); }
+
+ Pathname RepoManager::metadataPath( const RepoInfo &info ) const
+ { return _pimpl->metadataPath( info ); }
+
+ Pathname RepoManager::packagesPath( const RepoInfo &info ) const
+ { return _pimpl->packagesPath( info ); }
+
+ void RepoManager::refreshMetadata( const RepoInfo &info, RawMetadataRefreshPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->refreshMetadata( info, policy, progressrcv ); }
+
+ void RepoManager::cleanMetadata( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->cleanMetadata( info, progressrcv ); }
+
+ void RepoManager::cleanPackages( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->cleanPackages( info, progressrcv ); }
+
+ RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
+ { return _pimpl->cacheStatus( info ); }
+
+ void RepoManager::buildCache( const RepoInfo &info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->buildCache( info, policy, progressrcv ); }
+
+ void RepoManager::cleanCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->cleanCache( info, progressrcv ); }
+
+ bool RepoManager::isCached( const RepoInfo &info ) const
+ { return _pimpl->isCached( info ); }
+
+ void RepoManager::loadFromCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->loadFromCache( info, progressrcv ); }
+
+ void RepoManager::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->cleanCacheDirGarbage( progressrcv ); }
+
+ repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const
+ { return _pimpl->probe( url, path ); }
+
+ repo::RepoType RepoManager::probe( const Url & url ) const
+ { return _pimpl->probe( url ); }
+
+ void RepoManager::addRepository( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->addRepository( info, progressrcv ); }
+
+ void RepoManager::addRepositories( const Url &url, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->addRepositories( url, progressrcv ); }
+
+ void RepoManager::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->removeRepository( info, progressrcv ); }
+
+ void RepoManager::modifyRepository( const std::string &alias, const RepoInfo & newinfo, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->modifyRepository( alias, newinfo, progressrcv ); }
+
+ RepoInfo RepoManager::getRepositoryInfo( const std::string &alias, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->getRepositoryInfo( alias, progressrcv ); }
+
+ RepoInfo RepoManager::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
+ { return _pimpl->getRepositoryInfo( url, urlview, progressrcv ); }
+
+ bool RepoManager::serviceEmpty() const
+ { return _pimpl->serviceEmpty(); }
+
+ RepoManager::ServiceSizeType RepoManager::serviceSize() const
+ { return _pimpl->serviceSize(); }
+
+ RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
+ { return _pimpl->serviceBegin(); }
+
+ RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
+ { return _pimpl->serviceEnd(); }
+
+ ServiceInfo RepoManager::getService( const std::string & alias ) const
+ { return _pimpl->getService( alias ); }
+
+ bool RepoManager::hasService( const std::string & alias ) const
+ { return _pimpl->hasService( alias ); }
+
+ repo::ServiceType RepoManager::probeService( const Url &url ) const
+ { return _pimpl->probeService( url ); }
+
+ void RepoManager::addService( const std::string & alias, const Url& url )
+ { return _pimpl->addService( alias, url ); }
+
+ void RepoManager::addService( const ServiceInfo & service )
+ { return _pimpl->addService( service ); }
+
+ void RepoManager::removeService( const std::string & alias )
+ { return _pimpl->removeService( alias ); }
+
+ void RepoManager::removeService( const ServiceInfo & service )
+ { return _pimpl->removeService( service ); }
+
+ void RepoManager::refreshServices( const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshServices( options_r ); }
+
+ void RepoManager::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshService( alias, options_r ); }
+
+ void RepoManager::refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
+ { return _pimpl->refreshService( service, options_r ); }
+
+ void RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & service )
+ { return _pimpl->modifyService( oldAlias, service ); }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
+ { return str << *obj._pimpl; }
+
/////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////