///////////////////////////////////////////////////////////////////
namespace
{
+ ///////////////////////////////////////////////////////////////////
+ /// \class UrlCredentialExtractor
+ /// \brief Extract credentials in \ref Url authority and store them via \ref CredentialManager.
+ ///
+ /// Lazy init CredentialManager and save collected credentials when
+ /// going out of scope.
+ ///
+ /// Methods return whether a password has been collected/extracted.
+ ///
+ /// \code
+ /// UrlCredentialExtractor( "/rootdir" ).collect( oneUrlOrUrlContainer );
+ /// \endcode
+ /// \code
+ /// {
+ /// UrlCredentialExtractor extractCredentials;
+ /// extractCredentials.collect( oneUrlOrUrlContainer );
+ /// extractCredentials.extract( oneMoreUrlOrUrlContainer );
+ /// ....
+ /// }
+ /// \endcode
+ ///
+ class UrlCredentialExtractor
+ {
+ public:
+ UrlCredentialExtractor( Pathname & root_r )
+ : _root( root_r )
+ {}
+
+ ~UrlCredentialExtractor()
+ { if ( _cmPtr ) _cmPtr->save(); }
+
+ /** Remember credentials stored in URL authority leaving the password in \a url_r. */
+ bool collect( const Url & url_r )
+ {
+ bool ret = url_r.hasCredentialsInAuthority();
+ if ( ret )
+ {
+ if ( !_cmPtr ) _cmPtr.reset( new media::CredentialManager( _root ) );
+ _cmPtr->addUserCred( url_r );
+ }
+ return ret;
+ }
+ /** \overload operating on Url container */
+ template<class TContainer>
+ bool collect( const TContainer & urls_r )
+ { bool ret = false; for ( const Url & url : urls_r ) { if ( collect( url ) && !ret ) ret = true; } return ret; }
+
+ /** Remember credentials stored in URL authority stripping the passowrd from \a url_r. */
+ bool extract( Url & url_r )
+ {
+ bool ret = collect( url_r );
+ if ( ret )
+ url_r.setPassword( std::string() );
+ return ret;
+ }
+ /** \overload operating on Url container */
+ template<class TContainer>
+ bool extract( TContainer & urls_r )
+ { bool ret = false; for ( Url & url : urls_r ) { if ( extract( url ) && !ret ) ret = true; } return ret; }
+
+ private:
+ const Pathname & _root;
+ scoped_ptr<media::CredentialManager> _cmPtr;
+ };
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ {
/** Simple media mounter to access non-downloading URLs e.g. for non-local plaindir repos.
* \ingroup g_RAII
*/
bool nonroot( geteuid() != 0 );
if ( nonroot && ! PathInfo(dir).userMayRX() )
{
- JobReport::warning( formatNAC(_("Cannot read repo directory '%1%': Permission denied")) % dir );
+ JobReport::warning( str::FormatNAC(_("Cannot read repo directory '%1%': Permission denied")) % dir );
}
else
{
{
if ( nonroot && ! PathInfo(*it).userMayR() )
{
- JobReport::warning( formatNAC(_("Cannot read repo file '%1%': Permission denied")) % *it );
+ JobReport::warning( str::FormatNAC(_("Cannot read repo file '%1%': Permission denied")) % *it );
}
else
{
void buildCache( const RepoInfo & info, CacheBuildPolicy policy, OPT_PROGRESS );
repo::RepoType probe( const Url & url, const Pathname & path = Pathname() ) const;
+ repo::RepoType probeCache( const Pathname & path_r ) const;
void cleanCacheDirGarbage( OPT_PROGRESS );
// 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%'" ))
+ JobReport::warning( str::FormatNAC(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
% repoInfo.service()
% repoInfo.alias() );
try {
RepoType repokind = info.type();
// If unknown, probe the local metadata
if ( repokind == RepoType::NONE )
- repokind = probe( productdatapath.asUrl() );
+ repokind = probeCache( productdatapath );
RepoStatus status;
switch ( repokind.toEnum() )
RepoType repokind = info.type();
if ( repokind.toEnum() == RepoType::NONE_e )
// unknown, probe the local metadata
- repokind = probe( productdatapath.asUrl() );
+ repokind = probeCache( productdatapath );
// if still unknown, just return
if (repokind == RepoType::NONE_e)
return;
// cause of the problem of the first URL remembered
if (it == info.baseUrlsBegin())
rexception.remember(e);
+ else
+ rexception.addHistory( e.asUserString() );
+
}
} // for every url
ERR << "No more urls..." << endl;
{
case RepoType::NONE_e:
// unknown, probe the local metadata
- repokind = probe( productdatapath.asUrl() );
+ repokind = probeCache( productdatapath );
break;
default:
break;
////////////////////////////////////////////////////////////////////////////
+
+ /** Probe the metadata type of a repository located at \c url.
+ * Urls here may be rewritten by \ref MediaSetAccess to reflect the correct media number.
+ *
+ * \note Metadata in local cache directories must be probed using \ref probeCache as
+ * a cache path must not be rewritten (bnc#946129)
+ */
repo::RepoType RepoManager::Impl::probe( const Url & url, const Pathname & path ) const
{
MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
return repo::RepoType::NONE;
}
+ /** Probe Metadata in a local cache directory
+ *
+ * \note Metadata in local cache directories must not be probed using \ref probe as
+ * a cache path must not be rewritten (bnc#946129)
+ */
+ repo::RepoType RepoManager::Impl::probeCache( const Pathname & path_r ) const
+ {
+ MIL << "going to probe the cached repo at " << path_r << endl;
+
+ repo::RepoType ret = repo::RepoType::NONE;
+
+ if ( PathInfo(path_r/"/repodata/repomd.xml").isFile() )
+ { ret = repo::RepoType::RPMMD; }
+ else if ( PathInfo(path_r/"/content").isFile() )
+ { ret = repo::RepoType::YAST2; }
+ else if ( PathInfo(path_r).isDir() )
+ { ret = repo::RepoType::RPMPLAINDIR; }
+
+ MIL << "Probed cached type " << ret << " at " << path_r << endl;
+ return ret;
+ }
+
////////////////////////////////////////////////////////////////////////////
void RepoManager::Impl::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
progress.set(90);
// check for credentials in Urls
- bool havePasswords = false;
- for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() )
- if ( urlit->hasCredentialsInAuthority() )
- {
- havePasswords = true;
- break;
- }
- // save the credentials
- if ( havePasswords )
- {
- media::CredentialManager cm(
- media::CredManagerOptions(_options.rootDir) );
-
- for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd())
- if (urlit->hasCredentialsInAuthority())
- //! \todo use a method calling UI callbacks to ask where to save creds?
- cm.saveInUser(media::AuthData(*urlit));
- }
+ UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
- HistoryLog().addRepository(tosave);
+ HistoryLog(_options.rootDir).addRepository(tosave);
progress.toMax();
MIL << "done" << endl;
{
// figure how many repos are there in the file:
std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
- if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
+ if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
+ ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
{
- // easy, only this one, just delete the file
- if ( filesystem::unlink(todelete.filepath()) != 0 )
+ // easy: file does not exist, contains no or only the repo to delete: delete the file
+ int ret = filesystem::unlink( todelete.filepath() );
+ if ( ! ( ret == 0 || ret == ENOENT ) )
{
// TranslatorExplanation '%s' is a filename
ZYPP_THROW(RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
newinfo.setFilepath(toedit.filepath());
reposManip().erase(toedit);
reposManip().insert(newinfo);
+ // check for credentials in Urls
+ UrlCredentialExtractor( _options.rootDir ).collect( newinfo.baseUrls() );
HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
MIL << "repo " << alias << " modified" << endl;
}
saveService( toSave );
_services.insert( toSave );
- // check for credentials in Url (username:password, not ?credentials param)
- if ( toSave.url().hasCredentialsInAuthority() )
- {
- media::CredentialManager cm(
- media::CredManagerOptions(_options.rootDir) );
-
- //! \todo use a method calling UI callbacks to ask where to save creds?
- cm.saveInUser(media::AuthData(toSave.url()));
- }
+ // check for credentials in Url
+ UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
MIL << "added service " << toSave.alias() << endl;
}
{
// 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
+ // set reference 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.rawUrl();
- else
+ // - If the repo url was not set by the repoindex parser, set service's url.
+ // - Libzypp currently has problem with separate url + path handling so just
+ // append a path, if set, to the baseurls
+ // - Credentials in the url authority will be extracted later, either if the
+ // repository is added or if we check for changed urls.
+ Pathname path;
+ if ( !it->path().empty() )
{
- // service repo can contain only one URL now, so no need to iterate.
- url = it->rawUrl(); // raw!
+ if ( it->path() != "/" )
+ path = it->path();
+ it->setPath("");
}
- // libzypp currently has problem with separate url + path handling
- // so just append the path to the baseurl
- if ( !it->path().empty() )
+ if ( it->baseUrlsEmpty() )
{
- Pathname path(url.getPathName());
- path /= it->path();
- url.setPathName( path.asString() );
- it->setPath("");
+ Url url( service.rawUrl() );
+ if ( !path.empty() )
+ url.setPathName( url.getPathName() / path );
+ it->setBaseUrl( std::move(url) );
+ }
+ else if ( !path.empty() )
+ {
+ RepoInfo::url_set urls( it->rawBaseUrls() );
+ for ( Url & url : urls )
+ {
+ url.setPathName( url.getPathName() / path );
+ }
+ it->setBaseUrls( std::move(urls) );
}
-
- // save the url
- it->setBaseUrl( url );
}
////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////
- // create missing repositories and modify exising ones if needed...
+ // create missing repositories and modify existing ones if needed...
+ UrlCredentialExtractor urlCredentialExtractor( _options.rootDir ); // To collect any credentials stored in repo URLs
for_( it, collector.repos.begin(), collector.repos.end() )
{
// User explicitly requested the repo being enabled?
}
// changed url?
- // service repo can contain only one URL now, so no need to iterate.
- if ( oldRepo->rawUrl() != it->rawUrl() )
{
- DBG << "Service repo " << it->alias() << " gets new URL " << it->rawUrl() << endl;
- oldRepo->setBaseUrl( it->rawUrl() );
- oldRepoModified = true;
- }
+ RepoInfo::url_set newUrls( it->rawBaseUrls() );
+ urlCredentialExtractor.extract( newUrls ); // Extract! to prevent passwds from disturbing the comparison below
+ if ( oldRepo->rawBaseUrls() != newUrls )
+ {
+ DBG << "Service repo " << it->alias() << " gets new URLs " << newUrls << endl;
+ oldRepo->setBaseUrls( std::move(newUrls) );
+ oldRepoModified = true;
+ }
+ }
+
+ // changed gpg check settings?
+ // ATM only plugin services can set GPG values.
+ if ( service.type() == ServiceType::PLUGIN )
+ {
+ TriBool ogpg[3]; // Gpg RepoGpg PkgGpg
+ TriBool ngpg[3];
+ oldRepo->getRawGpgChecks( ogpg[0], ogpg[1], ogpg[2] );
+ it-> getRawGpgChecks( ngpg[0], ngpg[1], ngpg[2] );
+#define Z_CHKGPG(I,N) \
+ if ( ! sameTriboolState( ogpg[I], ngpg[I] ) ) \
+ { \
+ DBG << "Service repo " << it->alias() << " gets new "#N"Check " << ngpg[I] << endl; \
+ oldRepo->set##N##Check( ngpg[I] ); \
+ oldRepoModified = true; \
+ }
+ Z_CHKGPG( 0, Gpg );
+ Z_CHKGPG( 1, RepoGpg );
+ Z_CHKGPG( 2, PkgGpg );
+#undef Z_CHKGPG
+ }
// save if modified:
if ( oldRepoModified )
_services.erase(oldAlias);
_services.insert(service);
+ // check for credentials in Urls
+ UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
+
// changed properties affecting also repositories
if ( oldAlias != service.alias() // changed alias