Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / RepoManager.cc
index d9ce8bf..3853444 100644 (file)
@@ -64,6 +64,76 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
   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
      */
@@ -224,7 +294,7 @@ namespace zypp
       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
       {
@@ -242,7 +312,7 @@ namespace zypp
          {
            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
            {
@@ -520,6 +590,7 @@ namespace zypp
     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 );
 
@@ -775,7 +846,7 @@ namespace zypp
          // 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 {
@@ -812,7 +883,7 @@ namespace zypp
     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() )
@@ -846,7 +917,7 @@ namespace zypp
     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;
@@ -1130,6 +1201,9 @@ namespace zypp
         // 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;
@@ -1235,7 +1309,7 @@ namespace zypp
     {
       case RepoType::NONE_e:
         // unknown, probe the local metadata
-        repokind = probe( productdatapath.asUrl() );
+        repokind = probeCache( productdatapath );
       break;
       default:
       break;
@@ -1307,6 +1381,13 @@ namespace zypp
 
   ////////////////////////////////////////////////////////////////////////////
 
+
+  /** 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;
@@ -1391,6 +1472,28 @@ namespace zypp
     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 )
@@ -1559,26 +1662,9 @@ namespace zypp
     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;
@@ -1672,10 +1758,12 @@ namespace zypp
       {
         // 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() )));
@@ -1778,6 +1866,8 @@ namespace zypp
       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;
     }
@@ -1831,15 +1921,8 @@ namespace zypp
     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;
   }
@@ -1979,34 +2062,41 @@ namespace zypp
     {
       // 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 );
     }
 
     ////////////////////////////////////////////////////////////////////////////
@@ -2042,7 +2132,8 @@ namespace zypp
     }
 
     ////////////////////////////////////////////////////////////////////////////
-    // 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?
@@ -2167,13 +2258,37 @@ namespace zypp
        }
 
         // 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 )
@@ -2251,6 +2366,9 @@ namespace zypp
 
     _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