Add public accessible PublicKeyData/PublicKeyScanner classes
authorMichael Andres <ma@suse.de>
Wed, 31 Jul 2013 11:50:32 +0000 (13:50 +0200)
committerMichael Andres <ma@suse.de>
Tue, 27 Aug 2013 16:44:35 +0000 (18:44 +0200)
zypp/PublicKey.cc
zypp/PublicKey.h

index 0ca518f..7fe3ba4 100644 (file)
@@ -14,8 +14,6 @@
 #include <iostream>
 #include <vector>
 
-//#include "zypp/base/Logger.h"
-
 #include "zypp/base/Gettext.h"
 #include "zypp/base/String.h"
 #include "zypp/base/Regex.h"
@@ -24,7 +22,7 @@
 #include "zypp/TmpPath.h"
 #include "zypp/PathInfo.h"
 #include "zypp/base/Exception.h"
-#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
 #include "zypp/Date.h"
 #include "zypp/TmpPath.h"
 
@@ -37,127 +35,283 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
-  /// \class PublicKey::Impl
-  /// \brief  PublicKey implementation.
+  /// \class PublicKeyData::Impl
+  /// \brief  PublicKeyData implementation.
   ///////////////////////////////////////////////////////////////////
-  struct PublicKey::Impl
+  struct PublicKeyData::Impl
   {
-    /** Data we extract from one key. */
-    struct KeyData
+    std::string _id;
+    std::string _name;
+    std::string _fingerprint;
+    Date        _created;
+    Date        _expires;
+
+  public:
+    /** Offer default Impl. */
+    static shared_ptr<Impl> nullimpl()
     {
-      std::string _id;
-      std::string _name;
-      std::string _fingerprint;
-      Date        _created;
-      Date        _expires;
-    };
+      static shared_ptr<Impl> _nullimpl( new Impl );
+      return _nullimpl;
+    }
 
-    Impl()
-    {}
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+  ///////////////////////////////////////////////////////////////////
 
-    Impl( const Pathname & keyfile )
-    {
-      PathInfo info( keyfile );
-      MIL << "Takeing pubkey from " << keyfile << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyfile, "sha1") << endl;
+  ///////////////////////////////////////////////////////////////////
+  /// class PublicKeyData
+  ///////////////////////////////////////////////////////////////////
 
-      if ( !info.isExist() )
-        ZYPP_THROW(Exception("Can't read public key from " + keyfile.asString() + ", file not found"));
+  PublicKeyData::PublicKeyData()
+    : _pimpl( Impl::nullimpl() )
+  {}
 
-      if ( copy( keyfile, _dataFile.path() ) != 0 )
-        ZYPP_THROW(Exception("Can't copy public key data from " + keyfile.asString() + " to " +  _dataFile.path().asString() ));
+  PublicKeyData::~PublicKeyData()
+  {}
 
-      readFromFile();
-    }
+  PublicKeyData::operator bool() const
+  { return !_pimpl->_fingerprint.empty(); }
 
-    Impl( const filesystem::TmpFile & sharedfile )
-      : _dataFile( sharedfile )
-    { readFromFile(); }
+  std::string PublicKeyData::id() const
+  { return _pimpl->_id; }
 
-    public:
-      /** Offer default Impl. */
-      static shared_ptr<Impl> nullimpl()
-      {
-        static shared_ptr<Impl> _nullimpl( new Impl );
-        return _nullimpl;
-      }
+  std::string PublicKeyData::name() const
+  { return _pimpl->_name; }
 
-      std::string asString() const
-      {
-       return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
-                         id().c_str(), str::hexstring(created(),8).substr(2).c_str(),
-                         name().c_str(),
-                         fingerprint().c_str(),
-                         daysToLive() );
+  std::string PublicKeyData::fingerprint() const
+  { return _pimpl->_fingerprint; }
+
+  Date PublicKeyData::created() const
+  { return _pimpl->_created; }
+
+  Date PublicKeyData::expires() const
+  { return _pimpl->_expires; }
+
+  bool PublicKeyData::expired() const
+  { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
+
+  int PublicKeyData::daysToLive() const
+  {
+    if ( _pimpl->_expires )
+    {
+      Date exp( _pimpl->_expires - Date::now() );
+      return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
+    }
+    return INT_MAX;
+  }
+
+  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;
+  }
 
-      std::string id() const
-      { return _keyData._id; }
+  std::string PublicKeyData::gpgPubkeyVersion() const
+  { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
 
-      std::string name() const
-      { return _keyData._name; }
+  std::string PublicKeyData::gpgPubkeyRelease() const
+  { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
 
-      std::string fingerprint() const
-      { return _keyData._fingerprint; }
+  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() );
+  }
 
-      std::string gpgPubkeyVersion() const
-      { return _keyData._id.empty() ? _keyData._id : str::toLower( _keyData._id.substr(8,8) ); }
+  std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
+  {
+    str << "[" << obj.name() << "]" << endl;
+    str << "  fpr " << obj.fingerprint() << endl;
+    str << "   id " << obj.id() << endl;
+    str << "  cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
+    str << "  exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
+    str << "  ttl " << obj.daysToLive() << endl;
+    str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
+    str << "]";
+    return str;
+  }
 
-      std::string gpgPubkeyRelease() const
-      { return _keyData._created ? str::hexstring( _keyData._created ).substr(2) : std::string(); }
+  bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
+  { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
 
-      Date created() const
-      { return _keyData._created; }
 
-      Date expires() const
-      { return _keyData._expires; }
+  ///////////////////////////////////////////////////////////////////
+  /// \class PublicKeyScanner::Impl
+  /// \brief  PublicKeyScanner implementation.
+  ///////////////////////////////////////////////////////////////////
+  struct PublicKeyScanner::Impl
+  {
+    std::vector<std::string>                   _words;
+    enum { pNONE, pPUB, pSIG, pFPR, pUID }     _parseEntry;
+
+   Impl()
+      : _parseEntry( pNONE )
+    {}
 
-      std::string expiresAsString() const
+    void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
+    {
+      // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
+      // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
+      // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
+      // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
+      // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
+      // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
+      // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
+      // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
+      if ( line_r.empty() )
+       return;
+
+      // quick check for interesting entries
+      _parseEntry = pNONE;
+      switch ( line_r[0] )
       {
-       if ( !_keyData._expires )
-       { // translators: an annotation to a gpg keys expiry date
-         return _("(does not expire)");
-       }
-       std::string ret( _keyData._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;
+       #define DOTEST( C1, C2, C3, E ) case C1: if ( line_r[1] == C2 && line_r[2] == C3 && line_r[3] == ':' ) _parseEntry = E; break
+       DOTEST( 'p', 'u', 'b', pPUB );
+       DOTEST( 's', 'i', 'g', pSIG );
+       DOTEST( 'f', 'p', 'r', pFPR );
+       DOTEST( 'u', 'i', 'd', pUID );
+       #undef DOTEST
       }
+      if ( _parseEntry == pNONE )
+       return;
 
-      Pathname path() const
-      { return _dataFile.path(); }
+      if ( line_r[line_r.size()-1] == '\n' )
+       line_r.erase( line_r.size()-1 );
+      // DBG << line_r << endl;
+
+      _words.clear();
+      str::splitFields( line_r, std::back_inserter(_words), ":" );
+
+      PublicKeyData * key( &keys_r.back() );
 
-      bool expired() const
+      switch ( _parseEntry )
       {
-       Date exp( expires() );
-       return( exp && exp < Date::now() );
+       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]));
+         break;
+
+       case pSIG:
+         // Update creation/modification date from signatures type "13x".
+         if ( _words[_words.size()-2] == "13x" )
+         {
+           Date cdate(str::strtonum<Date::ValueType>(_words[5]));
+           if ( key->_pimpl->_created < cdate )
+             key->_pimpl->_created = cdate;
+         }
+         break;
+
+       case pFPR:
+         if ( key->_pimpl->_fingerprint.empty() )
+           key->_pimpl->_fingerprint = _words[9];
+         break;
+
+       case pUID:
+         if ( ! _words[9].empty() )
+           key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+         break;
+
+       case pNONE:
+         break;
       }
+    }
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  // class PublicKeyScanner
+  ///////////////////////////////////////////////////////////////////
+
+  PublicKeyScanner::PublicKeyScanner()
+    : _pimpl( new Impl )
+  {}
+
+  PublicKeyScanner::~PublicKeyScanner()
+  {}
 
-      int daysToLive() const
+  void PublicKeyScanner::scan( std::string line_r )
+  { _pimpl->scan( line_r, _keys ); }
+
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class PublicKey::Impl
+  /// \brief  PublicKey implementation.
+  ///////////////////////////////////////////////////////////////////
+  struct PublicKey::Impl
+  {
+    Impl()
+    {}
+
+    Impl( const Pathname & keyFile_r )
+    {
+      PathInfo info( keyFile_r );
+      MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
+
+      if ( !info.isExist() )
+        ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
+
+      if ( filesystem::hardlinkCopy( keyFile_r, _dataFile.path() ) != 0 )
+       ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " +  _dataFile.path().asString() ));
+
+      readFromFile();
+    }
+
+    Impl( const filesystem::TmpFile & sharedFile_r )
+      : _dataFile( sharedFile_r )
+    { readFromFile(); }
+
+    Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
+      : _dataFile( sharedFile_r )
+      , _keyData( keyData_r )
+    {
+      if ( ! keyData_r )
       {
-       Date exp( expires() );
-       if ( ! expires() )
-         return INT_MAX;
-       exp -= Date::now();
-       return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
+       WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
+       readFromFile();
       }
+    }
 
-    protected:
+    public:
+      const PublicKeyData & keyData() const
+      { return _keyData; }
 
+      Pathname path() const
+      { return _dataFile.path(); }
+
+    protected:
       void readFromFile()
       {
         PathInfo info( _dataFile.path() );
@@ -183,98 +337,45 @@ namespace zypp
           _dataFile.path().asString().c_str(),
           NULL
         };
+        ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
 
-        ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
-
-        // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
-        // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
-        // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
-        // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
-        // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
-        // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
-        // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
-       KeyData keyData;
-        std::string line;
-       std::vector<std::string> words;
-       enum { pNONE, pPUB, pSIG, pFPR, pUID } parseEntry;
-       bool sawSig = false;
-        for ( line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
+       PublicKeyScanner scanner;
+        for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
         {
-          if ( line.empty() )
-            continue;
-
-         // quick check for interesting entries
-         parseEntry = pNONE;
-         switch ( line[0] )
-         {
-#define DOTEST( C1, C2, C3, E ) case C1: if ( line[1] == C2 && line[2] == C3 && line[3] == ':' ) parseEntry = E; break
-           DOTEST( 'p', 'u', 'b', pPUB );
-           DOTEST( 's', 'i', 'g', pSIG );
-           DOTEST( 'f', 'p', 'r', pFPR );
-           DOTEST( 'u', 'i', 'd', pUID );
-#undef DOTEST
-         }
-         if ( parseEntry == pNONE )
-           continue;
-
-          if ( line[line.size()-1] == '\n' )
-            line.erase( line.size()-1 );
-
-         words.clear();
-          str::splitFields( line, std::back_inserter(words), ":" );
-
-         switch ( parseEntry )
-         {
-           case pPUB:
-             keyData = KeyData();      // reset upon new key
-             sawSig  = false;
-             keyData._id      = words[4];
-             keyData._name    = words[9];
-             keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
-             keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
-             break;
-
-           case pSIG:
-             if ( !sawSig && words[words.size()-2] == "13x"  )
-             {
-               // update creation and expire dates from 1st signature type "13x"
-               if ( ! words[5].empty() )
-                 keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
-               if ( ! words[6].empty() )
-                 keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
-               sawSig = true;
-             }
-             break;
-
-           case pFPR:
-             if ( ! words[9].empty() )
-               keyData._fingerprint = words[9];
-             break;
-
-           case pUID:
-             if ( ! words[9].empty() )
-               keyData._name = words[9];
-             break;
-
-           case pNONE:
-             break;
-         }
-        }
+         scanner.scan( line );
+       }
         prog.close();
 
-        if ( keyData._id.empty() )
-         ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
+       switch ( scanner._keys.size() )
+       {
+         case 0:
+           ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
+           break;
 
-        //replace all escaped semicolon with real ':'
-        str::replaceAll( keyData._name, "\\x3a", ":" );
+         case 1:
+           // ok.
+           break;
 
-       _keyData = keyData;
-        MIL << "Read pubkey from " << info.path() << ": " << asString() << endl;
+         default:
+           WAR << "File " << _dataFile.path().asString() << " contains multiple keys: " <<  scanner._keys << endl;
+           break;
+       }
+
+       _keyData = scanner._keys.back();
+       MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
       }
 
     private:
       filesystem::TmpFile      _dataFile;
-      KeyData                  _keyData;
+      PublicKeyData            _keyData;
+
+    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 );
@@ -285,85 +386,74 @@ namespace zypp
   ///////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
-  //
-  //   class PublicKey
-  //
+  // class PublicKey
   ///////////////////////////////////////////////////////////////////
   PublicKey::PublicKey()
   : _pimpl( Impl::nullimpl() )
   {}
 
   PublicKey::PublicKey( const Pathname & file )
-  : _pimpl( new Impl(file) )
+  : _pimpl( new Impl( file ) )
   {}
 
   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
-  : _pimpl( new Impl(sharedfile) )
+  : _pimpl( new Impl( sharedfile ) )
+  {}
+
+  PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keydata )
+  : _pimpl( new Impl( sharedfile, keydata ) )
   {}
 
   PublicKey::~PublicKey()
   {}
 
-  std::string PublicKey::asString() const
-  { return _pimpl->asString(); }
+  const PublicKeyData & PublicKey::keyData() const
+  { return _pimpl->keyData(); }
+
+  Pathname PublicKey::path() const
+  { return _pimpl->path(); }
 
   std::string PublicKey::id() const
-  { return _pimpl->id(); }
+  { return keyData().id(); }
 
   std::string PublicKey::name() const
-  { return _pimpl->name(); }
+  { return keyData().name(); }
 
   std::string PublicKey::fingerprint() const
-  { return _pimpl->fingerprint(); }
-
-  std::string PublicKey::gpgPubkeyVersion() const
-  { return _pimpl->gpgPubkeyVersion(); }
-
-  std::string PublicKey::gpgPubkeyRelease() const
-  { return _pimpl->gpgPubkeyRelease(); }
+  { return keyData().fingerprint(); }
 
   Date PublicKey::created() const
-  { return _pimpl->created(); }
+  { return keyData().created(); }
 
   Date PublicKey::expires() const
-  { return _pimpl->expires(); }
-
-  std::string PublicKey::expiresAsString() const
-  { return _pimpl->expiresAsString(); }
+  { return keyData().expires(); }
 
   bool PublicKey::expired() const
-  { return _pimpl->expired(); }
+  { return keyData().expired(); }
 
   int PublicKey::daysToLive() const
-  { return _pimpl->daysToLive(); }
+  { return keyData().daysToLive(); }
 
-  Pathname PublicKey::path() const
-  { return _pimpl->path(); }
+  std::string PublicKey::expiresAsString() const
+  { return keyData().expiresAsString(); }
 
-  bool PublicKey::operator==( PublicKey b ) const
-  {
-    return (   b.id() == id()
-            && b.fingerprint() == fingerprint()
-            && b.created() == created() );
-  }
+  std::string PublicKey::gpgPubkeyVersion() const
+  { return keyData().gpgPubkeyVersion(); }
+
+  std::string PublicKey::gpgPubkeyRelease() const
+  { return keyData().gpgPubkeyRelease(); }
+
+  std::string PublicKey::asString() const
+  { return keyData().asString(); }
+
+  bool PublicKey::operator==( PublicKey rhs ) const
+  { return rhs.keyData() == keyData(); }
 
   bool PublicKey::operator==( std::string sid ) const
-  {
-    return sid == id();
-  }
+  { return sid == id(); }
 
   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
-  {
-    str << "[" << obj.name() << "]" << endl;
-    str << "  fpr " << obj.fingerprint() << endl;
-    str << "   id " << obj.id() << endl;
-    str << "  cre " << obj.created() << endl;
-    str << "  exp " << obj.expiresAsString() << endl;
-    str << "  ttl " << obj.daysToLive() << endl;
-    str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
-    str << "]";
-    return str;
-  }
+  { return dumpOn( str, obj.keyData() ); }
 
   /////////////////////////////////////////////////////////////////
 } // namespace zypp
index 50983f0..036f076 100644 (file)
@@ -21,6 +21,7 @@
 #include "zypp/base/PtrTypes.h"
 #include "zypp/base/Exception.h"
 #include "zypp/Pathname.h"
+#include "zypp/Date.h"
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
@@ -30,13 +31,11 @@ namespace zypp
   {
     class TmpFile;
   }
-  class Date;
 
-
-  /**
-   * Exception thrown when the supplied key is
-   * not a valid gpg key
-   */
+  ///////////////////////////////////////////////////////////////////
+  /// \class BadKeyException
+  /// \brief Exception thrown when the supplied key is not a valid gpg key
+  ///////////////////////////////////////////////////////////////////
   class BadKeyException : public Exception
   {
     public:
@@ -61,18 +60,148 @@ namespace zypp
     private:
       Pathname _keyfile;
   };
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class PublicKeyData
+  /// \brief Class representing one GPG Public Keys data.
+  /// \ref PublicKeyData are provided e.g. by a \ref PublicKey or
+  /// a \ref KeyRing. \ref PublicKeyData are usually easier to
+  /// retrieve and sufficient unless you actually need an ASCII
+  /// armored version of the key placed in a tempfile. In this
+  /// case use \ref PublicKey.
+  ///////////////////////////////////////////////////////////////////
+  class PublicKeyData
+  {
+  public:
+    /** Default constructed: empty data. */
+    PublicKeyData();
+
+    ~PublicKeyData();
+
+    /** Scan data from 'gpg --with-colons' key listings. */
+    friend class PublicKeyScanner;
+
+    /** Whether this contains valid data (not default constructed). */
+    explicit operator bool() const;
+
+  public:
+    /** Key ID. */
+    std::string id() const;
+
+    /** Key name.  */
+    std::string name() const;
+
+    /** Key fingerprint.*/
+    std::string fingerprint() const;
+
+    /** Creation / last modification date (latest selfsig). */
+    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;
+
+    /** * Expiry info in a human readable form.
+     * The exipry daye plus an annotation if the key has expired, or will
+     * expire within 90 days.
+     * \code
+     * (does not expire)
+     * Tue May 11 13:37:33 CEST 2010
+     * Tue May 11 13:37:33 CEST 2010 (expires in 90 days)
+     * Tue May 11 13:37:33 CEST 2010 (expires in 1 day)
+     * Tue May 11 13:37:33 CEST 2010 (expires within 24h)
+     * Tue May 11 13:37:33 CEST 2010 (EXPIRED)
+     * \endcode
+     */
+    std::string expiresAsString() const;
+
+    /** Gpg-pubkey version as computed by rpm (trailing 8 byte \ref id) */
+    std::string gpgPubkeyVersion() const;
+
+    /** Gpg-pubkey release as computed by rpm (hexencoded \ref created) */
+    std::string gpgPubkeyRelease() const;
+
+    /** Simple string representation.
+     * Encodes \ref id, \ref gpgPubkeyRelease, \ref name and \ref fingerprint.
+     * \code
+     * [E3A5C360307E3D54-4be01a65] [SuSE Package Signing Key <build@suse.de>] [4E98E67519D98DC7362A5990E3A5C360307E3D54]
+     * \endcode
+     */
+    std::string asString() const;
+
+  private:
+    class Impl;
+    RWCOW_pointer<Impl> _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
+
+  /** \relates PublicKeyData Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const PublicKeyData & obj )
+  { return str << obj.asString(); }
+
+  /** \relates PublicKeyData Detailed stream output */
+  std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
+
+  /** \relates PublicKeyData Equal based on  fingerprint anf creation date. */
+  bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs );
+
+  /** \relates PublicKeyData NotEqual. */
+  inline bool operator!=( const PublicKeyData & lhs, const PublicKeyData & rhs )
+  { return !( lhs == rhs ); }
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class PublicKeyScanner
+  /// \brief Scan abstract from 'gpg --with-colons' key listings.
+  /// Feed gpg output line by line into \ref scan. The collected \ref PublicKeyData
+  /// contain the keys data (fingerprint, uid,...) but not the key itself (ASCII
+  /// armored stored in a file).
+  /// \code
+  ///   std::list<PublicKeyData> result;
+  ///   {
+  ///     PublicKeyScanner scanner;
+  ///     for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
+  ///       scanner.scan( line );
+  ///     result.swap( scanner._keys );
+  ///   }
+  /// \endcode
+  /// \relates PublicKeyData
+  ///////////////////////////////////////////////////////////////////
+  struct PublicKeyScanner
+  {
+    PublicKeyScanner();
+    ~PublicKeyScanner();
+
+    /** Feed gpg output line by line into \ref scan. */
+    void scan( std::string line_r );
+
+    /** Extracted keys. */
+    std::list<PublicKeyData> _keys;
+
+  private:
+    class Impl;
+    RW_pointer<Impl, rw_pointer::Scoped<Impl> > _pimpl;
+  };
+  ///////////////////////////////////////////////////////////////////
 
 
   ///////////////////////////////////////////////////////////////////
   /// \class PublicKey
-  /// \brief Class representing one GPG Public Key.
-  /// If a key file actually contains multiple keys, the last one
-  /// is taken.
+  /// \brief Class representing one GPG Public Key (PublicKeyData + ASCII armored in a tempfile).
+  /// If you don't need the ASCII armored version of the key stored in
+  /// a tempfile, using \ref PublicKeyData might be sufficient.
   ///////////////////////////////////////////////////////////////////
   class PublicKey
   {
-    friend std::ostream & operator<<( std::ostream & str, const PublicKey & obj );
-
   public:
     /** Implementation  */
     class Impl;
@@ -89,8 +218,7 @@ namespace zypp
      *
      * \throws when data does not make a key
      */
-    explicit
-    PublicKey( const Pathname & file );
+    explicit PublicKey( const Pathname & keyFile_r );
 
     /** Ctor reading the key from a \ref TmpFile.
      *
@@ -98,86 +226,42 @@ namespace zypp
      *
      * \throws when data does not make a key
      */
-    explicit
-    PublicKey( const filesystem::TmpFile & sharedfile );
+    explicit PublicKey( const filesystem::TmpFile & sharedFile_r );
 
     ~PublicKey();
 
-    bool isValid() const
-    { return ( ! id().empty() && ! fingerprint().empty() && !path().empty() ); }
-
-    std::string asString() const;
-    std::string id() const;
-    std::string name() const;
-    std::string fingerprint() const;
-
-    /** Version rpm would assign to this key if imported into the rpm database.
-     * Rpm uses the lowercased trailing 8 byte from \ref id as \c version, and the
-     * creations dates lowercased hexadecimal representation as \c release.
-     * \see \ref gpgPubkeyRelease
-     * \code
-     * [zypp OBS Project <zypp@build.opensuse.org>]
-     *   fpr 47D7CE1DD600935B3B90365733D38EBC7FB7F464
-     *    id 33D38EBC7FB7F464           <-- trailing 8 byte
-     *   cre Thu Mar 13 19:15:40 2008   <-- converted to hex
-     *   exp Sat May 22 20:15:40 2010
-     * ]
-     *
-     * Converting the creation date to its hexadecimal representation:
-     * $ bc <<<"obase=16;$(date -d 'Thu Mar 13 19:15:40 2008' +%s)"
-     * 47D96F4C
-     *
-     * Rpms name for this key: gpg-pubkey-7fb7f464-47d96f4c
-     * \endcode
-     */
-    std::string gpgPubkeyVersion() const;
-
-    /** Release rpm would assign to this key if imported into the rpm database.
-     * This is the creations dates hexadecimal representation as \c release lowercased.
-     * \see \ref gpgPubkeyVersion
-     */
-    std::string gpgPubkeyRelease() const;
-
-    /**
-     * Date when the key was created.
-     */
-    Date created() const;
-
-    /**
-     * Date when the key expires.
-     * If the key never expires the date is Date() (i.e. 0 seconds since the epoch (1.1.1970))
-     */
-    Date expires() const;
+  public:
+    /** The public keys data (\see \ref PublicKeyData).*/
+    const PublicKeyData & keyData() const;
 
-    /**
-     * Expiry info in a human readable form.
-     * The exipry daye plus an annotation if the key has expired, or will
-     * expire within 90 days.
-     * \code
-     * (does not expire)
-     * Tue May 11 13:37:33 CEST 2010
-     * Tue May 11 13:37:33 CEST 2010 (expires in 90 days)
-     * Tue May 11 13:37:33 CEST 2010 (expires in 1 day)
-     * Tue May 11 13:37:33 CEST 2010 (expires within 24h)
-     * Tue May 11 13:37:33 CEST 2010 (EXPIRED)
-     * \endcode
-     */
-    std::string expiresAsString() const;
+    bool isValid() const
+    { return ! ( id().empty() || fingerprint().empty() ); }
 
-    /** Whether the key has expired. */
-    bool expired() const;
+    std::string id() const;                    //!< \see \ref PublicKeyData
+    std::string name() const;                  //!< \see \ref PublicKeyData
+    std::string fingerprint() const;           //!< \see \ref PublicKeyData
+    Date created() const;                      //!< \see \ref PublicKeyData
+    Date expires() const;                      //!< \see \ref PublicKeyData
+    std::string expiresAsString() const;       //!< \see \ref PublicKeyData
+    bool expired() const;                      //!< \see \ref PublicKeyData
+    int daysToLive() const;                    //!< \see \ref PublicKeyData
+    std::string gpgPubkeyVersion() const;      //!< \see \ref PublicKeyData
+    std::string gpgPubkeyRelease() const;      //!< \see \ref PublicKeyData
+    std::string asString() const;              //!< \see \ref PublicKeyData
 
-    /** 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;
 
+  public:
+    /** File containig the ASCII armored key. */
     Pathname path() const;
 
-    bool operator==( PublicKey b ) const;
-    bool operator==( std::string sid ) const;
+  public:
+    bool operator==( PublicKey rhs ) const;    // FIXME: change arg to const&
+    bool operator==( std::string sid ) const;  // FIXME: change arg to const&
+
+  private:
+    friend class KeyRing;
+    /** KeyRing ctor: No need to parse file if KeyRing already had valid KeyData. */
+    PublicKey( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r );
 
   private:
     /** Pointer to implementation */