Imported Upstream version 14.45.10 upstream/14.45.10
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 2 Sep 2019 07:08:30 +0000 (16:08 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 2 Sep 2019 07:08:30 +0000 (16:08 +0900)
25 files changed:
VERSION.cmake
doc/autoinclude/FeatureTest.doc
libzypp.spec.cmake
package/libzypp.changes
po/sle-zypp-po.tar.bz2
po/zypp-po.tar.bz2
zypp/CMakeLists.txt
zypp/HistoryLog.cc
zypp/KeyRing.cc
zypp/PluginFrame.cc
zypp/PublicKey.cc
zypp/PublicKey.h
zypp/RepoManager.cc
zypp/base/Iterable.h [new file with mode: 0644]
zypp/base/LocaleGuard.h [new file with mode: 0644]
zypp/base/Xml.h
zypp/media/MediaCurl.cc
zypp/media/MediaHandler.cc
zypp/media/MediaISO.cc
zypp/media/MediaISO.h
zypp/media/TransferSettings.cc
zypp/media/UrlResolverPlugin.cc
zypp/misc/CheckAccessDeleted.cc
zypp/solver/detail/Testcase.cc
zypp/target/rpm/RpmDb.cc

index 29e768b..e045fed 100644 (file)
@@ -61,8 +61,8 @@
 SET(LIBZYPP_MAJOR "14")
 SET(LIBZYPP_COMPATMINOR "39")
 SET(LIBZYPP_MINOR "45")
-SET(LIBZYPP_PATCH "0")
+SET(LIBZYPP_PATCH "10")
 #
-# LAST RELEASED: 14.45.0 (39)
+# LAST RELEASED: 14.45.10 (39)
 # (The number in parenthesis is LIBZYPP_COMPATMINOR)
 #=======
index 3a34b32..29d0431 100644 (file)
@@ -25,6 +25,8 @@ Packages requiring a feature may use the corresponding \c Requires: in their .sp
   <DD><DL>
     <DT>version 0</DT>
     <DD>General ability to provide and handle plugins.</DD>
+    <DT>version 0.1</DT>
+    <DD>Bugfix: don't reject header values containing a ':'.</DD>
   </DL></DD>
 
   <DT>plugin:commit</DT>
index 62fddb6..498c826 100644 (file)
@@ -32,7 +32,7 @@ Provides:       yast2-packagemanager
 Obsoletes:      yast2-packagemanager
 
 # Features we provide (update doc/autoinclude/FeatureTest.doc):
-Provides:       libzypp(plugin) = 0
+Provides:       libzypp(plugin) = 0.1
 Provides:       libzypp(plugin:appdata) = 0
 Provides:       libzypp(plugin:commit) = 1
 Provides:       libzypp(plugin:services) = 1
index 62cbc4e..a0aad07 100644 (file)
@@ -1,4 +1,77 @@
 -------------------------------------------------------------------
+Tue Feb 20 19:01:49 CET 2018 - ma@suse.de
+
+- Adapt loop mounting of iso images (bsc#1038132, bsc#1033236)
+- version 14.45.10 (39)
+
+-------------------------------------------------------------------
+Tue Feb 20 18:43:33 CET 2018 - ma@suse.de
+
+- RpmDb::checkPackage: fix parsing localized rpm output (bsc#1076415)
+- version 14.45.9 (39)
+
+-------------------------------------------------------------------
+Thu Jan  4 17:51:56 CET 2018 - ma@suse.de
+
+- plugin: don't reject header values containing a ':' (bsc#1074687)
+- version 14.45.8 (39)
+
+-------------------------------------------------------------------
+Thu Nov 23 12:43:57 CET 2017 - ma@suse.de
+
+- Don't filter procs with a different mnt namespace (bsc#1068708)
+- version 14.45.7 (39)
+
+-------------------------------------------------------------------
+Mon Sep  4 11:43:22 CEST 2017 - ma@suse.de
+
+- verifyFileSignature: Support signing with subkeys (bsc#1008325)
+- Fix gpg-pubkey relase (creation time) computation (bsc#1036659)
+- More specific exception message if GPG binary is missing (bsc#637791)
+- version 14.45.6 (39)
+
+-------------------------------------------------------------------
+Fri Jun  9 11:25:00 CEST 2017 - ma@suse.de
+
+- Fix potential crash if repo has no baseurl (bnc#1043218)
+- version 14.45.5 (39)
+
+-------------------------------------------------------------------
+Wed May 31 12:47:52 CEST 2017 - ma@suse.de
+
+- Testcase: add missing solver flags (bsc#1041889)
+- version 14.45.4 (39)
+
+-------------------------------------------------------------------
+Mon Mar 27 09:36:53 CEST 2017 - ma@suse.de
+
+- Fix X-libcurl-Empty-Header-Workaround (bsc#1030919)
+- version 14.45.3 (39)
+
+-------------------------------------------------------------------
+Tue Mar 21 13:40:30 CET 2017 - ma@suse.de
+
+- MediaCurl: Treat http response 410(Gone) like 404(Not Found)
+  (bsc#1030136)
+- version 14.45.2 (39)
+
+-------------------------------------------------------------------
+Tue Feb 14 12:10:58 CET 2017 - ma@suse.de
+
+- dumpAsXmlOnL: xml escape node content (bsc#1024909)
+- version 14.45.1 (39)
+
+-------------------------------------------------------------------
+Thu Dec 22 01:14:09 CET 2016 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Dec 22 01:13:35 CET 2016 - ma@suse.de
+
+- Update sle-zypp-po.tar.bz2
+
+-------------------------------------------------------------------
 Fri Dec 16 11:27:36 CET 2016 - ma@suse.de
 
 - Merge branch 'bsc#985390' for zypper
index 34bab44..c6945a8 100644 (file)
Binary files a/po/sle-zypp-po.tar.bz2 and b/po/sle-zypp-po.tar.bz2 differ
index ee23cae..3a346e9 100644 (file)
Binary files a/po/zypp-po.tar.bz2 and b/po/zypp-po.tar.bz2 differ
index ec39705..8f27c4c 100644 (file)
@@ -247,8 +247,10 @@ SET( zypp_base_HEADERS
   base/GzStream.h
   base/IOStream.h
   base/InputStream.h
+  base/Iterable.h
   base/Iterator.h
   base/Json.h
+  base/LocaleGuard.h
   base/LogControl.h
   base/LogTools.h
   base/Logger.h
index 5f1a8fd..99d670f 100644 (file)
@@ -248,7 +248,7 @@ namespace zypp
       << timestamp()                                                   // 1 timestamp
       << _sep << HistoryActionID::REPO_ADD.asString(true)              // 2 action
       << _sep << str::escape(repo.alias(), _sep)                       // 3 alias
-      << _sep << *repo.baseUrlsBegin()                                 // 4 primary URL
+      << _sep << str::escape(repo.url().asString(), _sep)              // 4 primary URL
       << _sep << str::escape(ZConfig::instance().userData(), _sep)     // 5 userdata
       << endl;
   }
@@ -278,13 +278,13 @@ namespace zypp
         << _sep << str::escape(ZConfig::instance().userData(), _sep)   // 5 userdata
         << endl;
     }
-    if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
+    if ( oldrepo.url() != newrepo.url() )
     {
       _log
         << timestamp()                                                 // 1 timestamp
         << _sep << HistoryActionID::REPO_CHANGE_URL.asString(true)     // 2 action
-        << _sep << str::escape(oldrepo.alias(), _sep)                  // 3 old url
-        << _sep << *newrepo.baseUrlsBegin()                            // 4 new url
+        << _sep << str::escape(oldrepo.url().asString(), _sep)         // 3 old url
+        << _sep << str::escape(newrepo.url().asString(), _sep)         // 4 new url
         << _sep << str::escape(ZConfig::instance().userData(), _sep)   // 5 userdata
         << endl;
     }
index e0167b5..d8ce3fe 100644 (file)
@@ -310,15 +310,16 @@ namespace zypp
   PublicKeyData KeyRing::Impl::publicKeyExists( const std::string & id, const Pathname & keyring )
   {
     MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
-    const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
-    for_( it, keys.begin(), keys.end() )
+    PublicKeyData ret;
+    for ( const PublicKeyData & key : publicKeyData( keyring ) )
     {
-      if ( id == (*it).id() )
+      if ( key.providesKey( id ) )
       {
-        return *it;
+       ret = key;
+       break;
       }
     }
-    return PublicKeyData();
+    return ret;
   }
 
   PublicKey KeyRing::Impl::exportKey( const PublicKeyData & keyData, const Pathname & keyring )
@@ -389,10 +390,10 @@ namespace zypp
       return res;
     }
 
-    // get the id of the signature
+    // get the id of the signature (it might be a subkey id!)
     std::string id = readSignatureKeyId( signature );
 
-    // doeskey exists in trusted keyring
+    // does key exists in trusted keyring
     PublicKeyData trustedKeyData( publicKeyExists( id, trustedKeyRing() ) );
     if ( trustedKeyData )
     {
@@ -405,6 +406,12 @@ namespace zypp
       {
         // bnc #393160: Comment #30: Compare at least the fingerprint
         // in case an attacker created a key the the same id.
+       //
+       // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
+       // to compare the subkey sets, to tell whether a key was updated.
+       // because created() remains unchanged if the primary key is not touched.
+       // For now we wait until a new subkey signs the data and treat it as a
+       //  new key (else part below).
         if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
           && trustedKeyData.created() < generalKeyData.created() )
         {
index 6e4e4ad..6397825 100644 (file)
@@ -116,7 +116,7 @@ namespace zypp
       {
        if ( key_r.find_first_of( ":\n" ) != std::string::npos )
          ZYPP_THROW( PluginFrameException( "Illegal char in header key", key_r ) );
-       if ( value_r.find_first_of( ":\n" ) != std::string::npos )
+       if ( value_r.find_first_of( "\n" ) != std::string::npos )
          ZYPP_THROW( PluginFrameException( "Illegal char in header value", value_r ) );
        return HeaderList::value_type( key_r, value_r );
       }
index fc7435b..c942cbf 100644 (file)
@@ -35,7 +35,134 @@ using std::endl;
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
+  ///////////////////////////////////////////////////////////////////
+  namespace
+  {
+    inline bool isExpired( const Date & expires_r )
+    { return( expires_r && expires_r < Date::now() ); }
+
+    inline int hasDaysToLive( const Date & expires_r )
+    {
+      if ( expires_r )
+      {
+       Date exp( expires_r - Date::now() );
+       int ret = exp / Date::day;
+       if ( exp < 0 ) ret -= 1;
+       return ret;
+      }
+      return INT_MAX;
+    }
+
+    inline std::string expiresDetail( const Date & expires_r )
+    {
+      str::Str str;
+      if ( ! expires_r )
+      {
+       // translators: an annotation to a gpg keys expiry date
+       str << _("does not expire");
+      }
+      else if ( isExpired( expires_r ) )
+      {
+       // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
+       str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
+      }
+      else
+      {
+       // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
+       str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
+      }
+      return str;
+    }
+
+    inline std::string expiresDetailVerbose( const Date & expires_r )
+    {
+      if ( !expires_r )
+      { // translators: an annotation to a gpg keys expiry date
+       return _("(does not expire)");
+      }
+      std::string ret( expires_r.asString() );
+      int ttl( hasDaysToLive( expires_r ) );
+      if ( ttl <= 90 )
+      {
+       ret += " ";
+       if ( ttl < 0 )
+       { // translators: an annotation to a gpg keys expiry date
+         ret += _("(EXPIRED)");
+       }
+       else if ( ttl == 0 )
+       { // translators: an annotation to a gpg keys expiry date
+         ret += _("(expires within 24h)");
+       }
+       else
+       { // translators: an annotation to a gpg keys expiry date
+         ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
+       }
+      }
+      return ret;
+    }
+
+  } //namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class PublicSubkeyData::Impl
+  /// \brief  PublicSubkeyData implementation.
+  ///////////////////////////////////////////////////////////////////
+  struct PublicSubkeyData::Impl
+  {
+    std::string _id;
+    Date        _created;
+    Date        _expires;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
+    {
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// class PublicSubkeyData
+  ///////////////////////////////////////////////////////////////////
+
+  PublicSubkeyData::PublicSubkeyData()
+    : _pimpl( Impl::nullimpl() )
+  {}
+
+  PublicSubkeyData::~PublicSubkeyData()
+  {}
+
+  PublicSubkeyData::operator bool() const
+  { return !_pimpl->_id.empty(); }
+
+  std::string PublicSubkeyData::id() const
+  { return _pimpl->_id; }
+
+  Date PublicSubkeyData::created() const
+  { return _pimpl->_created; }
+
+  Date PublicSubkeyData::expires() const
+  { return _pimpl->_expires; }
+
+  bool PublicSubkeyData::expired() const
+  { return isExpired( _pimpl->_expires ); }
+
+  int PublicSubkeyData::daysToLive() const
+  { return hasDaysToLive( _pimpl->_expires ); }
+
+  std::string PublicSubkeyData::asString() const
+  {
+    return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
+  }
 
   ///////////////////////////////////////////////////////////////////
   /// \class PublicKeyData::Impl
@@ -49,6 +176,23 @@ namespace zypp
     Date        _created;
     Date        _expires;
 
+    std::vector<PublicSubkeyData> _subkeys;
+
+  public:
+    bool hasSubkeyId( const std::string & id_r ) const
+    {
+      bool ret = false;
+      for ( const PublicSubkeyData & sub : _subkeys )
+      {
+       if ( sub.id() == id_r )
+       {
+         ret = true;
+         break;
+       }
+      }
+      return ret;
+    }
+
   public:
     /** Offer default Impl. */
     static shared_ptr<Impl> nullimpl()
@@ -63,7 +207,6 @@ namespace zypp
     Impl * clone() const
     { return new Impl( *this ); }
   };
-  ///////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
   /// class PublicKeyData
@@ -95,46 +238,13 @@ namespace zypp
   { return _pimpl->_expires; }
 
   bool PublicKeyData::expired() const
-  { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
+  { return isExpired( _pimpl->_expires ); }
 
   int PublicKeyData::daysToLive() const
-  {
-    if ( _pimpl->_expires )
-    {
-      Date exp( _pimpl->_expires - Date::now() );
-      int ret = exp / Date::day;
-      if ( exp < 0 ) ret -= 1;
-      return ret;
-    }
-    return INT_MAX;
-  }
+  { return hasDaysToLive( _pimpl->_expires ); }
 
   std::string PublicKeyData::expiresAsString() const
-  {
-    if ( !_pimpl->_expires )
-    { // translators: an annotation to a gpg keys expiry date
-      return _("(does not expire)");
-    }
-    std::string ret( _pimpl->_expires.asString() );
-    int ttl( daysToLive() );
-    if ( ttl <= 90 )
-    {
-      ret += " ";
-      if ( ttl < 0 )
-      { // translators: an annotation to a gpg keys expiry date
-       ret += _("(EXPIRED)");
-      }
-      else if ( ttl == 0 )
-      { // translators: an annotation to a gpg keys expiry date
-       ret += _("(expires within 24h)");
-      }
-      else
-      { // translators: an annotation to a gpg keys expiry date
-       ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
-      }
-    }
-    return ret;
-  }
+  { return expiresDetailVerbose( _pimpl->_expires ); }
 
   std::string PublicKeyData::gpgPubkeyVersion() const
   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
@@ -144,14 +254,22 @@ namespace zypp
 
   std::string PublicKeyData::asString() const
   {
-    return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
-                     _pimpl->_id.c_str(),
-                     gpgPubkeyRelease().c_str(),
-                     _pimpl->_name.c_str(),
-                     _pimpl->_fingerprint.c_str(),
-                     daysToLive() );
+    str::Str str;
+    str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
+    for ( auto && sub : _pimpl->_subkeys )
+      str << ", " << sub.id();
+    return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
   }
 
+  bool PublicKeyData::hasSubkeys() const
+  { return !_pimpl->_subkeys.empty(); }
+
+  Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
+  { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
+
+  bool PublicKeyData::providesKey( const std::string & id_r ) const
+  { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
+
   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
   {
     str << "[" << obj.name() << "]" << endl;
@@ -160,8 +278,9 @@ namespace zypp
     str << "  cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
     str << "  exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
     str << "  ttl " << obj.daysToLive() << endl;
+    for ( auto && sub : obj._pimpl->_subkeys )
+      str << "  sub " << sub << endl;
     str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
-    str << "]";
     return str;
   }
 
@@ -175,13 +294,13 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
   struct PublicKeyScanner::Impl
   {
-    std::vector<std::string>                   _words;
-    enum { pNONE, pPUB, pSIG, pFPR, pUID }     _parseEntry;
-    bool                                       _parseOff;      // no 'sub:' key parsing
+    enum { pNONE, pPUB, pSIG, pFPR, pUID, pSUB } _parseEntry;
+    std::vector<std::string> _words;
+    PublicKeyData::Impl * _keyDataPtr;
 
    Impl()
       : _parseEntry( pNONE )
-      , _parseOff( false )
+      , _keyDataPtr( nullptr )
     {}
 
     void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
@@ -205,7 +324,8 @@ namespace zypp
          if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
          {
            _parseEntry = pPUB;
-           _parseOff = false;
+           keys_r.push_back( PublicKeyData() );        // reset upon new key
+           _keyDataPtr = keys_r.back()._pimpl.get();
          }
          break;
 
@@ -223,54 +343,62 @@ namespace zypp
          if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
            _parseEntry = pSIG;
          else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
-           _parseOff = true;
+           _parseEntry = pSUB;
          break;
 
        default:
          return;
       }
-      if ( _parseOff || _parseEntry == pNONE )
+      if ( _parseEntry == pNONE )
        return;
+      if ( ! ( _keyDataPtr->_subkeys.empty() || _parseEntry == pSUB ) )
+       return; // collecting subkeys only
 
       if ( line_r[line_r.size()-1] == '\n' )
        line_r.erase( line_r.size()-1 );
-      // DBG << line_r << endl;
+      //DBG << line_r << endl;
 
       _words.clear();
       str::splitFields( line_r, std::back_inserter(_words), ":" );
 
-      PublicKeyData * key( &keys_r.back() );
-
       switch ( _parseEntry )
       {
        case pPUB:
-         keys_r.push_back( PublicKeyData() );  // reset upon new key
-         key = &keys_r.back();
-         key->_pimpl->_id      = _words[4];
-         key->_pimpl->_name    = str::replaceAll( _words[9], "\\x3a", ":" );
-         key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
-         key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
+         _keyDataPtr->_id      = _words[4];
+         _keyDataPtr->_name    = str::replaceAll( _words[9], "\\x3a", ":" );
+         _keyDataPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
+         _keyDataPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
          break;
 
        case pSIG:
          // Update creation/modification date from signatures type "13x".
-         if ( ( _words.size() > 10 && _words[10] == "13x" )
-           || ( _words.size() > 12 && _words[12] == "13x" ) )
+         if ( ( _words.size() > 10 && _words[10] == "13x" && !_words[9].empty() && _words[9] != "[User ID not found]" )
+           || ( _words.size() > 12 && _words[12] == "13x" /* [selfsig] */) )
          {
            Date cdate(str::strtonum<Date::ValueType>(_words[5]));
-           if ( key->_pimpl->_created < cdate )
-             key->_pimpl->_created = cdate;
+           if ( _keyDataPtr->_created < cdate )
+             _keyDataPtr->_created = cdate;
          }
          break;
 
        case pFPR:
-         if ( key->_pimpl->_fingerprint.empty() )
-           key->_pimpl->_fingerprint = _words[9];
+         if ( _keyDataPtr->_fingerprint.empty() )
+           _keyDataPtr->_fingerprint = _words[9];
          break;
 
        case pUID:
-         if ( ! _words[9].empty() )
-           key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+         if ( ! _words[9].empty() && _words[9] != "[User ID not found]" )
+           _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+         break;
+
+       case pSUB:
+         _keyDataPtr->_subkeys.push_back( PublicSubkeyData() );
+         {
+           PublicSubkeyData::Impl * subPtr = _keyDataPtr->_subkeys.back()._pimpl.get();
+           subPtr->_id      = _words[4];
+           subPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
+           subPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
+         }
          break;
 
        case pNONE:
@@ -375,12 +503,15 @@ namespace zypp
         {
          scanner.scan( line );
        }
-        prog.close();
+        int ret = prog.close();
 
        switch ( scanner._keys.size() )
        {
          case 0:
-           ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
+           if ( ret == 129 )
+             ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
+           else
+             ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
            break;
 
          case 1:
index 9ba6bad..e1c3c38 100644 (file)
@@ -18,6 +18,7 @@
 #include <set>
 #include <string>
 
+#include "zypp/base/Iterable.h"
 #include "zypp/base/PtrTypes.h"
 #include "zypp/base/Exception.h"
 #include "zypp/Pathname.h"
@@ -31,6 +32,7 @@ namespace zypp
   {
     class TmpFile;
   }
+  class PublicKeyData;
 
   ///////////////////////////////////////////////////////////////////
   /// \class BadKeyException
@@ -63,6 +65,62 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
+  /// \class PublicSubkeyData
+  /// \brief Class representing a GPG Public Keys subkeys.
+  /// \see \ref PublicKeyData.
+  ///////////////////////////////////////////////////////////////////
+  class PublicSubkeyData
+  {
+  public:
+    /** Default constructed: empty data. */
+    PublicSubkeyData();
+
+    ~PublicSubkeyData();
+
+    /** Whether this contains valid data (not default constructed). */
+    explicit operator bool() const;
+
+  public:
+    /** Subkey ID. */
+    std::string id() const;
+
+    /** Creation date. */
+    Date created() const;
+
+    /** Expiry date, or \c Date() if the key never expires. */
+    Date expires() const;
+
+    /**  Whether the key has expired. */
+    bool expired() const;
+
+    /** Number of days (24h) until the key expires (or since it exired).
+     * A value of \c 0 means the key will expire within the next 24h.
+     * Negative values indicate the key has expired less than \c N days ago.
+     * For keys without expiration date \c INT_MAX is returned.
+     */
+    int daysToLive() const;
+
+    /** Simple string representation.
+     * Encodes \ref id, \ref created and \ref expires
+     * \code
+     * 640DB551 2016-04-12 [expires: 2019-04-12]
+     * \endcode
+     */
+    std::string asString() const;
+
+  private:
+    class Impl;
+    RWCOW_pointer<Impl> _pimpl;
+    friend class PublicKeyScanner;
+    friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PublicSubkeyData Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PublicSubkeyData & obj )
+  { return str << obj.asString(); }
+
+  ///////////////////////////////////////////////////////////////////
   /// \class PublicKeyData
   /// \brief Class representing one GPG Public Keys data.
   /// \ref PublicKeyData are provided e.g. by a \ref PublicKey or
@@ -79,9 +137,6 @@ namespace zypp
 
     ~PublicKeyData();
 
-    /** Scan data from 'gpg --with-colons' key listings. */
-    friend class PublicKeyScanner;
-
     /** Whether this contains valid data (not default constructed). */
     explicit operator bool() const;
 
@@ -139,9 +194,23 @@ namespace zypp
      */
     std::string asString() const;
 
+  public:
+    typedef const PublicSubkeyData * SubkeyIterator;
+
+    /** Whether \ref subkeys is not empty. */
+    bool hasSubkeys() const;
+
+    /** Iterate any subkeys. */
+    Iterable<SubkeyIterator> subkeys() const;
+
+    /** Whether \a id_r is the \ref id of the primary key or of a subkey. */
+    bool providesKey( const std::string & id_r ) const;
+
   private:
     class Impl;
     RWCOW_pointer<Impl> _pimpl;
+    friend class PublicKeyScanner;
+    friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
   };
   ///////////////////////////////////////////////////////////////////
 
@@ -239,6 +308,8 @@ namespace zypp
     /** The public keys data (\see \ref PublicKeyData).*/
     const PublicKeyData & keyData() const;
 
+    typedef PublicKeyData::SubkeyIterator SubkeyIterator;
+
     bool isValid() const
     { return ! ( id().empty() || fingerprint().empty() ); }
 
@@ -254,6 +325,15 @@ namespace zypp
     std::string gpgPubkeyRelease() const;      //!< \see \ref PublicKeyData
     std::string asString() const;              //!< \see \ref PublicKeyData
 
+    bool hasSubkeys() const                    ///!< \see \ref PublicKeyData
+    { return keyData().hasSubkeys(); }
+
+    Iterable<SubkeyIterator> subkeys() const   ///!< \see \ref PublicKeyData
+    { return keyData().subkeys(); }
+
+    bool providesKey( const std::string & id_r ) const ///!< \see \ref PublicKeyData
+    { return keyData().providesKey( id_r ); }
+
   public:
     /** File containig the ASCII armored key. */
     Pathname path() const;
index 3853444..7490044 100644 (file)
@@ -1336,7 +1336,7 @@ namespace zypp
 
         if ( repokind == RepoType::RPMPLAINDIR )
         {
-          forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) );
+          forPlainDirs.reset( new MediaMounter( info.url() ) );
           // recusive for plaindir as 2nd arg!
           cmd.push_back( "-R" );
           // FIXME this does only work form dir: URLs
@@ -1617,16 +1617,13 @@ namespace zypp
     if ( _options.probe )
     {
       DBG << "unknown repository type, probing" << endl;
+      assert_urls(tosave);
 
-      RepoType probedtype;
-      probedtype = probe( *tosave.baseUrlsBegin(), info.path() );
-      if ( tosave.baseUrlsSize() > 0 )
-      {
-        if ( probedtype == RepoType::NONE )
-          ZYPP_THROW(RepoUnknownTypeException(info));
-        else
-          tosave.setType(probedtype);
-      }
+      RepoType probedtype( probe( tosave.url(), info.path() ) );
+      if ( probedtype == RepoType::NONE )
+       ZYPP_THROW(RepoUnknownTypeException(info));
+      else
+       tosave.setType(probedtype);
     }
 
     progress.set(50);
diff --git a/zypp/base/Iterable.h b/zypp/base/Iterable.h
new file mode 100644 (file)
index 0000000..8bf5533
--- /dev/null
@@ -0,0 +1,97 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Iterable.h
+ */
+#ifndef ZYPP_BASE_ITERABLE_H
+#define ZYPP_BASE_ITERABLE_H
+
+#include <iterator>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /// \class Iterable
+  /// \brief
+  /// \code
+  ///   struct Foo
+  ///   {
+  ///     class Iterator;
+  ///     typedef Iterable<Iterator> IterableType;
+  ///
+  ///     Iterator myBegin();
+  ///     Iterator myEnd();
+  ///
+  ///     IterableType iterate() { return makeIterable( myBegin(), myEnd() ); }
+  ///   };
+  /// \endcode
+  ///////////////////////////////////////////////////////////////////
+  template <class TIterator>
+  class Iterable
+  {
+  public:
+    typedef size_t size_type;
+    typedef TIterator iterator_type;
+    typedef typename std::iterator_traits<iterator_type>::value_type           value_type;
+    typedef typename std::iterator_traits<iterator_type>::difference_type      difference_type;
+    typedef typename std::iterator_traits<iterator_type>::pointer              pointer;
+    typedef typename std::iterator_traits<iterator_type>::reference            reference;
+    typedef typename std::iterator_traits<iterator_type>::iterator_category    iterator_category;
+
+    /** Ctor taking the iterator pair */
+    Iterable()
+    {}
+
+    /** Ctor taking the iterator pair */
+    Iterable( iterator_type begin_r, iterator_type end_r )
+    : _begin( std::move(begin_r) )
+    , _end( std::move(end_r) )
+    {}
+
+    /** Ctor taking the iterator pair */
+    Iterable( std::pair<iterator_type,iterator_type> range_r )
+    : _begin( std::move(range_r.first) )
+    , _end( std::move(range_r.second) )
+    {}
+
+    iterator_type begin() const
+    { return _begin; }
+
+    iterator_type end() const
+    { return _end; }
+
+    bool empty() const
+    { return( _begin == _end ); }
+
+    size_type size() const
+    { size_type ret = 0; for ( iterator_type i = _begin; i != _end; ++i ) ++ret; return ret; }
+
+    bool contains( const value_type & val_r ) const
+    { return( find( val_r ) != _end ); }
+
+    iterator_type find( const value_type & val_r ) const
+    { iterator_type ret = _begin; for ( ; ret != _end; ++ret ) if ( *ret == val_r ) break; return ret; }
+
+  private:
+    iterator_type _begin;
+    iterator_type _end;
+  };
+
+  /** \relates Iterable convenient construction. */
+  template <class TIterator>
+  Iterable<TIterator> makeIterable( TIterator && begin_r, TIterator && end_r )
+  { return Iterable<TIterator>( std::forward<TIterator>(begin_r), std::forward<TIterator>(end_r) ); }
+
+  /** \relates Iterable convenient construction. */
+  template <class TIterator>
+  Iterable<TIterator> makeIterable( std::pair<TIterator,TIterator> && range_r )
+  { return Iterable<TIterator>( std::forward<std::pair<TIterator,TIterator>>(range_r) ); }
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_ITERABLE_H
diff --git a/zypp/base/LocaleGuard.h b/zypp/base/LocaleGuard.h
new file mode 100644 (file)
index 0000000..79fc072
--- /dev/null
@@ -0,0 +1,66 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/LocaleGuard.h
+ */
+#ifndef ZYPP_BASE_LOCALEGUARD_H
+#define ZYPP_BASE_LOCALEGUARD_H
+
+#include <locale.h>
+#include <string>
+
+#include "zypp/base/Easy.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  /// \class LocaleGuard
+  /// \brief Temorarily change a locale category value
+  /// \ingroup g_RAII
+  ///////////////////////////////////////////////////////////////////
+  class LocaleGuard
+  {
+    NON_COPYABLE(LocaleGuard);
+    NON_MOVABLE(LocaleGuard);
+
+  public:
+    /** Ctor saving the current locale category value. */
+    LocaleGuard( int category_r, const std::string & value_r = "C" )
+    : _category( -1 )
+    {
+      const char * ovalue = ::setlocale( category_r, nullptr );
+      if ( ovalue && ovalue != value_r )
+      {
+       _category = category_r;
+       _value    = ovalue;
+       ::setlocale( _category, value_r.c_str() );
+      }
+    }
+
+    /** Dtor asserts the saved locale category value is restored. */
+    ~LocaleGuard()
+    { restore(); }
+
+    /** immediately restore the saved locale category value. */
+    void restore()
+    {
+      if ( _category != -1 )
+      {
+       ::setlocale( _category, _value.c_str() );
+       _category = -1;
+      }
+    }
+
+  private:
+    int         _category;     ///< saved category or -1 if no restore needed
+    std::string _value;                ///< saved category value
+  };
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_LOCALEGUARD_H
index 717a9f3..801decd 100644 (file)
@@ -186,7 +186,7 @@ namespace zypp
   {
     xmlout::Node guard( str, name_r, xmlout::Node::optionalContent );
     const std::string & content( asString( obj ) );
-    if ( ! content.empty() ) *guard << content;
+    if ( ! content.empty() ) *guard << xml::escape( content );
     return str;
   }
   //@}
index d8ecef2..eee68c3 100644 (file)
@@ -1009,6 +1009,7 @@ void MediaCurl::evaluateCurlCode( const Pathname &filename,
             ZYPP_THROW(MediaForbiddenException(url, msg403));
           }
           case 404:
+          case 410:
               ZYPP_THROW(MediaFileNotFoundException(_url, filename));
           }
 
index 18c05fe..90967bd 100644 (file)
@@ -608,6 +608,21 @@ MediaHandler::checkAttached(bool matchMountFs) const
          }
           // differs
         }
+        else // mixed cases:
+       {
+         // Type ISO: Since 11.1 mtab might contain the name of
+         // the loop device instead of the iso file:
+         if ( ref.mediaSource->type == "iso"
+           && str::hasPrefix( Pathname(e->src).asString(), "/dev/loop" )
+           && ref.attachPoint->path == Pathname(e->dir) )
+         {
+           DBG << "Found bound media "
+           << ref.mediaSource->asString()
+           << " in the mount table as " << e->src << std::endl;
+           _isAttached = true;
+           break;
+         }
+       }
       }
 
       if( !_isAttached)
index 83bd5cf..85aa783 100644 (file)
 
 #include "zypp/media/MediaISO.h"
 
-
-#define LOSETUP_TOOL_PATH "/sbin/losetup"
-
-using std::string;
 using std::endl;
 
 //////////////////////////////////////////////////////////////////////
@@ -149,32 +145,6 @@ namespace zypp
     }
 
     // ---------------------------------------------------------------
-    string MediaISO::findUnusedLoopDevice()
-    {
-      const char* argv[] =
-      {
-        LOSETUP_TOOL_PATH,
-        "-f",
-        NULL
-      };
-      ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
-
-      string out = losetup.receiveLine();
-      string device = out.substr(0, out.size() - 1); // remove the trailing endl
-      for(; out.length(); out = losetup.receiveLine())
-        DBG << "losetup: " << out;
-
-      if (losetup.close() != 0)
-      {
-        ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
-        ZYPP_THROW(MediaNoLoopDeviceException(_url));
-      }
-
-      DBG << "found " << device << endl;
-      return device;
-    }
-
-    // ---------------------------------------------------------------
     void MediaISO::attachTo(bool next)
     {
       if(next)
@@ -215,20 +185,9 @@ namespace zypp
         ZYPP_THROW(MediaNotSupportedException(_url));
       }
 
-      //! \todo make this thread-safe - another thread might pick up the same device
-      string loopdev = findUnusedLoopDevice(); // (bnc #428009)
-
-      MediaSourceRef media( new MediaSource("iso",  loopdev));
-      PathInfo dinfo(loopdev);
-      if( dinfo.isBlk())
-      {
-        media->maj_nr = dinfo.devMajor();
-        media->min_nr = dinfo.devMinor();
-      }
-      else
-        ERR << loopdev << " is not a block device" << endl;
+      MediaSourceRef media( new MediaSource("iso", isofile.asString() ) );
 
-      AttachedMedia  ret( findAttachedMedia( media));
+      AttachedMedia  ret( findAttachedMedia(media));
       if( ret.mediaSource &&
           ret.attachPoint &&
           !ret.attachPoint->empty())
@@ -253,7 +212,7 @@ namespace zypp
         setAttachPoint( mountpoint, true);
       }
 
-      std::string mountopts("ro,loop=" + loopdev);
+      std::string mountopts("ro,loop");
 
       Mount mount;
       mount.mount(isofile.asString(), mountpoint,
index 79715bd..7828e8b 100644 (file)
@@ -39,9 +39,6 @@ namespace zypp
         MediaAccessId _isosource;
         std::string   _filesystem;
 
-      private:
-        std::string findUnusedLoopDevice();
-
       protected:
 
        virtual void attachTo (bool next = false);
index f5edfb6..81262c7 100644 (file)
@@ -95,6 +95,7 @@ void TransferSettings::reset()
 
 void TransferSettings::addHeader( const std::string &header )
 {
+  if ( ! header.empty() )
     _impl->_headers.push_back(header);
 }
 
index 1c6572d..c2da3c2 100644 (file)
@@ -68,7 +68,7 @@ namespace zypp
                     std::pair<std::string, std::string> values(*it);
                     // curl resets headers that are empty, so we use a workaround
                     if (values.second.empty()) {
-                        values.second = "\nX-libcurl-Empty-Header-Workaround: *";
+                        values.second = "\r\nX-libcurl-Empty-Header-Workaround: *";
                     }                    
                     headers.insert(values);                    
                 }
index 0efb181..ee32138 100644 (file)
@@ -173,16 +173,15 @@ namespace zypp
     /// \class FilterRunsInLXC
     /// \brief Functor guessing whether \a PID is running in a container.
     ///
-    /// Asumme a using different \c pid/mnt namespace than \c self.
+    /// Assumme using different \c pid namespace than \c self.
     /////////////////////////////////////////////////////////////////
     struct FilterRunsInLXC
     {
       bool operator()( pid_t pid_r ) const
-      { return( nsIno( pid_r, "pid" ) != pidNS || nsIno( pid_r, "mnt" ) != mntNS ); }
+      { return( nsIno( pid_r, "pid" ) != pidNS ); }
 
       FilterRunsInLXC()
       : pidNS( nsIno( "self", "pid" ) )
-      , mntNS( nsIno( "self", "mnt" ) )
       {}
 
       static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
@@ -192,7 +191,6 @@ namespace zypp
       { return  nsIno( asString(pid_r), ns_r ); }
 
       ino_t pidNS;
-      ino_t mntNS;
     };
 
     /////////////////////////////////////////////////////////////////
index c25ebab..82f7c16 100644 (file)
@@ -274,6 +274,7 @@ class  HelixControl {
   private:
     std::string dumpFile; // Path of the generated testcase
     std::ofstream *file;
+    bool _inSetup;
 
   public:
     HelixControl (const std::string & controlPath,
@@ -282,13 +283,25 @@ class  HelixControl {
                  const LocaleSet &languages,
                  const target::Modalias::ModaliasList & modaliasList,
                  const std::set<std::string> & multiversionSpec,
-                 const std::string & systemPath,
-                 const bool forceResolve,
-                 const bool onlyRequires,
-                 const bool ignorealreadyrecommended);
+                 const std::string & systemPath);
     HelixControl ();
     ~HelixControl ();
 
+    void closeSetup()
+    {
+      if ( _inSetup )
+      {
+       *file << "</setup>" << endl << "<trial>" << endl;
+       _inSetup = false;
+      }
+    }
+
+    void addTagIf( const std::string & tag_r, bool yesno_r = true )
+    {
+      if ( yesno_r )
+       *file << (_inSetup ? TAB : "") << "<" << tag_r << "/>" << endl;
+    }
+
     void installResolvable (const ResObject::constPtr &resObject,
                            const ResStatus &status);
     void lockResolvable (const ResObject::constPtr &resObject,
@@ -300,10 +313,6 @@ class  HelixControl {
     void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
     void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
 
-    void distupgrade ();
-    void verifySystem ();
-    void update ();
-
     std::string filename () { return dumpFile; }
 };
 
@@ -313,11 +322,9 @@ HelixControl::HelixControl(const std::string & controlPath,
                           const LocaleSet &languages,
                           const target::Modalias::ModaliasList & modaliasList,
                           const std::set<std::string> & multiversionSpec,
-                          const std::string & systemPath,
-                          const bool forceResolve,
-                          const bool onlyRequires,
-                          const bool ignorealreadyrecommended)
+                          const std::string & systemPath)
     :dumpFile (controlPath)
+    ,_inSetup( true )
 {
     file = new ofstream(controlPath.c_str());
     if (!file) {
@@ -358,7 +365,7 @@ HelixControl::HelixControl(const std::string & controlPath,
     }
 
     for_( it, modaliasList.begin(), modaliasList.end() ) {
-       *file << TAB << "<modalias name=\"" <<  *it
+       *file << TAB << "<modalias name=\"" <<  xml_escape(*it)
              << "\" />" << endl;
     }
 
@@ -367,15 +374,7 @@ HelixControl::HelixControl(const std::string & controlPath,
              << "\" />" << endl;
     }
 
-    if (forceResolve)
-       *file << TAB << "<forceResolve/>" << endl;
-    if (onlyRequires)
-       *file << TAB << "<onlyRequires/>" << endl;
-    if (ignorealreadyrecommended)
-       *file << TAB << "<ignorealreadyrecommended/>" << endl;
-
-    *file << "</setup>" << endl
-         << "<trial>" << endl;
+    // setup continued outside....
 }
 
 HelixControl::HelixControl()
@@ -386,6 +385,7 @@ HelixControl::HelixControl()
 
 HelixControl::~HelixControl()
 {
+    closeSetup();      // in case it is still open
     *file << "</trial>" << endl
          << "</test>" << endl;
     delete(file);
@@ -448,21 +448,6 @@ void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r
   }
 }
 
-void HelixControl::distupgrade()
-{
-    *file << "<distupgrade/>" << endl;
-}
-
-void HelixControl::verifySystem()
-{
-    *file << "<verify/>" << endl;
-}
-
-void HelixControl::update()
-{
-    *file << "<update/>" << endl;
-}
-
 //---------------------------------------------------------------------------
 
 Testcase::Testcase()
@@ -559,10 +544,24 @@ bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver
                          pool.getRequestedLocales(),
                          target::Modalias::instance().modaliasList(),
                          ZConfig::instance().multiversionSpec(),
-                         "solver-system.xml.gz",
-                         resolver.forceResolve(),
-                         resolver.onlyRequires(),
-                         resolver.ignoreAlreadyRecommended() );
+                         "solver-system.xml.gz");
+
+    // In <setup>: resolver flags,...
+    control.addTagIf( "ignorealreadyrecommended",      resolver.ignoreAlreadyRecommended() );
+    control.addTagIf( "onlyRequires",          resolver.onlyRequires() );
+    control.addTagIf( "forceResolve",          resolver.forceResolve() );
+
+    control.addTagIf( "cleandepsOnRemove",     resolver.cleandepsOnRemove() );
+
+    control.addTagIf( "allowVendorChange",     resolver.allowVendorChange() );
+
+    control.addTagIf( "dupAllowDowngrade",     resolver.dupAllowDowngrade() );
+    control.addTagIf( "dupAllowNameChange",    resolver.dupAllowNameChange() );
+    control.addTagIf( "dupAllowArchChange",    resolver.dupAllowArchChange() );
+    control.addTagIf( "dupAllowVendorChange",  resolver.dupAllowVendorChange() );
+
+    control.closeSetup();
+    // Entering <trial>...
 
     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
        control.installResolvable (iter->resolvable(), iter->status());
@@ -585,12 +584,9 @@ bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver
                             SystemCheck::instance().conflictSystemCap());
     control.addUpgradeRepos( resolver.upgradeRepos() );
 
-    if (resolver.isUpgradeMode())
-       control.distupgrade ();
-    if (resolver.isUpdateMode())
-       control.update();
-    if (resolver.isVerifyingMode())
-       control.verifySystem();
+    control.addTagIf( "distupgrade",   resolver.isUpgradeMode() );
+    control.addTagIf( "update",                resolver.isUpdateMode() );
+    control.addTagIf( "verify",                resolver.isVerifyingMode() );
 
     return true;
 }
index 8b0cb55..7e8d956 100644 (file)
@@ -32,6 +32,7 @@ extern "C"
 #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"
@@ -1051,7 +1052,12 @@ void RpmDb::importPubkey( const PublicKey & pubkey_r )
 
   for_( it, rpmKeys.begin(), rpmKeys.end() )
   {
-    if ( keyEd == *it ) // quick test (Edition is IdStringType!)
+    // 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;
@@ -1517,7 +1523,9 @@ RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPac
   qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
 
   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 );