Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / target / rpm / RpmDb.cc
index 692e0ec..8b0cb55 100644 (file)
  *
 */
 #include "librpm.h"
-
+extern "C"
+{
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlog.h>
+}
 #include <cstdlib>
 #include <cstdio>
 #include <ctime>
 
 #include <iostream>
 #include <fstream>
+#include <sstream>
 #include <list>
 #include <map>
 #include <set>
@@ -26,7 +31,7 @@
 
 #include "zypp/base/Logger.h"
 #include "zypp/base/String.h"
-#include "zypp/base/Regex.h"
+#include "zypp/base/Gettext.h"
 
 #include "zypp/Date.h"
 #include "zypp/Pathname.h"
 #include "zypp/target/rpm/RpmDb.h"
 #include "zypp/target/rpm/RpmCallbacks.h"
 
-#include "zypp/target/CommitLog.h"
+#include "zypp/HistoryLog.h"
 #include "zypp/target/rpm/librpmDb.h"
 #include "zypp/target/rpm/RpmException.h"
 #include "zypp/TmpPath.h"
 #include "zypp/KeyRing.h"
 #include "zypp/ZYppFactory.h"
-
-#ifndef _
-#define _(X) X
-#endif
+#include "zypp/ZConfig.h"
 
 using namespace std;
 using namespace zypp::filesystem;
 
+#define WARNINGMAILPATH                "/var/log/YaST2/"
+#define FILEFORBACKUPFILES     "YaSTBackupModifiedFiles"
+#define MAXRPMMESSAGELINES     10000
+
+#define WORKAROUNDRPMPWDBUG
+
 namespace zypp
 {
+  namespace zypp_readonly_hack
+  {
+    bool IGotIt(); // in readonly-mode
+  }
 namespace target
 {
 namespace rpm
 {
 namespace
 {
-const char* quoteInFilename_m = " \t";
+#if 1 // No more need to escape whitespace since rpm-4.4.2.3
+const char* quoteInFilename_m = "\'\"";
+#else
+const char* quoteInFilename_m = " \t\'\"";
+#endif
 inline string rpmQuoteFilename( const Pathname & path_r )
 {
   string path( path_r.asString() );
@@ -71,6 +87,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>
@@ -189,83 +225,7 @@ ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
   return str;
 }
 
-///////////////////////////////////////////////////////////////////
-//     CLASS NAME : RpmDbPtr
-//     CLASS NAME : RpmDbconstPtr
-///////////////////////////////////////////////////////////////////
-
-#define WARNINGMAILPATH "/var/log/YaST2/"
-#define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
-
-///////////////////////////////////////////////////////////////////
-//
-//     CLASS NAME : RpmDb::Packages
-/**
- * Helper class for RpmDb::getPackages() to build the
- * list<Package::Ptr> returned. We have to assert, that there
- * is a unique entry for every string.
- *
- * In the first step we build the _list list which contains all
- * packages (even those which are contained in multiple versions).
- *
- * At the end buildIndex() is called to build the _index is created
- * and points to the last installed versions of all packages.
- * Operations changing the rpmdb
- * content (install/remove package) should set _valid to false. The
- * next call to RpmDb::getPackages() will then reread the the rpmdb.
- *
- * Note that outside RpmDb::getPackages() _list and _index are always
- * in sync. So you may use lookup(PkgName) to retrieve a specific
- * Package::Ptr.
- **/
-class RpmDb::Packages
-{
-public:
-  list<Package::Ptr>        _list;
-  map<string,Package::Ptr> _index;
-  bool                      _valid;
-  Packages() : _valid( false )
-  {}
-  void clear()
-  {
-    _list.clear();
-    _index.clear();
-    _valid = false;
-  }
-  Package::Ptr lookup( const string & name_r ) const
-  {
-    map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
-    if ( got != _index.end() )
-      return got->second;
-    return Package::Ptr();
-  }
-  void buildIndex()
-  {
-    _index.clear();
-    for ( list<Package::Ptr>::iterator iter = _list.begin();
-          iter != _list.end(); ++iter )
-    {
-      string name = (*iter)->name();
-      Package::Ptr & nptr = _index[name]; // be shure to get a reference!
-
-      if ( nptr )
-      {
-        WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
-        if ( nptr->installtime() > (*iter)->installtime() )
-          continue;
-        else
-          nptr = *iter;
-      }
-      else
-      {
-        nptr = *iter;
-      }
-    }
-    _valid = true;
-  }
-};
 
-///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -285,7 +245,6 @@ public:
 //
 RpmDb::RpmDb()
     : _dbStateInfo( DbSI_NO_INIT )
-    , _packages( * new Packages ) // delete in destructor
 #warning Check for obsolete memebers
     , _backuppath ("/var/adm/backup")
     , _packagebackups(false)
@@ -293,7 +252,7 @@ RpmDb::RpmDb()
 {
   process = 0;
   exit_code = -1;
-
+  librpmDb::globalInit();
   // Some rpm versions are patched not to abort installation if
   // symlink creation failed.
   setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
@@ -310,9 +269,7 @@ RpmDb::~RpmDb()
 {
   MIL << "~RpmDb()" << endl;
   closeDatabase();
-
   delete process;
-  delete &_packages;
   MIL  << "~RpmDb() end" << endl;
   sKeyRingReceiver.reset();
 }
@@ -371,11 +328,13 @@ ostream & RpmDb::dumpOn( ostream & str ) const
 //     METHOD NAME : RpmDb::initDatabase
 //     METHOD TYPE : PMError
 //
-void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
+void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
 {
   ///////////////////////////////////////////////////////////////////
   // Check arguments
   ///////////////////////////////////////////////////////////////////
+  bool quickinit( root_r.empty() );
+
   if ( root_r.empty() )
     root_r = "/";
 
@@ -388,7 +347,9 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
     ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
   }
 
-  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
+  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
+      << ( doRebuild_r ? " (rebuilddb)" : "" )
+      << ( quickinit ? " (quickinit)" : "" ) << endl;
 
   ///////////////////////////////////////////////////////////////////
   // Check whether already initialized
@@ -409,6 +370,13 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
   // init database
   ///////////////////////////////////////////////////////////////////
   librpmDb::unblockAccess();
+
+  if ( quickinit )
+  {
+    MIL << "QUICK initDatabase (no systemRoot set)" << endl;
+    return;
+  }
+
   DbStateInfoBits info = DbSI_NO_INIT;
   try
   {
@@ -451,22 +419,17 @@ void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
   _dbPath = dbPath_r;
   _dbStateInfo = info;
 
-#warning Add rebuild database once have the info about context
-#if 0
-  if ( ! ( Y2PM::runningFromSystem() ) )
+  if ( doRebuild_r )
   {
     if (      dbsi_has( info, DbSI_HAVE_V4 )
-              && ! dbsi_has( info, DbSI_MADE_V4 ) )
+         && ! dbsi_has( info, DbSI_MADE_V4 ) )
     {
-      err = rebuildDatabase();
+      rebuildDatabase();
     }
   }
-#endif
 
-  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
@@ -587,11 +550,6 @@ void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbP
     {
 
       WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
-#warning EXCEPTION: nonempty rpm4 and rpm3 database found.
-      //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
-      //report->start( dbInfo.dbV3().path() );
-      //report->stop( some error );
-
       // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
       dbsi_set( info_r, DbSI_MODIFIED_V4 );
 
@@ -760,9 +718,6 @@ void RpmDb::modifyDatabase()
     removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
     dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
   }
-
-  // invalidate Packages list
-  _packages._valid = false;
 }
 
 ///////////////////////////////////////////////////////////////////
@@ -783,7 +738,6 @@ void RpmDb::closeDatabase()
   ///////////////////////////////////////////////////////////////////
   // Block further database access
   ///////////////////////////////////////////////////////////////////
-  _packages.clear();
   librpmDb::blockAccess();
 
   ///////////////////////////////////////////////////////////////////
@@ -831,7 +785,7 @@ void RpmDb::rebuildDatabase()
   }
   catch (RpmException & excpt_r)
   {
-    report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
+    report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
     ZYPP_RETHROW(excpt_r);
   }
   report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
@@ -854,8 +808,6 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
 
   // don't call modifyDatabase because it would remove the old
   // rpm3 database, if the current database is a temporary one.
-  // But do invalidate packages list.
-  _packages._valid = false;
   run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
 
   // progress report: watch this file growing
@@ -872,7 +824,12 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
     if ( newMaster() )
     { // file is removed at the end of rebuild.
       // current size should be upper limit for new db
-      report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath());
+      if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
+      {
+        WAR << "User requested abort." << endl;
+        systemKill();
+        filesystem::recursive_rmdir( newMaster.path().dirname() );
+      }
     }
 
     if ( line.compare( 0, 2, "D:" ) )
@@ -887,7 +844,9 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
 
   if ( rpm_status != 0 )
   {
-    ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
+    //TranslatorExplanation after semicolon is error message
+    ZYPP_THROW(RpmSubprocessException(string(_("RPM failed: ") +
+               (errmsg.empty() ? error_message: errmsg))));
   }
   else
   {
@@ -895,96 +854,179 @@ void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
   }
 }
 
-void RpmDb::importZyppKeyRingTrustedKeys()
+///////////////////////////////////////////////////////////////////
+namespace
 {
-  MIL << "Importing zypp trusted keyring" << std::endl;
+  /** \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();
+      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 R: 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 Z: 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::list<PublicKey> zypp_keys;
+      std::string _release;
+      const Edition * _inRpmKeys;
+      const PublicKeyData * _inZyppKeys;
+    };
+    ///////////////////////////////////////////////////////////////////
 
-  zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
+    // collect keys by ID(version) and latest creation(release)
+    std::map<std::string,Key> _keymap;
 
-  for ( std::list<PublicKey>::const_iterator it = zypp_keys.begin(); it != zypp_keys.end(); ++it)
+    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).path());
-              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;
-
-  set<Edition> rpm_keys = pubkeyEditions();
-
-  list<PublicKey> zypp_keys;
-  zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
-
-  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
-  {
-    // 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() )
+    for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
     {
-      MIL << "Key " << (*it) << " is already in zypp database." << endl;
+      _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
     }
-    else
+
+    // compute missing keys
+    std::set<Edition> rpmKeys;
+    std::list<PublicKeyData> zyppKeys;
+    for_( it, _keymap.begin(), _keymap.end() )
     {
-      // we export the rpm key into a file
-      RpmHeader::constPtr result = new RpmHeader();
-      getData( string("gpg-pubkey"), *it, result );
-      TmpFile file(getZYpp()->tmpPath());
-      ofstream os;
-      try
+      DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
+          << ( (*it).second._inRpmKeys  ? "R" : "_" )
+         << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
+      if ( ! (*it).second._inRpmKeys )
       {
-        os.open(file.path().asString().c_str());
-        // dump rpm key into the tmp file
-        os << result->tag_description();
-        //MIL << "-----------------------------------------------" << endl;
-        //MIL << result->tag_description() <<endl;
-        //MIL << "-----------------------------------------------" << endl;
-        os.close();
+       zyppKeys.push_back( *(*it).second._inZyppKeys );
       }
-      catch (exception &e)
+      if ( ! (*it).second._inZyppKeys )
       {
-        ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << endl;
-        // just ignore the key
+       rpmKeys.insert( *(*it).second._inRpmKeys );
       }
+    }
+    rpmKeys_r.swap( rpmKeys );
+    zyppKeys_r.swap( zyppKeys );
+  }
+} // namespace
+///////////////////////////////////////////////////////////////////
+
+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() );
+  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 attemt to re-import the exported keys.
+    callback::TempConnect<KeyRingSignals> tempDisconnect;
+    librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
+
+    TmpFile tmpfile( getZYpp()->tmpPath() );
+    {
+      ofstream tmpos( tmpfile.path().c_str() );
+      for_( it, rpmKeys.begin(), rpmKeys.end() )
+      {
+       // we export the rpm key into a file
+       RpmHeader::constPtr result;
+       getData( string("gpg-pubkey"), *it, result );
+       tmpos << result->tag_description() << endl;
+      }
+    }
+    try
+    {
+      getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
+    }
+    catch (Exception &e)
+    {
+      ERR << "Could not import keys into in zypp keyring" << endl;
+    }
+  }
 
-      // now import the key in zypp
+  ///////////////////////////////////////////////////////////////////
+  if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
+  {
+    // import from zypp keyring
+    MIL << "Importing zypp trusted keyring" << std::endl;
+    for_( it, zyppKeys.begin(), zyppKeys.end() )
+    {
       try
       {
-        getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
-        MIL << "Trusted key " << (*it) << " imported in zypp keyring." << endl;
+       importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
       }
-      catch (Exception &e)
+      catch ( const RpmException & exp )
       {
-        ERR << "Could not import key " << (*it) << " in zypp keyring" << endl;
+       ZYPP_CAUGHT( exp );
       }
     }
   }
+  MIL << "Trusted keys synced." << endl;
 }
 
+void RpmDb::importZyppKeyRingTrustedKeys()
+{ syncTrustedKeys( SYNC_FROM_KEYRING ); }
+
+void RpmDb::exportTrustedKeysInZyppKeyRing()
+{ syncTrustedKeys( SYNC_TO_KEYRING ); }
+
 ///////////////////////////////////////////////////////////////////
 //
 //
@@ -995,24 +1037,72 @@ 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();
-  for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
+  // 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;
+  }
+
+  // check if the key is already in the rpm database
+  Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
+  set<Edition> rpmKeys = pubkeyEditions();
+  bool hasOldkeys = false;
+
+  for_( it, rpmKeys.begin(), rpmKeys.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 ( keyEd == *it ) // quick test (Edition is IdStringType!)
     {
-      // they match id
-      // FIXME id is not sufficient?
-      MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
+      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 << "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 );
+
+    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 ( "--" );
@@ -1020,28 +1110,20 @@ void RpmDb::importPubkey( const PublicKey & pubkey_r )
 
   // don't call modifyDatabase because it would remove the old
   // rpm3 database, if the current database is a temporary one.
-  // But do invalidate packages list.
-  _packages._valid = false;
   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
   string line;
   while ( systemReadLine( line ) )
   {
-    if ( line.substr( 0, 6 ) == "error:" )
-    {
-      WAR << line << endl;
-    }
-    else
-    {
-      DBG << line << endl;
-    }
+    ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
   }
 
-  int rpm_status = systemStatus();
-
-  if ( rpm_status != 0 )
+  if ( systemStatus() != 0 )
   {
-    ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
+    //TranslatorExplanation first %s is file name, second is error message
+    ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to import public key from file %s: %s"))
+                                      % pubkey_r.asString()
+                                      % error_message ));
   }
   else
   {
@@ -1062,16 +1144,12 @@ 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();
-
-  // search the key
   set<Edition>::const_iterator found_edition = rpm_keys.end();
+  std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
 
-  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;
@@ -1094,8 +1172,6 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
 
   // don't call modifyDatabase because it would remove the old
   // rpm3 database, if the current database is a temporary one.
-  // But do invalidate packages list.
-  _packages._valid = false;
   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
   string line;
@@ -1115,7 +1191,10 @@ void RpmDb::removePubkey( const PublicKey & pubkey_r )
 
   if ( rpm_status != 0 )
   {
-    ZYPP_THROW(RpmSubprocessException(string("Failed to remove public key ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
+    //TranslatorExplanation first %s is key name, second is error message
+    ZYPP_THROW(RpmSubprocessException( str::Format(_("Failed to remove public key %s: %s"))
+                                      % pubkey_r.asString()
+                                      % error_message ));
   }
   else
   {
@@ -1140,7 +1219,7 @@ list<PublicKey> RpmDb::pubkeys() const
     if (edition != Edition::noedition)
     {
       // we export the rpm key into a file
-      RpmHeader::constPtr result = new RpmHeader();
+      RpmHeader::constPtr result;
       getData( string("gpg-pubkey"), edition, result );
       TmpFile file(getZYpp()->tmpPath());
       ofstream os;
@@ -1154,7 +1233,7 @@ list<PublicKey> RpmDb::pubkeys() const
         //MIL << "-----------------------------------------------" << endl;
         os.close();
         // read the public key from the dumped file
-        PublicKey key(file.path());
+        PublicKey key(file);
         ret.push_back(key);
       }
       catch (exception &e)
@@ -1181,238 +1260,6 @@ set<Edition> RpmDb::pubkeyEditions() const
     return ret;
   }
 
-///////////////////////////////////////////////////////////////////
-//
-//
-//     METHOD NAME : RpmDb::packagesValid
-//     METHOD TYPE : bool
-//
-bool RpmDb::packagesValid() const
-{
-  return( _packages._valid || ! initialized() );
-}
-
-///////////////////////////////////////////////////////////////////
-//
-//
-//     METHOD NAME : RpmDb::getPackages
-//     METHOD TYPE : const list<Package::Ptr> &
-//
-//     DESCRIPTION :
-//
-const list<Package::Ptr> & RpmDb::getPackages()
-{
-  callback::SendReport<ScanDBReport> report;
-
-  report->start ();
-
-  try
-  {
-    const list<Package::Ptr> & ret = doGetPackages(report);
-    report->finish(ScanDBReport::NO_ERROR, "");
-    return ret;
-  }
-  catch (RpmException & excpt_r)
-  {
-    report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
-    ZYPP_RETHROW(excpt_r);
-  }
-#warning fixme
-  static const list<Package::Ptr> empty_list;
-  return empty_list;
-}
-
-#warning FIX READING RPM DATBASE TO POOL
-#if 0 // obsolete helper
-inline static void insertCaps( Capabilities &capset, capability::CapabilityImplPtrSet ptrset, CapFactory &factory )
-{
-  for ( capability::CapabilityImplPtrSet::const_iterator it = ptrset.begin();
-        it != ptrset.end();
-        ++it )
-  {
-    capset.insert( factory.fromImpl(*it) );
-  }
-}
-#endif
-
-//
-// make Package::Ptr from RpmHeader
-// return NULL on error
-//
-Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header,
-                                           set<string> * filerequires,
-                                           const Pathname & location, Repository repo )
-{
-  if ( ! header )
-    return 0;
-
-  if ( header->isSrc() )
-  {
-    WAR << "Can't make Package from SourcePackage header" << endl;
-    return 0;
-  }
-
-  Package::Ptr pptr;
-#warning FIX READING RPM DATBASE TO POOL
-#if 0
-  string name = header->tag_name();
-
-  // create dataprovider
-  detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
-
-  impl->setRepository( repo );
-  if (!location.empty())
-    impl->setLocation( OnMediaLocation(location,1) );
-
-  Edition edition;
-  try
-  {
-    edition = Edition( header->tag_version(),
-                       header->tag_release(),
-                       header->tag_epoch());
-  }
-  catch (Exception & excpt_r)
-  {
-    ZYPP_CAUGHT( excpt_r );
-    WAR << "Package " << name << " has bad edition '"
-    << (header->tag_epoch()==0?"":(header->tag_epoch()+":"))
-    << header->tag_version()
-    << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
-    return pptr;
-  }
-
-  Arch arch;
-  try
-  {
-    arch = Arch( header->tag_arch() );
-  }
-  catch (Exception & excpt_r)
-  {
-    ZYPP_CAUGHT( excpt_r );
-    WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
-    return pptr;
-  }
-
-  // Collect basic Resolvable data
-  NVRAD dataCollect( header->tag_name(),
-                     edition,
-                     arch );
-
-  list<string> filenames = impl->filenames();
-  CapFactory capfactory;
-  insertCaps( dataCollect[Dep::PROVIDES], header->tag_provides( filerequires ), capfactory );
-
-  for (list<string>::const_iterator filename = filenames.begin();
-       filename != filenames.end();
-       ++filename)
-  {
-    if ( capability::isInterestingFileSpec( *filename ) )
-    {
-      try
-      {
-        dataCollect[Dep::PROVIDES].insert(capfactory.fromImpl(capability::buildFile(ResTraits<Package>::kind, *filename) ));
-      }
-      catch (Exception & excpt_r)
-      {
-        ZYPP_CAUGHT( excpt_r );
-        WAR << "Ignoring invalid capability: " << *filename << endl;
-      }
-    }
-  }
-
-  insertCaps( dataCollect[Dep::REQUIRES], header->tag_requires( filerequires ), capfactory );
-  insertCaps( dataCollect[Dep::PREREQUIRES], header->tag_prerequires( filerequires ), capfactory );
-  insertCaps( dataCollect[Dep::CONFLICTS], header->tag_conflicts( filerequires ), capfactory );
-  insertCaps( dataCollect[Dep::OBSOLETES], header->tag_obsoletes( filerequires ), capfactory );
-  insertCaps( dataCollect[Dep::ENHANCES], header->tag_enhances( filerequires ), capfactory );
-  insertCaps( dataCollect[Dep::SUPPLEMENTS], header->tag_supplements( filerequires ), capfactory );
-
-  try
-  {
-    // create package from dataprovider
-    pptr = detail::makeResolvableFromImpl( dataCollect, impl );
-  }
-  catch (Exception & excpt_r)
-  {
-    ZYPP_CAUGHT( excpt_r );
-    ERR << "Can't create Package::Ptr" << endl;
-  }
-#endif
-  return pptr;
-}
-
-const list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
-{
-  if ( packagesValid() )
-  {
-    return _packages._list;
-  }
-
-  _packages.clear();
-
-  ///////////////////////////////////////////////////////////////////
-  // Collect package data.
-  ///////////////////////////////////////////////////////////////////
-  unsigned expect = 0;
-  librpmDb::constPtr dbptr;
-  librpmDb::dbAccess( dbptr );
-  expect = dbptr->size();
-  DBG << "Expecting " << expect << " packages" << endl;
-
-  librpmDb::db_const_iterator iter;
-  unsigned current = 0;
-  Pathname location;
-
-  for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
-  {
-
-    string name = iter->tag_name();
-    if ( name == string( "gpg-pubkey" ) )
-    {
-      DBG << "Ignoring pseudo package " << name << endl;
-      // pseudo package filtered, as we can't handle multiple instances
-      // of 'gpg-pubkey-VERS-REL'.
-      continue;
-    }
-
-    Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Repository() );
-    if ( ! pptr )
-    {
-      WAR << "Failed to make package from database header '" << name << "'" << endl;
-      continue;
-    }
-
-    _packages._list.push_back( pptr );
-  }
-  _packages.buildIndex();
-  DBG << "Found installed packages: " << _packages._list.size() << endl;
-
-#warning FILEREQUIRES HACK SHOULD BE DONE WHEN WRITING THE RPMDB SOLV FILE
-#if 0
-  ///////////////////////////////////////////////////////////////////
-  // Evaluate filerequires collected so far
-  ///////////////////////////////////////////////////////////////////
-  for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
-    {
-
-      for ( iter.findByFile( *it ); *iter; ++iter )
-      {
-        Package::Ptr pptr = _packages.lookup( iter->tag_name() );
-        if ( !pptr )
-        {
-          WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
-          continue;
-        }
-        pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
-      }
-    }
-#endif
-
-  ///////////////////////////////////////////////////////////////////
-  // Build final packages list
-  ///////////////////////////////////////////////////////////////////
-  return _packages._list;
-}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1595,11 +1442,56 @@ void RpmDb::getData( const string & name_r, const Edition & ed_r,
 }
 
 ///////////////////////////////////////////////////////////////////
+namespace
+{
+  struct RpmlogCapture : public std::string
+  {
+    RpmlogCapture()
+    { rpmlog()._cap = this; }
+
+    ~RpmlogCapture()
+    { rpmlog()._cap = nullptr; }
+
+  private:
+    struct Rpmlog
+    {
+      Rpmlog()
+      : _cap( nullptr )
+      {
+       rpmlogSetCallback( rpmLogCB, this );
+       rpmSetVerbosity( RPMLOG_INFO );
+       _f = ::fopen( "/dev/null","w");
+       rpmlogSetFile( _f );
+      }
+
+      ~Rpmlog()
+      { if ( _f ) ::fclose( _f ); }
+
+      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;
+      }
+
+      FILE * _f;
+      std::string * _cap;
+    };
+
+    static Rpmlog & rpmlog()
+    { static Rpmlog _rpmlog; return _rpmlog; }
+  };
+
+
+} // namespace
+///////////////////////////////////////////////////////////////////
 //
 //     METHOD NAME : RpmDb::checkPackage
-//     METHOD TYPE : RpmDb::checkPackageResult
+//     METHOD TYPE : RpmDb::CheckPackageResult
 //
-RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
 {
   PathInfo file( path_r );
   if ( ! file.isFile() )
@@ -1616,41 +1508,72 @@ RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
       ::Fclose( fd );
     return CHK_ERROR;
   }
-
   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);
 
+  rpmQVKArguments_s qva;
+  memset( &qva, 0, sizeof(rpmQVKArguments_s) );
+  qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
+
+  RpmlogCapture vresult;
+  int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
+
+  ts = rpmtsFree(ts);
   ::Fclose( fd );
 
-  switch ( res )
+
+  if ( res == 0 )
   {
-  case RPMRC_OK:
+    detail_r.push_back( CheckPackageDetail::value_type( CHK_OK, std::move(vresult) ) );
     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;
   }
-  ERR << "Error reading header." << file << endl;
-  return CHK_ERROR;
+
+  // results per line...
+  WAR << vresult;
+  std::vector<std::string> lines;
+  str::split( vresult, std::back_inserter(lines), "\n" );
+  unsigned count[6] = { 0, 0, 0, 0, 0, 0 };
+
+  for ( unsigned i = 1; i < lines.size(); ++i )
+  {
+    std::string & line( lines[i] );
+    CheckPackageResult lineres = CHK_ERROR;
+    if ( line.find( ": OK" ) != std::string::npos )
+    { lineres = CHK_OK; }
+    else if ( line.find( ": NOKEY" ) != std::string::npos )
+    { lineres = CHK_NOKEY; }
+    else if ( line.find( ": BAD" ) != std::string::npos )
+    { lineres = CHK_FAIL; }
+    else if ( line.find( ": UNKNOWN" ) != std::string::npos )
+    { lineres = CHK_NOTFOUND; }
+    else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
+    { lineres = CHK_NOTTRUSTED; }
+
+    ++count[lineres];
+    detail_r.push_back( CheckPackageDetail::value_type( lineres, std::move(line) ) );
+  }
+
+  CheckPackageResult ret = CHK_ERROR;
+  if ( count[CHK_FAIL] )
+    ret = CHK_FAIL;
+
+  else if ( count[CHK_NOTFOUND] )
+    ret = CHK_NOTFOUND;
+
+  else if ( count[CHK_NOKEY] )
+    ret = CHK_NOKEY;
+
+  else if ( count[CHK_NOTTRUSTED] )
+    ret = CHK_NOTTRUSTED;
+
+  return ret;
 }
 
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
+{ CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
+
+
 // determine changed files of installed package
 bool
 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
@@ -1737,6 +1660,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());
@@ -1762,23 +1688,73 @@ RpmDb::run_rpm (const RpmArgVec& opts,
 /*--------------------------------------------------------------*/
 /* Read a line from the rpm process                            */
 /*--------------------------------------------------------------*/
-bool
-RpmDb::systemReadLine(string &line)
+bool RpmDb::systemReadLine( string & line )
 {
   line.erase();
 
   if ( process == NULL )
     return false;
 
-  line = process->receiveLine();
+  if ( process->inputFile() )
+  {
+    process->setBlocking( false );
+    FILE * inputfile = process->inputFile();
+    int    inputfileFd = ::fileno( inputfile );
+    do
+    {
+      /* Watch inputFile to see when it has input. */
+      fd_set rfds;
+      FD_ZERO( &rfds );
+      FD_SET( inputfileFd, &rfds );
+
+      /* Wait up to 5 seconds. */
+      struct timeval tv;
+      tv.tv_sec = 5;
+      tv.tv_usec = 0;
 
-  if (line.length() == 0)
-    return false;
+      int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
 
-  if (line[line.length() - 1] == '\n')
-    line.erase(line.length() - 1);
+      if ( retval == -1 )
+      {
+       ERR << "select error: " << strerror(errno) << endl;
+       if ( errno != EINTR )
+         return false;
+      }
+      else if ( retval )
+      {
+       // Data is available now.
+       static size_t linebuffer_size = 0;      // static because getline allocs
+       static char * linebuffer = 0;           // and reallocs if buffer is too small
+       ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
+       if ( nread == -1 )
+       {
+         if ( ::feof( inputfile ) )
+           return line.size(); // in case of pending output
+       }
+       else
+       {
+         if ( nread > 0 )
+         {
+           if ( linebuffer[nread-1] == '\n' )
+             --nread;
+           line += string( linebuffer, nread );
+         }
+
+         if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
+           return true; // complete line
+       }
+       clearerr( inputfile );
+      }
+      else
+      {
+       // No data within time.
+       if ( ! process->running() )
+         return false;
+      }
+    } while ( true );
+  }
 
-  return true;
+  return false;
 }
 
 /*--------------------------------------------------------------*/
@@ -1792,6 +1768,10 @@ RpmDb::systemStatus()
     return -1;
 
   exit_code = process->close();
+  if (exit_code == 0)
+    error_message = "";
+  else
+    error_message = process->execError();
   process->kill();
   delete process;
   process = 0;
@@ -1909,7 +1889,7 @@ void RpmDb::processConfigFiles(const string& line, const string& name, const cha
 //     METHOD NAME : RpmDb::installPackage
 //     METHOD TYPE : PMError
 //
-void RpmDb::installPackage( const Pathname & filename, unsigned flags )
+void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
 {
   callback::SendReport<RpmInstallReport> report;
 
@@ -1939,10 +1919,10 @@ void RpmDb::installPackage( const Pathname & filename, unsigned flags )
   while (true);
 }
 
-void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
+void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
 {
   FAILIFNOTINITIALIZED;
-  CommitLog progresslog;
+  HistoryLog historylog;
 
   MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
 
@@ -1958,10 +1938,6 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
     // FIXME status handling
     report->progress( 0 ); // allow 1% for backup creation.
   }
-  else
-  {
-    report->progress( 100 );
-  }
 
   // run rpm
   RpmArgVec opts;
@@ -1969,13 +1945,19 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
     opts.push_back("-i");
   else
     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() ) )
+    opts.push_back("--ignorearch");
 
   if (flags & RPMINST_NODIGEST)
     opts.push_back("--nodigest");
   if (flags & RPMINST_NOSIGNATURE)
     opts.push_back("--nosignature");
-  if (flags & RPMINST_NODOCS)
+  if (flags & RPMINST_EXCLUDEDOCS)
     opts.push_back ("--excludedocs");
   if (flags & RPMINST_NOSCRIPTS)
     opts.push_back ("--noscripts");
@@ -1989,11 +1971,13 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
     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 ) );
+  string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
   opts.push_back ( quotedFilename.c_str() );
 
   modifyDatabase(); // BEFORE run_rpm
@@ -2002,10 +1986,15 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
   string line;
   string rpmmsg;
   vector<string> configwarnings;
-  vector<string> errorlines;
 
+  unsigned linecnt = 0;
   while (systemReadLine(line))
   {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else
+      continue;
+
     if (line.substr(0,2)=="%%")
     {
       int percent;
@@ -2020,6 +2009,9 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
       configwarnings.push_back(line);
     }
   }
+  if ( linecnt > MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
+
   int rpm_status = systemStatus();
 
   // evaluate result
@@ -2040,19 +2032,28 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
 
   if ( rpm_status != 0 )
   {
-    // %s = filename of rpm package
-    progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
-    progresslog() << _("rpm output:") << endl << rpmmsg << endl;
-    ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
-  }
-  else
-  {
-    // %s = filename of rpm package
-    progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
-    if ( ! rpmmsg.empty() )
-    {
-      progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
-    }
+    historylog.comment(
+        str::form("%s install failed", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    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)));
+  }
+  else if ( ! rpmmsg.empty() )
+  {
+    historylog.comment(
+        str::form("%s installed ok", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
   }
 }
 
@@ -2062,10 +2063,12 @@ void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callbac
 //     METHOD NAME : RpmDb::removePackage
 //     METHOD TYPE : PMError
 //
-void RpmDb::removePackage( Package::constPtr package, unsigned flags )
+void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
 {
+  // 'rpm -e' does not like epochs
   return removePackage( package->name()
-                        + "-" + package->edition().asString()
+                        + "-" + package->edition().version()
+                        + "-" + package->edition().release()
                         + "." + package->arch().asString(), flags );
 }
 
@@ -2075,29 +2078,41 @@ void RpmDb::removePackage( Package::constPtr package, unsigned flags )
 //     METHOD NAME : RpmDb::removePackage
 //     METHOD TYPE : PMError
 //
-void RpmDb::removePackage( const string & name_r, unsigned flags )
+void RpmDb::removePackage( const string & name_r, RpmInstFlags flags )
 {
   callback::SendReport<RpmRemoveReport> report;
 
   report->start( name_r );
 
-  try
-  {
-    doRemovePackage(name_r, flags, report);
-  }
-  catch (RpmException & excpt_r)
-  {
-    report->finish(excpt_r);
-    ZYPP_RETHROW(excpt_r);
-  }
-  report->finish();
+  do
+    try
+    {
+      doRemovePackage(name_r, flags, report);
+      report->finish();
+      break;
+    }
+    catch (RpmException & excpt_r)
+    {
+      RpmRemoveReport::Action user = report->problem( excpt_r );
+
+      if ( user == RpmRemoveReport::ABORT )
+      {
+        report->finish( excpt_r );
+        ZYPP_RETHROW(excpt_r);
+      }
+      else if ( user == RpmRemoveReport::IGNORE )
+      {
+        break;
+      }
+    }
+  while (true);
 }
 
 
-void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
+void RpmDb::doRemovePackage( const string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
 {
   FAILIFNOTINITIALIZED;
-  CommitLog progresslog;
+  HistoryLog historylog;
 
   MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
 
@@ -2149,27 +2164,43 @@ void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::Se
   // 50 - command completed
   // 100 if no error
   report->progress( 5 );
+  unsigned linecnt = 0;
   while (systemReadLine(line))
   {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else
+      continue;
     rpmmsg += line+'\n';
   }
+  if ( linecnt > MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
   report->progress( 50 );
   int rpm_status = systemStatus();
 
   if ( rpm_status != 0 )
   {
-    // %s = name of rpm package
-    progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
-    progresslog() << _("rpm output:") << endl << rpmmsg << endl;
-    ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
+    historylog.comment(
+        str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
+    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)));
   }
-  else
+  else if ( ! rpmmsg.empty() )
   {
-    progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
-    if ( ! rpmmsg.empty() )
-    {
-      progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
-    }
+    historylog.comment(
+        str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
+
+    ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
   }
 }
 
@@ -2196,7 +2227,7 @@ bool RpmDb::backupPackage( const Pathname & filename )
 //
 bool RpmDb::backupPackage(const string& packageName)
 {
-  CommitLog progresslog;
+  HistoryLog progresslog;
   bool ret = true;
   Pathname backupFilename;
   Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
@@ -2310,7 +2341,9 @@ bool RpmDb::backupPackage(const string& packageName)
     else
     {
       MIL << "tar backup ok" << endl;
-      progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
+      progresslog.comment(
+          str::form(_("created backup %s"), backupFilename.asString().c_str())
+          , /*timestamp*/true);
     }
 
     filesystem::unlink(filestobackupfile);
@@ -2324,6 +2357,35 @@ 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") );
+#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