Imported Upstream version 17.14.1
[platform/upstream/libzypp.git] / zypp / target / rpm / RpmDb.cc
index a76ddac..7203d79 100644 (file)
  *
 */
 #include "librpm.h"
-
+extern "C"
+{
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlog.h>
+}
 #include <cstdlib>
 #include <cstdio>
 #include <ctime>
 #include <vector>
 #include <algorithm>
 
-#include <boost/format.hpp>
-
 #include "zypp/base/Logger.h"
 #include "zypp/base/String.h"
 #include "zypp/base/Gettext.h"
+#include "zypp/base/LocaleGuard.h"
 
 #include "zypp/Date.h"
 #include "zypp/Pathname.h"
 #include "zypp/ZYppFactory.h"
 #include "zypp/ZConfig.h"
 
-using namespace std;
+using std::endl;
 using namespace zypp::filesystem;
 
 #define WARNINGMAILPATH                "/var/log/YaST2/"
 #define FILEFORBACKUPFILES     "YaSTBackupModifiedFiles"
 #define MAXRPMMESSAGELINES     10000
 
+#define WORKAROUNDRPMPWDBUG
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
+
 namespace zypp
 {
+  namespace zypp_readonly_hack
+  {
+    bool IGotIt(); // in readonly-mode
+  }
 namespace target
 {
 namespace rpm
@@ -67,11 +79,11 @@ const char* quoteInFilename_m = "\'\"";
 #else
 const char* quoteInFilename_m = " \t\'\"";
 #endif
-inline string rpmQuoteFilename( const Pathname & path_r )
+inline std::string rpmQuoteFilename( const Pathname & path_r )
 {
-  string path( path_r.asString() );
-  for ( string::size_type pos = path.find_first_of( quoteInFilename_m );
-        pos != string::npos;
+  std::string path( path_r.asString() );
+  for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
+        pos != std::string::npos;
         pos = path.find_first_of( quoteInFilename_m, pos ) )
   {
     path.insert( pos, "\\" );
@@ -79,6 +91,26 @@ inline string rpmQuoteFilename( const Pathname & path_r )
   }
   return path;
 }
+
+
+  /** Workaround bnc#827609 - rpm needs a readable pwd so we
+   * chdir to /. Turn realtive pathnames into absolute ones
+   * by prepending cwd so rpm still finds them
+   */
+  inline Pathname workaroundRpmPwdBug( Pathname path_r )
+  {
+#if defined(WORKAROUNDRPMPWDBUG)
+    if ( path_r.relative() )
+    {
+      // try to prepend cwd
+      AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
+      if ( cwd )
+       return Pathname( cwd ) / path_r;
+      WAR << "Can't get cwd!" << endl;
+    }
+#endif
+    return path_r;     // no problem with absolute pathnames
+  }
 }
 
 struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
@@ -95,31 +127,14 @@ struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
 
   virtual void trustedKeyAdded( const PublicKey &key )
   {
-    MIL << "trusted key added to zypp Keyring. Importing" << endl;
-    // now import the key in rpm
-    try
-    {
-      _rpmdb.importPubkey( key );
-    }
-    catch (RpmException &e)
-    {
-      ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
-    }
+    MIL << "trusted key added to zypp Keyring. Importing..." << endl;
+    _rpmdb.importPubkey( key );
   }
 
   virtual void trustedKeyRemoved( const PublicKey &key  )
   {
     MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
-
-    // remove the key from rpm
-    try
-    {
-      _rpmdb.removePubkey( key );
-    }
-    catch (RpmException &e)
-    {
-      ERR << "Could not remove key " << key.id() << " (" << key.name() << ") from rpm database" << endl;
-    }
+    _rpmdb.removePubkey( key );
   }
 
   RpmDb &_rpmdb;
@@ -127,7 +142,7 @@ struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
 
 static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
 
-unsigned diffFiles(const string file1, const string file2, string& out, int maxlines)
+unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
 {
   const char* argv[] =
     {
@@ -142,7 +157,7 @@ unsigned diffFiles(const string file1, const string file2, string& out, int maxl
   //if(!prog)
   //return 2;
 
-  string line;
+  std::string line;
   int count = 0;
   for (line = prog.receiveLine(), count=0;
        !line.empty();
@@ -161,9 +176,9 @@ unsigned diffFiles(const string file1, const string file2, string& out, int maxl
  **
  **
  **    FUNCTION NAME : stringPath
- **    FUNCTION TYPE : inline string
+ **    FUNCTION TYPE : inline std::string
 */
-inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
+inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
 {
   return librpmDb::stringPath( root_r, sub_r );
 }
@@ -172,9 +187,9 @@ inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
  **
  **
  **    FUNCTION NAME : operator<<
- **    FUNCTION TYPE : ostream &
+ **    FUNCTION TYPE : std::ostream &
 */
-ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
+std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
 {
   if ( obj == RpmDb::DbSI_NO_INIT )
   {
@@ -267,9 +282,9 @@ Date RpmDb::timestamp() const
 //
 //
 //     METHOD NAME : RpmDb::dumpOn
-//     METHOD TYPE : ostream &
+//     METHOD TYPE : std::ostream &
 //
-ostream & RpmDb::dumpOn( ostream & str ) const
+std::ostream & RpmDb::dumpOn( std::ostream & str ) const
 {
   str << "RpmDb[";
 
@@ -319,6 +334,14 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
     ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
   }
 
+  if ( dbPath_r == "/var/lib/rpm"
+    && ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
+    && PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
+  {
+    WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
+    filesystem::symlink( "../../usr/lib/sysimage/rpm", root_r/"/var/lib/rpm" );
+  }
+
   MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
       << ( doRebuild_r ? " (rebuilddb)" : "" )
       << ( quickinit ? " (quickinit)" : "" ) << endl;
@@ -400,10 +423,8 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
     }
   }
 
-  MIL << "Syncronizing keys with zypp keyring" << endl;
-  // we do this one by one now.
-  importZyppKeyRingTrustedKeys();
-  exportTrustedKeysInZyppKeyRing();
+  MIL << "Synchronizing keys with zypp keyring" << endl;
+  syncTrustedKeys();
 
   // Close the database in case any write acces (create/convert)
   // happened during init. This should drop any lock acquired
@@ -790,8 +811,8 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
                                                     process?process->getpid():0) )
                       + "Packages" );
 
-  string       line;
-  string       errmsg;
+  std::string       line;
+  std::string       errmsg;
 
   while ( systemReadLine( line ) )
   {
@@ -819,8 +840,7 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
   if ( rpm_status != 0 )
   {
     //TranslatorExplanation after semicolon is error message
-    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
-               (errmsg.empty() ? error_message: errmsg))));
+    ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
   }
   else
   {
@@ -828,87 +848,221 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
   }
 }
 
-void RpmDb::importZyppKeyRingTrustedKeys()
+///////////////////////////////////////////////////////////////////
+namespace
 {
-  MIL << "Importing zypp trusted keyring" << std::endl;
-
-  std::list<PublicKey> zypp_keys;
-  zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
-  /* The pubkeys() call below is expensive.  It calls gpg2 for each
-     gpg-pubkey in the rpm db.  Useless if we don't have any keys in
-     zypp yet.  */
-  if (zypp_keys.empty())
-    return;
+  /** \ref RpmDb::syncTrustedKeys helper
+   * Compute which keys need to be exprted to / imported from the zypp keyring.
+   * Return result via argument list.
+   */
+  void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
+  {
+    ///////////////////////////////////////////////////////////////////
+    // Remember latest release and where it ocurred
+    struct Key
+    {
+      Key()
+       : _inRpmKeys( nullptr )
+       , _inZyppKeys( nullptr )
+      {}
 
-  std::list<PublicKey> rpm_keys = pubkeys();
-  for_( it, zypp_keys.begin(), zypp_keys.end() )
+      void updateIf( const Edition & rpmKey_r )
+      {
+       std::string keyRelease( rpmKey_r.release() );
+       int comp = _release.compare( keyRelease );
+       if ( comp < 0 )
+       {
+         // update to newer release
+         _release.swap( keyRelease );
+         _inRpmKeys  = &rpmKey_r;
+         _inZyppKeys = nullptr;
+         if ( !keyRelease.empty() )
+           DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" <<  keyRelease << endl;
+       }
+       else if ( comp == 0 )
+       {
+         // stay with this release
+         if ( ! _inRpmKeys )
+           _inRpmKeys = &rpmKey_r;
+       }
+       // else: this is an old release
+       else
+         DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" <<  keyRelease << endl;
+      }
+
+      void updateIf( const PublicKeyData & zyppKey_r )
+      {
+       std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
+       int comp = _release.compare( keyRelease );
+       if ( comp < 0 )
+       {
+         // update to newer release
+         _release.swap( keyRelease );
+         _inRpmKeys  = nullptr;
+         _inZyppKeys = &zyppKey_r;
+         if ( !keyRelease.empty() )
+           DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
+       }
+       else if ( comp == 0 )
+       {
+         // stay with this release
+         if ( ! _inZyppKeys )
+           _inZyppKeys = &zyppKey_r;
+       }
+       // else: this is an old release
+       else
+         DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
+      }
+
+      std::string _release;
+      const Edition * _inRpmKeys;
+      const PublicKeyData * _inZyppKeys;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    // collect keys by ID(version) and latest creation(release)
+    std::map<std::string,Key> _keymap;
+
+    for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
     {
-      // we find only the left part of the long gpg key, as rpm does not support long ids
-      std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
-      if ( ik != rpm_keys.end() )
-        {
-          MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
-        }
-      else
-        {
-          // now import the key in rpm
-          try
-            {
-              importPubkey( *it );
-              MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
-            }
-          catch (RpmException &e)
-            {
-              ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
-            }
-        }
+      _keymap[(*it).version()].updateIf( *it );
     }
-}
-
-void RpmDb::exportTrustedKeysInZyppKeyRing()
-{
-  MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
-  librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
 
-  set<Edition>    rpm_keys( pubkeyEditions() );
-  list<PublicKey> zypp_keys( getZYpp()->keyRing()->trustedPublicKeys() );
+    for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
+    {
+      _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
+    }
 
-  // Temporarily disconnect to prevent the attemt to re-import the exported keys.
-  callback::TempConnect<KeyRingSignals> tempDisconnect;
+    // compute missing keys
+    std::set<Edition> rpmKeys;
+    std::list<PublicKeyData> zyppKeys;
+    for_( it, _keymap.begin(), _keymap.end() )
+    {
+      DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
+          << ( (*it).second._inRpmKeys  ? "R" : "_" )
+         << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
+      if ( ! (*it).second._inRpmKeys )
+      {
+       zyppKeys.push_back( *(*it).second._inZyppKeys );
+      }
+      if ( ! (*it).second._inZyppKeys )
+      {
+       rpmKeys.insert( *(*it).second._inRpmKeys );
+      }
+    }
+    rpmKeys_r.swap( rpmKeys );
+    zyppKeys_r.swap( zyppKeys );
+  }
+} // namespace
+///////////////////////////////////////////////////////////////////
 
-  TmpFile tmpfile( getZYpp()->tmpPath() );
-  {
-    ofstream tmpos( tmpfile.path().c_str() );
-    for_( it, rpm_keys.begin(), rpm_keys.end() )
+void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
+{
+  MIL << "Going to sync trusted keys..." << endl;
+  std::set<Edition> rpmKeys( pubkeyEditions() );
+  std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
+
+  if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
+  {
+    // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
+    // when re-acquiring the zyppp lock. For now we remove all excess keys.
+    // TODO: Once we can safely assume that all PK versions are updated we
+    // can think about re-importing newer key versions found in the zypp keyring and
+    // removing only excess ones (but case is not very likely). Unfixed PK versions
+    // however will remove the newer version found in the zypp keyring and by doing
+    // this, the key here will be removed via callback as well (keys are deleted
+    // via gpg id, regardless of the edition).
+    MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
+    // Temporarily disconnect to prevent the attempt to pass back the delete request.
+    callback::TempConnect<KeyRingSignals> tempDisconnect;
+    bool dirty = false;
+    for ( const PublicKeyData & keyData : zyppKeys )
     {
-      // search the zypp key into the rpm keys
-      // long id is edition version + release
-      string id = str::toUpper( (*it).version() + (*it).release());
-      list<PublicKey>::iterator ik( find( zypp_keys.begin(), zypp_keys.end(), id) );
-      if ( ik != zypp_keys.end() )
+      if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
       {
-       MIL << "Key " << (*it) << " is already in zypp database." << endl;
+       DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
+       getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
+       if ( !dirty ) dirty = true;
       }
-      else
+    }
+    if ( dirty )
+      zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
+  }
+
+  computeKeyRingSync( rpmKeys, zyppKeys );
+  MIL << (mode_r & SYNC_TO_KEYRING   ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
+  MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  if ( (mode_r & SYNC_TO_KEYRING) &&  ! rpmKeys.empty() )
+  {
+    // export to zypp keyring
+    MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
+    // Temporarily disconnect to prevent the attempt to re-import the exported keys.
+    callback::TempConnect<KeyRingSignals> tempDisconnect;
+    librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
+
+    TmpFile tmpfile( getZYpp()->tmpPath() );
+    {
+      std::ofstream tmpos( tmpfile.path().c_str() );
+      for_( it, rpmKeys.begin(), rpmKeys.end() )
       {
        // we export the rpm key into a file
-       RpmHeader::constPtr result( new RpmHeader() );
-       getData( string("gpg-pubkey"), *it, result );
-       MIL <<  "Will export trusted key " << (*it) << " to zypp keyring." << endl;
+       RpmHeader::constPtr result;
+       getData( "gpg-pubkey", *it, result );
        tmpos << result->tag_description() << endl;
       }
     }
+    try
+    {
+      getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
+      // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
+      // Modern rpm does not import those keys, but when migrating a pre SLE12 system
+      // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
+      std::set<Edition> missingKeys;
+      for ( const Edition & key : rpmKeys )
+      {
+       if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
+         continue;
+       ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
+       missingKeys.insert( key );
+      }
+      if ( ! missingKeys.empty() )
+        callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
+    }
+    catch ( const Exception & excpt )
+    {
+      ZYPP_CAUGHT( excpt );
+      ERR << "Could not import keys into zypp keyring: " << endl;
+    }
   }
-  try
-  {
-    getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
-  }
-  catch (Exception &e)
+
+  ///////////////////////////////////////////////////////////////////
+  if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
   {
-    ERR << "Could not import keys into in zypp keyring" << endl;
+    // import from zypp keyring
+    MIL << "Importing zypp trusted keyring" << std::endl;
+    for_( it, zyppKeys.begin(), zyppKeys.end() )
+    {
+      try
+      {
+       importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
+      }
+      catch ( const RpmException & exp )
+      {
+       ZYPP_CAUGHT( exp );
+      }
+    }
   }
+  MIL << "Trusted keys synced." << endl;
 }
 
+void RpmDb::importZyppKeyRingTrustedKeys()
+{ syncTrustedKeys( SYNC_FROM_KEYRING ); }
+
+void RpmDb::exportTrustedKeysInZyppKeyRing()
+{ syncTrustedKeys( SYNC_TO_KEYRING ); }
+
 ///////////////////////////////////////////////////////////////////
 //
 //
@@ -919,67 +1073,107 @@ void RpmDb::importPubkey( const PublicKey & pubkey_r )
 {
   FAILIFNOTINITIALIZED;
 
-  // check if the key is already in the rpm database and just
-  // return if it does.
-  set<Edition> rpm_keys = pubkeyEditions();
-  string keyshortid = pubkey_r.id().substr(8,8);
-  MIL << "Comparing '" << keyshortid << "' to: ";
-  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
-  {
-    string id = str::toUpper( (*it).version() );
-    MIL <<  ", '" << id << "'";
-    if ( id == keyshortid )
-    {
-        // they match id
-        // now check if timestamp is different
-        Date date = Date(str::strtonum<Date::ValueType>("0x" + (*it).release()));
-        if (  date == pubkey_r.created() )
-        {
+  // bnc#828672: On the fly key import in READONLY
+  if ( zypp_readonly_hack::IGotIt() )
+  {
+    WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
+    return;
+  }
 
-            MIL << endl << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
-            return;
-        }
-        else
-        {
-            MIL << endl << "Key " << pubkey_r << " has another version in keyring. ( " << date << " & " << pubkey_r.created() << ")" << endl;
+  // check if the key is already in the rpm database
+  Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
+  std::set<Edition> rpmKeys = pubkeyEditions();
+  bool hasOldkeys = false;
 
-        }
+  for_( it, rpmKeys.begin(), rpmKeys.end() )
+  {
+    // bsc#1008325: Keys using subkeys for signing don't get a higher release
+    // if new subkeys are added, because the primary key remains unchanged.
+    // For now always re-import keys with subkeys. Here we don't want to export the
+    // keys in the rpm database to check whether the subkeys are the same. The calling
+    // code should take care, we don't re-import the same kesy over and over again.
+    if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
+    {
+      MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
+      return;
+    }
+
+    if ( keyEd.version() != (*it).version() )
+      continue; // different key ID (version)
 
+    if ( keyEd.release() < (*it).release() )
+    {
+      MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
+      return;
+    }
+    else
+    {
+      hasOldkeys = true;
     }
   }
-  // key does not exists, lets import it
-  MIL <<  endl;
+  MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
 
+  if ( hasOldkeys )
+  {
+    // We must explicitly delete old key IDs first (all releases,
+    // that's why we don't call removePubkey here).
+    std::string keyName( "gpg-pubkey-" + keyEd.version() );
+    RpmArgVec opts;
+    opts.push_back ( "-e" );
+    opts.push_back ( "--allmatches" );
+    opts.push_back ( "--" );
+    opts.push_back ( keyName.c_str() );
+    // don't call modifyDatabase because it would remove the old
+    // rpm3 database, if the current database is a temporary one.
+    run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+    std::string line;
+    while ( systemReadLine( line ) )
+    {
+      ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
+    }
+
+    if ( systemStatus() != 0 )
+    {
+      ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
+    }
+    else
+    {
+      MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
+    }
+  }
+
+  // import the new key
   RpmArgVec opts;
   opts.push_back ( "--import" );
   opts.push_back ( "--" );
-  opts.push_back ( pubkey_r.path().asString().c_str() );
+  std::string pubkeypath( pubkey_r.path().asString() );
+  opts.push_back ( pubkeypath.c_str() );
 
   // don't call modifyDatabase because it would remove the old
   // rpm3 database, if the current database is a temporary one.
   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
-  string line;
+  std::string line;
+  std::vector<std::string> excplines;
   while ( systemReadLine( line ) )
   {
-    if ( line.substr( 0, 6 ) == "error:" )
+    if ( str::startsWith( line, "error:" ) )
     {
       WAR << line << endl;
+      excplines.push_back( std::move(line) );
     }
     else
-    {
       DBG << line << endl;
-    }
   }
 
-  int rpm_status = systemStatus();
-
-  if ( rpm_status != 0 )
+  if ( systemStatus() != 0 )
   {
-    //TranslatorExplanation first %s is file name, second is error message
-    ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
-        _("Failed to import public key from file %s: %s"))
-        % pubkey_r.asString() % error_message)));
+    // Translator: %1% is a gpg public key
+    RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
+    excp.moveToHistory( excplines );
+    excp.addHistory( std::move(error_message) );
+    ZYPP_THROW( std::move(excp) );
   }
   else
   {
@@ -999,17 +1193,13 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
 
   // check if the key is in the rpm database and just
   // return if it does not.
-  set<Edition> rpm_keys = pubkeyEditions();
+  std::set<Edition> rpm_keys = pubkeyEditions();
+  std::set<Edition>::const_iterator found_edition = rpm_keys.end();
+  std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
 
-  // search the key
-  set<Edition>::const_iterator found_edition = rpm_keys.end();
-
-  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
+  for_( it, rpm_keys.begin(), rpm_keys.end() )
   {
-    string id = str::toUpper( (*it).version() );
-    string keyshortid = pubkey_r.id().substr(8,8);
-    MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
-    if ( id == keyshortid )
+    if ( (*it).version() == pubkeyVersion )
     {
        found_edition = it;
        break;
@@ -1023,7 +1213,7 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
       return;
   }
 
-  string rpm_name("gpg-pubkey-" + found_edition->asString());
+  std::string rpm_name("gpg-pubkey-" + found_edition->asString());
 
   RpmArgVec opts;
   opts.push_back ( "-e" );
@@ -1034,27 +1224,26 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
   // rpm3 database, if the current database is a temporary one.
   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
-  string line;
+  std::string line;
+  std::vector<std::string> excplines;
   while ( systemReadLine( line ) )
   {
-    if ( line.substr( 0, 6 ) == "error:" )
+    if ( str::startsWith( line, "error:" ) )
     {
       WAR << line << endl;
+      excplines.push_back( std::move(line) );
     }
     else
-    {
       DBG << line << endl;
-    }
   }
 
-  int rpm_status = systemStatus();
-
-  if ( rpm_status != 0 )
+  if ( systemStatus() != 0 )
   {
-    //TranslatorExplanation first %s is key name, second is error message
-    ZYPP_THROW(RpmSubprocessException(boost::str(boost::format(
-        _("Failed to remove public key %s: %s")) % pubkey_r.asString()
-        % error_message)));
+    // Translator: %1% is a gpg public key
+    RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
+    excp.moveToHistory( excplines );
+    excp.addHistory( std::move(error_message) );
+    ZYPP_THROW( std::move(excp) );
   }
   else
   {
@@ -1066,23 +1255,23 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
 //
 //
 //     METHOD NAME : RpmDb::pubkeys
-//     METHOD TYPE : set<Edition>
+//     METHOD TYPE : std::set<Edition>
 //
-list<PublicKey> RpmDb::pubkeys() const
+std::list<PublicKey> RpmDb::pubkeys() const
 {
-  list<PublicKey> ret;
+  std::list<PublicKey> ret;
 
   librpmDb::db_const_iterator it;
-  for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
+  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
   {
     Edition edition = it->tag_edition();
     if (edition != Edition::noedition)
     {
       // we export the rpm key into a file
-      RpmHeader::constPtr result = new RpmHeader();
-      getData( string("gpg-pubkey"), edition, result );
+      RpmHeader::constPtr result;
+      getData( "gpg-pubkey", edition, result );
       TmpFile file(getZYpp()->tmpPath());
-      ofstream os;
+      std::ofstream os;
       try
       {
         os.open(file.path().asString().c_str());
@@ -1096,7 +1285,7 @@ list<PublicKey> RpmDb::pubkeys() const
         PublicKey key(file);
         ret.push_back(key);
       }
-      catch (exception &e)
+      catch ( std::exception & e )
       {
         ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
         // just ignore the key
@@ -1106,12 +1295,12 @@ list<PublicKey> RpmDb::pubkeys() const
   return ret;
 }
 
-set<Edition> RpmDb::pubkeyEditions() const
+std::set<Edition> RpmDb::pubkeyEditions() const
   {
-    set<Edition> ret;
+    std::set<Edition> ret;
 
     librpmDb::db_const_iterator it;
-    for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
+    for ( it.findByName( "gpg-pubkey" ); *it; ++it )
     {
       Edition edition = it->tag_edition();
       if (edition != Edition::noedition)
@@ -1129,10 +1318,10 @@ set<Edition> RpmDb::pubkeyEditions() const
 //
 //     DESCRIPTION :
 //
-list<FileInfo>
-RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
+std::list<FileInfo>
+RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
 {
-  list<FileInfo> result;
+  std::list<FileInfo> result;
 
   librpmDb::db_const_iterator it;
   bool found;
@@ -1159,7 +1348,7 @@ RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
+bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
 {
   librpmDb::db_const_iterator it;
   bool res;
@@ -1181,11 +1370,11 @@ bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
 //
 //
 //     METHOD NAME : RpmDb::whoOwnsFile
-//     METHOD TYPE : string
+//     METHOD TYPE : std::string
 //
 //     DESCRIPTION :
 //
-string RpmDb::whoOwnsFile( const string & file_r) const
+std::string RpmDb::whoOwnsFile( const std::string & file_r) const
 {
   librpmDb::db_const_iterator it;
   if (it.findByFile( file_r ))
@@ -1203,7 +1392,7 @@ string RpmDb::whoOwnsFile( const string & file_r) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasProvides( const string & tag_r ) const
+bool RpmDb::hasProvides( const std::string & tag_r ) const
 {
   librpmDb::db_const_iterator it;
   return it.findByProvides( tag_r );
@@ -1217,7 +1406,7 @@ bool RpmDb::hasProvides( const string & tag_r ) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasRequiredBy( const string & tag_r ) const
+bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
 {
   librpmDb::db_const_iterator it;
   return it.findByRequiredBy( tag_r );
@@ -1231,7 +1420,7 @@ bool RpmDb::hasRequiredBy( const string & tag_r ) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasConflicts( const string & tag_r ) const
+bool RpmDb::hasConflicts( const std::string & tag_r ) const
 {
   librpmDb::db_const_iterator it;
   return it.findByConflicts( tag_r );
@@ -1245,7 +1434,7 @@ bool RpmDb::hasConflicts( const string & tag_r ) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasPackage( const string & name_r ) const
+bool RpmDb::hasPackage( const std::string & name_r ) const
 {
   librpmDb::db_const_iterator it;
   return it.findPackage( name_r );
@@ -1259,7 +1448,7 @@ bool RpmDb::hasPackage( const string & name_r ) const
 //
 //     DESCRIPTION :
 //
-bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
+bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
 {
   librpmDb::db_const_iterator it;
   return it.findPackage( name_r, ed_r );
@@ -1273,7 +1462,7 @@ bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
 //
 //     DESCRIPTION :
 //
-void RpmDb::getData( const string & name_r,
+void RpmDb::getData( const std::string & name_r,
                      RpmHeader::constPtr & result_r ) const
 {
   librpmDb::db_const_iterator it;
@@ -1291,7 +1480,7 @@ void RpmDb::getData( const string & name_r,
 //
 //     DESCRIPTION :
 //
-void RpmDb::getData( const string & name_r, const Edition & ed_r,
+void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
                      RpmHeader::constPtr & result_r ) const
 {
   librpmDb::db_const_iterator it;
@@ -1302,65 +1491,174 @@ void RpmDb::getData( const string & name_r, const Edition & ed_r,
 }
 
 ///////////////////////////////////////////////////////////////////
-//
-//     METHOD NAME : RpmDb::checkPackage
-//     METHOD TYPE : RpmDb::checkPackageResult
-//
-RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
+namespace
 {
-  PathInfo file( path_r );
-  if ( ! file.isFile() )
+  struct RpmlogCapture : public std::string
   {
-    ERR << "Not a file: " << file << endl;
-    return CHK_ERROR;
-  }
+    RpmlogCapture()
+    { rpmlog()._cap = this; }
 
-  FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
-  if ( fd == 0 || ::Ferror(fd) )
-  {
-    ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
-    if ( fd )
-      ::Fclose( fd );
-    return CHK_ERROR;
-  }
+    ~RpmlogCapture()
+    { rpmlog()._cap = nullptr; }
 
-  rpmts ts = ::rpmtsCreate();
-  ::rpmtsSetRootDir( ts, root().asString().c_str() );
-  ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
-  int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
-  ts = rpmtsFree(ts);
+  private:
+    struct Rpmlog
+    {
+      Rpmlog()
+      : _cap( nullptr )
+      {
+       rpmlogSetCallback( rpmLogCB, this );
+       rpmSetVerbosity( RPMLOG_INFO );
+       _f = ::fopen( "/dev/null","w");
+       rpmlogSetFile( _f );
+      }
+
+      ~Rpmlog()
+      { if ( _f ) ::fclose( _f ); }
 
-  ::Fclose( fd );
+      static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
+      { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
+
+      int rpmLog( rpmlogRec rec_r )
+      {
+       if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
+       return RPMLOG_DEFAULT;
+      }
 
-  switch ( res )
+      FILE * _f;
+      std::string * _cap;
+    };
+
+    static Rpmlog & rpmlog()
+    { static Rpmlog _rpmlog; return _rpmlog; }
+  };
+
+  RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r,                        // rpm file to check
+                                              const Pathname & root_r,                 // target root
+                                              bool  requireGPGSig_r,                   // whether no gpg signature is to be reported
+                                              RpmDb::CheckPackageDetail & detail_r )   // detailed result
   {
-  case RPMRC_OK:
-    return CHK_OK;
-    break;
-  case RPMRC_NOTFOUND:
-    WAR << "Signature is unknown type. " << file << endl;
-    return CHK_NOTFOUND;
-    break;
-  case RPMRC_FAIL:
-    WAR << "Signature does not verify. " << file << endl;
-    return CHK_FAIL;
-    break;
-  case RPMRC_NOTTRUSTED:
-    WAR << "Signature is OK, but key is not trusted. " << file << endl;
-    return CHK_NOTTRUSTED;
-    break;
-  case RPMRC_NOKEY:
-    WAR << "Public key is unavailable. " << file << endl;
-    return CHK_NOKEY;
-    break;
+    PathInfo file( path_r );
+    if ( ! file.isFile() )
+    {
+      ERR << "Not a file: " << file << endl;
+      return RpmDb::CHK_ERROR;
+    }
+
+    FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+    if ( fd == 0 || ::Ferror(fd) )
+    {
+      ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+      if ( fd )
+       ::Fclose( fd );
+      return RpmDb::CHK_ERROR;
+    }
+    rpmts ts = ::rpmtsCreate();
+    ::rpmtsSetRootDir( ts, root_r.c_str() );
+    ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
+
+    rpmQVKArguments_s qva;
+    memset( &qva, 0, sizeof(rpmQVKArguments_s) );
+#ifdef HAVE_NO_RPMTSSETVFYFLAGS
+    // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
+    // and qva_flags is not used in signature checking at all.
+    qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
+#else
+    ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
+#endif
+    RpmlogCapture vresult;
+    LocaleGuard guard( LC_ALL, "C" );  // bsc#1076415: rpm log output is localized, but we need to parse it :(
+    int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
+    guard.restore();
+
+    ts = rpmtsFree(ts);
+    ::Fclose( fd );
+
+    // results per line...
+    //     Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
+    //     Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
+    //     V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
+    //     MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
+    //
+    // TODO: try to get SIG info from the header rather than parsing the output
+    std::vector<std::string> lines;
+    str::split( vresult, std::back_inserter(lines), "\n" );
+    unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+    for ( unsigned i = 1; i < lines.size(); ++i )
+    {
+      std::string & line( lines[i] );
+      RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
+      if ( line.find( ": OK" ) != std::string::npos )
+      {
+       lineres = RpmDb::CHK_OK;
+       if ( line.find( "Signature, key ID" ) == std::string::npos )
+         ++count[RpmDb::CHK_NOSIG];    // Valid but no gpg signature -> CHK_NOSIG
+      }
+      else if ( line.find( ": NOKEY" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOKEY; }
+      else if ( line.find( ": BAD" ) != std::string::npos )
+      { lineres = RpmDb::CHK_FAIL; }
+      else if ( line.find( ": UNKNOWN" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOTFOUND; }
+      else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOTTRUSTED; }
+
+      ++count[lineres];
+      detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
+    }
+
+    RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
+
+    if ( count[RpmDb::CHK_FAIL] )
+      ret = RpmDb::CHK_FAIL;
+
+    else if ( count[RpmDb::CHK_NOTFOUND] )
+      ret = RpmDb::CHK_NOTFOUND;
+
+    else if ( count[RpmDb::CHK_NOKEY] )
+      ret = RpmDb::CHK_NOKEY;
+
+    else if ( count[RpmDb::CHK_NOTTRUSTED] )
+      ret = RpmDb::CHK_NOTTRUSTED;
+
+    else if ( ret == RpmDb::CHK_OK )
+    {
+      if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG]  )
+      {
+       detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string("    ")+_("Package is not signed!") ) );
+       if ( requireGPGSig_r )
+         ret = RpmDb::CHK_NOSIG;
+      }
+    }
+
+    if ( ret != RpmDb::CHK_OK )
+    {
+      WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
+      WAR << vresult;
+    }
+    return ret;
   }
-  ERR << "Error reading header." << file << endl;
-  return CHK_ERROR;
-}
+
+} // namespace
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : RpmDb::checkPackage
+//     METHOD TYPE : RpmDb::CheckPackageResult
+//
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
+{ return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
+
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
+{ CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
+
+RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
+{ return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
+
 
 // determine changed files of installed package
 bool
-RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
+RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
 {
   bool ok = true;
 
@@ -1393,7 +1691,7 @@ RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
    M      Mode (includes permissions and file type)
   */
 
-  string line;
+  std::string line;
   while (systemReadLine(line))
   {
     if (line.length() > 12 &&
@@ -1401,7 +1699,7 @@ RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
          (line[0] == '.' && line[7] == 'T')))
     {
       // file has been changed
-      string filename;
+      std::string filename;
 
       filename.assign(line, 11, line.length() - 11);
       fileList.insert(filename);
@@ -1444,6 +1742,9 @@ RpmDb::run_rpm (const RpmArgVec& opts,
   RpmArgVec args;
 
   // always set root and dbpath
+#if defined(WORKAROUNDRPMPWDBUG)
+  args.push_back("#/");                // chdir to / to workaround bnc#819354
+#endif
   args.push_back("rpm");
   args.push_back("--root");
   args.push_back(_root.asString().c_str());
@@ -1469,7 +1770,7 @@ RpmDb::run_rpm (const RpmArgVec& opts,
 /*--------------------------------------------------------------*/
 /* Read a line from the rpm process                            */
 /*--------------------------------------------------------------*/
-bool RpmDb::systemReadLine( string & line )
+bool RpmDb::systemReadLine( std::string & line )
 {
   line.erase();
 
@@ -1518,7 +1819,7 @@ bool RpmDb::systemReadLine( string & line )
          {
            if ( linebuffer[nread-1] == '\n' )
              --nread;
-           line += string( linebuffer, nread );
+           line += std::string( linebuffer, nread );
          }
 
          if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
@@ -1573,19 +1874,19 @@ RpmDb::systemKill()
 
 
 // generate diff mails for config files
-void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
+void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
 {
-  string msg = line.substr(9);
-  string::size_type pos1 = string::npos;
-  string::size_type pos2 = string::npos;
-  string file1s, file2s;
+  std::string msg = line.substr(9);
+  std::string::size_type pos1 = std::string::npos;
+  std::string::size_type pos2 = std::string::npos;
+  std::string file1s, file2s;
   Pathname file1;
   Pathname file2;
 
   pos1 = msg.find (typemsg);
   for (;;)
   {
-    if ( pos1 == string::npos )
+    if ( pos1 == std::string::npos )
       break;
 
     pos2 = pos1 + strlen (typemsg);
@@ -1605,7 +1906,7 @@ void RpmDb::processConfigFiles(const string& line, const string& name, const cha
       file2 = _root + file2;
     }
 
-    string out;
+    std::string out;
     int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
     if (ret)
     {
@@ -1616,7 +1917,7 @@ void RpmDb::processConfigFiles(const string& line, const string& name, const cha
         break;
       }
       file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
-      ofstream notify(file.asString().c_str(), ios::out|ios::app);
+      std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
       if (!notify)
       {
         ERR << "Could not open " <<  file << endl;
@@ -1644,8 +1945,8 @@ void RpmDb::processConfigFiles(const string& line, const string& name, const cha
           {
             out.replace(4, file1.asString().length(), file1s);
           }
-          string::size_type pos = out.find("\n+++ ");
-          if (pos != string::npos)
+          std::string::size_type pos = out.find("\n+++ ");
+          if (pos != std::string::npos)
           {
             out.replace(pos+5, file2.asString().length(), file2s);
           }
@@ -1719,10 +2020,6 @@ void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, cal
     // FIXME status handling
     report->progress( 0 ); // allow 1% for backup creation.
   }
-  else
-  {
-    report->progress( 100 );
-  }
 
   // run rpm
   RpmArgVec opts;
@@ -1732,6 +2029,7 @@ void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, cal
     opts.push_back("-U");
 
   opts.push_back("--percent");
+  opts.push_back("--noglob");
 
   // ZConfig defines cross-arch installation
   if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
@@ -1755,49 +2053,50 @@ void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, cal
     opts.push_back ("--justdb");
   if (flags & RPMINST_TEST)
     opts.push_back ("--test");
+  if (flags & RPMINST_NOPOSTTRANS)
+    opts.push_back ("--noposttrans");
 
   opts.push_back("--");
 
   // rpm requires additional quoting of special chars:
-  string quotedFilename( rpmQuoteFilename( filename ) );
+  std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
   opts.push_back ( quotedFilename.c_str() );
 
   modifyDatabase(); // BEFORE run_rpm
   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
-  string line;
-  string rpmmsg;
-  vector<string> configwarnings;
+  std::string line;
+  std::string rpmmsg;                          // TODO: immediately forward lines via Callback::report rather than collecting
+  std::vector<std::string> configwarnings;     // TODO: immediately process lines rather than collecting
 
   unsigned linecnt = 0;
-  while (systemReadLine(line))
+  while ( systemReadLine( line ) )
   {
-    if ( linecnt < MAXRPMMESSAGELINES )
-      ++linecnt;
-    else
-      continue;
-
-    if (line.substr(0,2)=="%%")
+    if ( str::startsWith( line, "%%" ) )
     {
       int percent;
-      sscanf (line.c_str () + 2, "%d", &percent);
+      sscanf( line.c_str() + 2, "%d", &percent );
       report->progress( percent );
+      continue;
     }
-    else
-      rpmmsg += line+'\n';
 
-    if ( line.substr(0,8) == "warning:" )
-    {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else if ( line.find( " scriptlet failed, " ) == std::string::npos )        // always log %script errors
+      continue;
+
+    rpmmsg += line+'\n';
+
+    if ( str::startsWith( line, "warning:" ) )
       configwarnings.push_back(line);
-    }
   }
-  if ( linecnt > MAXRPMMESSAGELINES )
+  if ( linecnt >= MAXRPMMESSAGELINES )
     rpmmsg += "[truncated]\n";
 
   int rpm_status = systemStatus();
 
   // evaluate result
-  for (vector<string>::iterator it = configwarnings.begin();
+  for (std::vector<std::string>::iterator it = configwarnings.begin();
        it != configwarnings.end(); ++it)
   {
     processConfigFiles(*it, Pathname::basename(filename), " saved as ",
@@ -1817,19 +2116,18 @@ void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, cal
     historylog.comment(
         str::form("%s install failed", Pathname::basename(filename).c_str()),
         true /*timestamp*/);
-    ostringstream sstr;
+    std::ostringstream sstr;
     sstr << "rpm output:" << endl << rpmmsg << endl;
     historylog.comment(sstr.str());
     // TranslatorExplanation the colon is followed by an error message
-    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
-               (rpmmsg.empty() ? error_message : rpmmsg)));
+    ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
   }
   else if ( ! rpmmsg.empty() )
   {
     historylog.comment(
         str::form("%s installed ok", Pathname::basename(filename).c_str()),
         true /*timestamp*/);
-    ostringstream sstr;
+    std::ostringstream sstr;
     sstr << "Additional rpm output:" << endl << rpmmsg << endl;
     historylog.comment(sstr.str());
 
@@ -1860,7 +2158,7 @@ void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
 //     METHOD NAME : RpmDb::removePackage
 //     METHOD TYPE : PMError
 //
-void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
+void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
 {
   callback::SendReport<RpmRemoveReport> report;
 
@@ -1891,7 +2189,7 @@ void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
 }
 
 
-void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
+void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
 {
   FAILIFNOTINITIALIZED;
   HistoryLog historylog;
@@ -1938,8 +2236,8 @@ void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback
   modifyDatabase(); // BEFORE run_rpm
   run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
 
-  string line;
-  string rpmmsg;
+  std::string line;
+  std::string rpmmsg;          // TODO: immediately forward lines via Callback::report rather than collecting
 
   // got no progress from command, so we fake it:
   // 5  - command started
@@ -1951,11 +2249,11 @@ void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback
   {
     if ( linecnt < MAXRPMMESSAGELINES )
       ++linecnt;
-    else
+    else if ( line.find( " scriptlet failed, " ) == std::string::npos )        // always log %script errors
       continue;
     rpmmsg += line+'\n';
   }
-  if ( linecnt > MAXRPMMESSAGELINES )
+  if ( linecnt >= MAXRPMMESSAGELINES )
     rpmmsg += "[truncated]\n";
   report->progress( 50 );
   int rpm_status = systemStatus();
@@ -1964,19 +2262,18 @@ void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback
   {
     historylog.comment(
         str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
-    ostringstream sstr;
+    std::ostringstream sstr;
     sstr << "rpm output:" << endl << rpmmsg << endl;
     historylog.comment(sstr.str());
     // TranslatorExplanation the colon is followed by an error message
-    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ")) +
-               (rpmmsg.empty() ? error_message: rpmmsg)));
+    ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
   }
   else if ( ! rpmmsg.empty() )
   {
     historylog.comment(
         str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
 
-    ostringstream sstr;
+    std::ostringstream sstr;
     sstr << "Additional rpm output:" << endl << rpmmsg << endl;
     historylog.comment(sstr.str());
 
@@ -2007,7 +2304,7 @@ bool RpmDb::backupPackage( const Pathname & filename )
 //     METHOD NAME : RpmDb::backupPackage
 //     METHOD TYPE : bool
 //
-bool RpmDb::backupPackage(const string& packageName)
+bool RpmDb::backupPackage(const std::string& packageName)
 {
   HistoryLog progresslog;
   bool ret = true;
@@ -2065,7 +2362,7 @@ bool RpmDb::backupPackage(const string& packageName)
       return false;
     }
 
-    ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
+    std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
 
     if (!fp)
     {
@@ -2076,7 +2373,7 @@ bool RpmDb::backupPackage(const string& packageName)
     for (FileList::const_iterator cit = fileList.begin();
          cit != fileList.end(); ++cit)
     {
-      string name = *cit;
+      std::string name = *cit;
       if ( name[0] == '/' )
       {
         // remove slash, file must be relative to -C parameter of tar
@@ -2104,11 +2401,11 @@ bool RpmDb::backupPackage(const string& packageName)
     // execute tar in inst-sys (we dont know if there is a tar below _root !)
     ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
 
-    string tarmsg;
+    std::string tarmsg;
 
     // TODO: its probably possible to start tar with -v and watch it adding
     // files to report progress
-    for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
+    for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
     {
       tarmsg+=output;
     }
@@ -2139,6 +2436,37 @@ void RpmDb::setBackupPath(const Pathname& path)
   _backuppath = path;
 }
 
+std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
+{
+  switch ( obj )
+  {
+#define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_OK,              _("Signature is OK") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTFOUND,                _("Unknown type of signature") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_FAIL,            _("Signature does not verify") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTTRUSTED,      _("Signature is OK, but key is not trusted") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOKEY,           _("Signatures public key is not available") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_ERROR,           _("File does not exist or signature can't be checked") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOSIG,           _("File is unsigned") );
+#undef OUTS
+  }
+  return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
+}
+
+std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
+{
+  for ( const auto & el : obj )
+    str << el.second << endl;
+  return str;
+}
+
 } // namespace rpm
 } // namespace target
 } // namespace zypp