fic date computation
[platform/upstream/libzypp.git] / zypp / PublicKey.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/PublicKey.cc
10  *
11 */
12 #include <climits>
13
14 #include <iostream>
15 #include <vector>
16
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/String.h"
19 #include "zypp/base/Regex.h"
20 #include "zypp/PublicKey.h"
21 #include "zypp/ExternalProgram.h"
22 #include "zypp/TmpPath.h"
23 #include "zypp/PathInfo.h"
24 #include "zypp/base/Exception.h"
25 #include "zypp/base/LogTools.h"
26 #include "zypp/Date.h"
27 #include "zypp/TmpPath.h"
28
29 #include <ctime>
30
31 using std::endl;
32
33 ///////////////////////////////////////////////////////////////////
34 namespace zypp
35 { /////////////////////////////////////////////////////////////////
36
37   ///////////////////////////////////////////////////////////////////
38   /// \class PublicKeyData::Impl
39   /// \brief  PublicKeyData implementation.
40   ///////////////////////////////////////////////////////////////////
41   struct PublicKeyData::Impl
42   {
43     std::string _id;
44     std::string _name;
45     std::string _fingerprint;
46     Date        _created;
47     Date        _expires;
48
49   public:
50     /** Offer default Impl. */
51     static shared_ptr<Impl> nullimpl()
52     {
53       static shared_ptr<Impl> _nullimpl( new Impl );
54       return _nullimpl;
55     }
56
57   private:
58     friend Impl * rwcowClone<Impl>( const Impl * rhs );
59     /** clone for RWCOW_pointer */
60     Impl * clone() const
61     { return new Impl( *this ); }
62   };
63   ///////////////////////////////////////////////////////////////////
64
65   ///////////////////////////////////////////////////////////////////
66   /// class PublicKeyData
67   ///////////////////////////////////////////////////////////////////
68
69   PublicKeyData::PublicKeyData()
70     : _pimpl( Impl::nullimpl() )
71   {}
72
73   PublicKeyData::~PublicKeyData()
74   {}
75
76   PublicKeyData::operator bool() const
77   { return !_pimpl->_fingerprint.empty(); }
78
79   std::string PublicKeyData::id() const
80   { return _pimpl->_id; }
81
82   std::string PublicKeyData::name() const
83   { return _pimpl->_name; }
84
85   std::string PublicKeyData::fingerprint() const
86   { return _pimpl->_fingerprint; }
87
88   Date PublicKeyData::created() const
89   { return _pimpl->_created; }
90
91   Date PublicKeyData::expires() const
92   { return _pimpl->_expires; }
93
94   bool PublicKeyData::expired() const
95   { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
96
97   int PublicKeyData::daysToLive() const
98   {
99     if ( _pimpl->_expires )
100     {
101       Date exp( _pimpl->_expires - Date::now() );
102       int ret = exp / Date::day;
103       if ( exp < 0 ) ret -= 1;
104       return ret;
105     }
106     return INT_MAX;
107   }
108
109   std::string PublicKeyData::expiresAsString() const
110   {
111     if ( !_pimpl->_expires )
112     { // translators: an annotation to a gpg keys expiry date
113       return _("(does not expire)");
114     }
115     std::string ret( _pimpl->_expires.asString() );
116     int ttl( daysToLive() );
117     if ( ttl <= 90 )
118     {
119       ret += " ";
120       if ( ttl < 0 )
121       { // translators: an annotation to a gpg keys expiry date
122         ret += _("(EXPIRED)");
123       }
124       else if ( ttl == 0 )
125       { // translators: an annotation to a gpg keys expiry date
126         ret += _("(expires within 24h)");
127       }
128       else
129       { // translators: an annotation to a gpg keys expiry date
130         ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
131       }
132     }
133     return ret;
134   }
135
136   std::string PublicKeyData::gpgPubkeyVersion() const
137   { return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
138
139   std::string PublicKeyData::gpgPubkeyRelease() const
140   { return _pimpl->_created ? str::hexstring( _pimpl->_created ).substr(2) : std::string(); }
141
142   std::string PublicKeyData::asString() const
143   {
144     return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
145                       _pimpl->_id.c_str(),
146                       gpgPubkeyRelease().c_str(),
147                       _pimpl->_name.c_str(),
148                       _pimpl->_fingerprint.c_str(),
149                       daysToLive() );
150   }
151
152   std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
153   {
154     str << "[" << obj.name() << "]" << endl;
155     str << "  fpr " << obj.fingerprint() << endl;
156     str << "   id " << obj.id() << endl;
157     str << "  cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
158     str << "  exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
159     str << "  ttl " << obj.daysToLive() << endl;
160     str << "  rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
161     str << "]";
162     return str;
163   }
164
165   bool operator==( const PublicKeyData & lhs, const PublicKeyData & rhs )
166   { return ( lhs.fingerprint() == rhs.fingerprint() && lhs.created() == rhs.created() ); }
167
168
169   ///////////////////////////////////////////////////////////////////
170   /// \class PublicKeyScanner::Impl
171   /// \brief  PublicKeyScanner implementation.
172   ///////////////////////////////////////////////////////////////////
173   struct PublicKeyScanner::Impl
174   {
175     std::vector<std::string>                    _words;
176     enum { pNONE, pPUB, pSIG, pFPR, pUID }      _parseEntry;
177     bool                                        _parseOff;      // no 'sub:' key parsing
178
179    Impl()
180       : _parseEntry( pNONE )
181       , _parseOff( false )
182     {}
183
184     void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
185     {
186       // pub:-:1024:17:A84EDAE89C800ACA:971961473:1214043198::-:SuSE Package Signing Key <build@suse.de>:
187       // fpr:::::::::79C179B2E1C820C1890F9994A84EDAE89C800ACA:
188       // sig:::17:A84EDAE89C800ACA:1087899198:::::[selfsig]::13x:
189       // sig:::17:9E40E310000AABA4:980442706::::[User ID not found]:10x:
190       // sig:::1:77B2E6003D25D3D9:980443247::::[User ID not found]:10x:
191       // sig:::17:A84EDAE89C800ACA:1318348291:::::[selfsig]::13x:
192       // sub:-:2048:16:197448E88495160C:971961490:1214043258::: [expires: 2008-06-21]
193       // sig:::17:A84EDAE89C800ACA:1087899258:::::[keybind]::18x:
194       if ( line_r.empty() )
195         return;
196
197       // quick check for interesting entries, no parsing in subkeys
198       _parseEntry = pNONE;
199       switch ( line_r[0] )
200       {
201         case 'p':
202           if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
203           {
204             _parseEntry = pPUB;
205             _parseOff = false;
206           }
207           break;
208
209         case 'f':
210           if ( line_r[1] == 'p' && line_r[2] == 'r' && line_r[3] == ':' )
211             _parseEntry = pFPR;
212           break;
213
214         case 'u':
215           if ( line_r[1] == 'i' && line_r[2] == 'd' && line_r[3] == ':' )
216             _parseEntry = pUID;
217           break;
218
219         case 's':
220           if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
221             _parseEntry = pSIG;
222           else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
223             _parseOff = true;
224           break;
225
226         default:
227           return;
228       }
229       if ( _parseOff || _parseEntry == pNONE )
230         return;
231
232       if ( line_r[line_r.size()-1] == '\n' )
233         line_r.erase( line_r.size()-1 );
234       // DBG << line_r << endl;
235
236       _words.clear();
237       str::splitFields( line_r, std::back_inserter(_words), ":" );
238
239       PublicKeyData * key( &keys_r.back() );
240
241       switch ( _parseEntry )
242       {
243         case pPUB:
244           keys_r.push_back( PublicKeyData() );  // reset upon new key
245           key = &keys_r.back();
246           key->_pimpl->_id      = _words[4];
247           key->_pimpl->_name    = str::replaceAll( _words[9], "\\x3a", ":" );
248           key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
249           key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
250           break;
251
252         case pSIG:
253           // Update creation/modification date from signatures type "13x".
254           if ( _words[_words.size()-2] == "13x" )
255           {
256             Date cdate(str::strtonum<Date::ValueType>(_words[5]));
257             if ( key->_pimpl->_created < cdate )
258               key->_pimpl->_created = cdate;
259           }
260           break;
261
262         case pFPR:
263           if ( key->_pimpl->_fingerprint.empty() )
264             key->_pimpl->_fingerprint = _words[9];
265           break;
266
267         case pUID:
268           if ( ! _words[9].empty() )
269             key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
270           break;
271
272         case pNONE:
273           break;        // intentionally no default:
274       }
275     }
276   };
277   ///////////////////////////////////////////////////////////////////
278
279   ///////////////////////////////////////////////////////////////////
280   // class PublicKeyScanner
281   ///////////////////////////////////////////////////////////////////
282
283   PublicKeyScanner::PublicKeyScanner()
284     : _pimpl( new Impl )
285   {}
286
287   PublicKeyScanner::~PublicKeyScanner()
288   {}
289
290   void PublicKeyScanner::scan( std::string line_r )
291   { _pimpl->scan( line_r, _keys ); }
292
293
294   ///////////////////////////////////////////////////////////////////
295   /// \class PublicKey::Impl
296   /// \brief  PublicKey implementation.
297   ///////////////////////////////////////////////////////////////////
298   struct PublicKey::Impl
299   {
300     Impl()
301     {}
302
303     Impl( const Pathname & keyFile_r )
304     {
305       PathInfo info( keyFile_r );
306       MIL << "Taking pubkey from " << keyFile_r << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyFile_r, "sha1") << endl;
307
308       if ( !info.isExist() )
309         ZYPP_THROW(Exception("Can't read public key from " + keyFile_r.asString() + ", file not found"));
310
311       if ( filesystem::hardlinkCopy( keyFile_r, _dataFile.path() ) != 0 )
312         ZYPP_THROW(Exception("Can't copy public key data from " + keyFile_r.asString() + " to " +  _dataFile.path().asString() ));
313
314       readFromFile();
315     }
316
317     Impl( const filesystem::TmpFile & sharedFile_r )
318       : _dataFile( sharedFile_r )
319     { readFromFile(); }
320
321     Impl( const filesystem::TmpFile & sharedFile_r, const PublicKeyData & keyData_r )
322       : _dataFile( sharedFile_r )
323       , _keyData( keyData_r )
324     {
325       if ( ! keyData_r )
326       {
327         WAR << "Invalid PublicKeyData supplied: scanning from file" << endl;
328         readFromFile();
329       }
330     }
331
332     public:
333       const PublicKeyData & keyData() const
334       { return _keyData; }
335
336       Pathname path() const
337       { return _dataFile.path(); }
338
339       const std::list<PublicKeyData> & hiddenKeys() const
340       { return _hiddenKeys; }
341
342     protected:
343       void readFromFile()
344       {
345         PathInfo info( _dataFile.path() );
346         MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
347
348         static filesystem::TmpDir dir;
349         const char* argv[] =
350         {
351           "gpg",
352           "-v",
353           "--no-default-keyring",
354           "--fixed-list-mode",
355           "--with-fingerprint",
356           "--with-colons",
357           "--homedir",
358           dir.path().asString().c_str(),
359           "--quiet",
360           "--no-tty",
361           "--no-greeting",
362           "--batch",
363           "--status-fd", "1",
364           _dataFile.path().asString().c_str(),
365           NULL
366         };
367         ExternalProgram prog( argv, ExternalProgram::Discard_Stderr, false, -1, true );
368
369         PublicKeyScanner scanner;
370         for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
371         {
372           scanner.scan( line );
373         }
374         prog.close();
375
376         switch ( scanner._keys.size() )
377         {
378           case 0:
379             ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
380             break;
381
382           case 1:
383             // ok.
384             _keyData = scanner._keys.back();
385             _hiddenKeys.clear();
386             break;
387
388           default:
389             WAR << "File " << _dataFile.path().asString() << " contains multiple keys: " <<  scanner._keys << endl;
390             _keyData = scanner._keys.back();
391             scanner._keys.pop_back();
392             _hiddenKeys.swap( scanner._keys );
393             break;
394         }
395
396         MIL << "Read pubkey from " << info.path() << ": " << _keyData << endl;
397       }
398
399     private:
400       filesystem::TmpFile       _dataFile;
401       PublicKeyData             _keyData;
402       std::list<PublicKeyData>  _hiddenKeys;
403
404     public:
405       /** Offer default Impl. */
406       static shared_ptr<Impl> nullimpl()
407       {
408         static shared_ptr<Impl> _nullimpl( new Impl );
409         return _nullimpl;
410       }
411
412     private:
413       friend Impl * rwcowClone<Impl>( const Impl * rhs );
414       /** clone for RWCOW_pointer */
415       Impl * clone() const
416       { return new Impl( *this ); }
417   };
418   ///////////////////////////////////////////////////////////////////
419
420   ///////////////////////////////////////////////////////////////////
421   // class PublicKey
422   ///////////////////////////////////////////////////////////////////
423   PublicKey::PublicKey()
424   : _pimpl( Impl::nullimpl() )
425   {}
426
427   PublicKey::PublicKey( const Pathname & file )
428   : _pimpl( new Impl( file ) )
429   {}
430
431   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
432   : _pimpl( new Impl( sharedfile ) )
433   {}
434
435   PublicKey::PublicKey( const filesystem::TmpFile & sharedfile, const PublicKeyData & keydata )
436   : _pimpl( new Impl( sharedfile, keydata ) )
437   {}
438
439   PublicKey::~PublicKey()
440   {}
441
442   const PublicKeyData & PublicKey::keyData() const
443   { return _pimpl->keyData(); }
444
445   Pathname PublicKey::path() const
446   { return _pimpl->path(); }
447
448   const std::list<PublicKeyData> & PublicKey::hiddenKeys() const
449   { return _pimpl->hiddenKeys(); }
450
451   std::string PublicKey::id() const
452   { return keyData().id(); }
453
454   std::string PublicKey::name() const
455   { return keyData().name(); }
456
457   std::string PublicKey::fingerprint() const
458   { return keyData().fingerprint(); }
459
460   Date PublicKey::created() const
461   { return keyData().created(); }
462
463   Date PublicKey::expires() const
464   { return keyData().expires(); }
465
466   bool PublicKey::expired() const
467   { return keyData().expired(); }
468
469   int PublicKey::daysToLive() const
470   { return keyData().daysToLive(); }
471
472   std::string PublicKey::expiresAsString() const
473   { return keyData().expiresAsString(); }
474
475   std::string PublicKey::gpgPubkeyVersion() const
476   { return keyData().gpgPubkeyVersion(); }
477
478   std::string PublicKey::gpgPubkeyRelease() const
479   { return keyData().gpgPubkeyRelease(); }
480
481   std::string PublicKey::asString() const
482   { return keyData().asString(); }
483
484   bool PublicKey::operator==( const PublicKey & rhs ) const
485   { return rhs.keyData() == keyData(); }
486
487   bool PublicKey::operator==( const std::string & sid ) const
488   { return sid == id(); }
489
490   std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
491   { return dumpOn( str, obj.keyData() ); }
492
493   /////////////////////////////////////////////////////////////////
494 } // namespace zypp
495 ///////////////////////////////////////////////////////////////////