00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <climits>
00013
00014 #include <iostream>
00015 #include <vector>
00016
00017
00018
00019 #include "zypp/base/Gettext.h"
00020 #include "zypp/base/String.h"
00021 #include "zypp/base/Regex.h"
00022 #include "zypp/PublicKey.h"
00023 #include "zypp/ExternalProgram.h"
00024 #include "zypp/TmpPath.h"
00025 #include "zypp/PathInfo.h"
00026 #include "zypp/base/Exception.h"
00027 #include "zypp/base/Logger.h"
00028 #include "zypp/Date.h"
00029 #include "zypp/TmpPath.h"
00030
00031 #include <ctime>
00032
00033 using std::endl;
00034
00036 namespace zypp
00037 {
00038
00043 struct PublicKey::Impl
00044 {
00046 struct KeyData
00047 {
00048 std::string _id;
00049 std::string _name;
00050 std::string _fingerprint;
00051 Date _created;
00052 Date _expires;
00053 };
00054
00055 Impl()
00056 {}
00057
00058 Impl( const Pathname & keyfile )
00059 {
00060 PathInfo info( keyfile );
00061 MIL << "Takeing pubkey from " << keyfile << " of size " << info.size() << " and sha1 " << filesystem::checksum(keyfile, "sha1") << endl;
00062
00063 if ( !info.isExist() )
00064 ZYPP_THROW(Exception("Can't read public key from " + keyfile.asString() + ", file not found"));
00065
00066 if ( copy( keyfile, _dataFile.path() ) != 0 )
00067 ZYPP_THROW(Exception("Can't copy public key data from " + keyfile.asString() + " to " + _dataFile.path().asString() ));
00068
00069 readFromFile();
00070 }
00071
00072 Impl( const filesystem::TmpFile & sharedfile )
00073 : _dataFile( sharedfile )
00074 { readFromFile(); }
00075
00076 public:
00078 static shared_ptr<Impl> nullimpl()
00079 {
00080 static shared_ptr<Impl> _nullimpl( new Impl );
00081 return _nullimpl;
00082 }
00083
00084 std::string asString() const
00085 {
00086 return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
00087 id().c_str(), str::hexstring(created(),8).substr(2).c_str(),
00088 name().c_str(),
00089 fingerprint().c_str(),
00090 daysToLive() );
00091 }
00092
00093 std::string id() const
00094 { return _keyData._id; }
00095
00096 std::string name() const
00097 { return _keyData._name; }
00098
00099 std::string fingerprint() const
00100 { return _keyData._fingerprint; }
00101
00102 std::string gpgPubkeyVersion() const
00103 { return _keyData._id.empty() ? _keyData._id : str::toLower( _keyData._id.substr(8,8) ); }
00104
00105 std::string gpgPubkeyRelease() const
00106 { return _keyData._created ? str::hexstring( _keyData._created ).substr(2) : std::string(); }
00107
00108 Date created() const
00109 { return _keyData._created; }
00110
00111 Date expires() const
00112 { return _keyData._expires; }
00113
00114 std::string expiresAsString() const
00115 {
00116 if ( !_keyData._expires )
00117 {
00118 return _("(does not expire)");
00119 }
00120 std::string ret( _keyData._expires.asString() );
00121 int ttl( daysToLive() );
00122 if ( ttl <= 90 )
00123 {
00124 ret += " ";
00125 if ( ttl < 0 )
00126 {
00127 ret += _("(EXPIRED)");
00128 }
00129 else if ( ttl == 0 )
00130 {
00131 ret += _("(expires within 24h)");
00132 }
00133 else
00134 {
00135 ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
00136 }
00137 }
00138 return ret;
00139 }
00140
00141 Pathname path() const
00142 { return _dataFile.path(); }
00143
00144 bool expired() const
00145 {
00146 Date exp( expires() );
00147 return( exp && exp < Date::now() );
00148 }
00149
00150 int daysToLive() const
00151 {
00152 Date exp( expires() );
00153 if ( ! expires() )
00154 return INT_MAX;
00155 exp -= Date::now();
00156 return exp < 0 ? exp / Date::day - 1 : exp / Date::day;
00157 }
00158
00159 protected:
00160
00161 void readFromFile()
00162 {
00163 PathInfo info( _dataFile.path() );
00164 MIL << "Reading pubkey from " << info.path() << " of size " << info.size() << " and sha1 " << filesystem::checksum(info.path(), "sha1") << endl;
00165
00166 static filesystem::TmpDir dir;
00167 const char* argv[] =
00168 {
00169 "gpg",
00170 "-v",
00171 "--no-default-keyring",
00172 "--fixed-list-mode",
00173 "--with-fingerprint",
00174 "--with-colons",
00175 "--homedir",
00176 dir.path().asString().c_str(),
00177 "--quiet",
00178 "--no-tty",
00179 "--no-greeting",
00180 "--batch",
00181 "--status-fd",
00182 "1",
00183 _dataFile.path().asString().c_str(),
00184 NULL
00185 };
00186
00187 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00188
00189
00190
00191
00192
00193
00194
00195
00196 KeyData keyData;
00197 std::vector<std::string> words;
00198 enum { pNONE, pPUB, pSIG, pFPR, pUID } parseEntry;
00199 bool sawSig = false;
00200 bool sawSub = false;
00201 for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
00202 {
00203 if ( line.empty() )
00204 continue;
00205
00206 if ( sawSub )
00207 {
00208 if ( line[0] == 'p' && line[1] == 'u' && line[2] == 'b' && line[3] == ':' )
00209 sawSub = false;
00210 else
00211 continue;
00212 }
00213
00214
00215 parseEntry = pNONE;
00216 switch ( line[0] )
00217 {
00218 #define DOTEST( C1, C2, C3, E ) case C1: if ( line[1] == C2 && line[2] == C3 && line[3] == ':' ) parseEntry = E; break
00219 DOTEST( 'p', 'u', 'b', pPUB );
00220 DOTEST( 's', 'i', 'g', pSIG );
00221 DOTEST( 'f', 'p', 'r', pFPR );
00222 DOTEST( 'u', 'i', 'd', pUID );
00223 #undef DOTEST
00224 }
00225 if ( parseEntry == pNONE )
00226 {
00227 if ( line[0] == 's' && line[1] == 'u' && line[2] == 'b' && line[3] == ':' )
00228 sawSub = true;
00229 continue;
00230 }
00231
00232 if ( line[line.size()-1] == '\n' )
00233 line.erase( line.size()-1 );
00234
00235 words.clear();
00236 str::splitFields( line, std::back_inserter(words), ":" );
00237
00238 switch ( parseEntry )
00239 {
00240 case pPUB:
00241 keyData = KeyData();
00242 sawSig = false;
00243 keyData._id = words[4];
00244 keyData._name = words[9];
00245 keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
00246 keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
00247 break;
00248
00249 case pSIG:
00250 if ( !sawSig && words[words.size()-2] == "13x" )
00251 {
00252
00253 if ( ! words[5].empty() )
00254 keyData._created = Date(str::strtonum<Date::ValueType>(words[5]));
00255 if ( ! words[6].empty() )
00256 keyData._expires = Date(str::strtonum<Date::ValueType>(words[6]));
00257 sawSig = true;
00258 }
00259 break;
00260
00261 case pFPR:
00262 if ( ! words[9].empty() )
00263 keyData._fingerprint = words[9];
00264 break;
00265
00266 case pUID:
00267 if ( ! words[9].empty() )
00268 keyData._name = words[9];
00269 break;
00270
00271 case pNONE:
00272 break;
00273 }
00274 }
00275 prog.close();
00276
00277 if ( keyData._id.empty() )
00278 ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
00279
00280
00281 str::replaceAll( keyData._name, "\\x3a", ":" );
00282
00283 _keyData = keyData;
00284 MIL << "Read pubkey from " << info.path() << ": " << asString() << endl;
00285 }
00286
00287 private:
00288 filesystem::TmpFile _dataFile;
00289 KeyData _keyData;
00290
00291 private:
00292 friend Impl * rwcowClone<Impl>( const Impl * rhs );
00294 Impl * clone() const
00295 { return new Impl( *this ); }
00296 };
00298
00300
00301
00302
00304 PublicKey::PublicKey()
00305 : _pimpl( Impl::nullimpl() )
00306 {}
00307
00308 PublicKey::PublicKey( const Pathname & file )
00309 : _pimpl( new Impl(file) )
00310 {}
00311
00312 PublicKey::PublicKey( const filesystem::TmpFile & sharedfile )
00313 : _pimpl( new Impl(sharedfile) )
00314 {}
00315
00316 PublicKey::~PublicKey()
00317 {}
00318
00319 std::string PublicKey::asString() const
00320 { return _pimpl->asString(); }
00321
00322 std::string PublicKey::armoredData() const
00323 { return std::string(); }
00324
00325 std::string PublicKey::id() const
00326 { return _pimpl->id(); }
00327
00328 std::string PublicKey::name() const
00329 { return _pimpl->name(); }
00330
00331 std::string PublicKey::fingerprint() const
00332 { return _pimpl->fingerprint(); }
00333
00334 std::string PublicKey::gpgPubkeyVersion() const
00335 { return _pimpl->gpgPubkeyVersion(); }
00336
00337 std::string PublicKey::gpgPubkeyRelease() const
00338 { return _pimpl->gpgPubkeyRelease(); }
00339
00340 Date PublicKey::created() const
00341 { return _pimpl->created(); }
00342
00343 Date PublicKey::expires() const
00344 { return _pimpl->expires(); }
00345
00346 std::string PublicKey::expiresAsString() const
00347 { return _pimpl->expiresAsString(); }
00348
00349 bool PublicKey::expired() const
00350 { return _pimpl->expired(); }
00351
00352 int PublicKey::daysToLive() const
00353 { return _pimpl->daysToLive(); }
00354
00355 Pathname PublicKey::path() const
00356 { return _pimpl->path(); }
00357
00358 bool PublicKey::operator==( PublicKey b ) const
00359 {
00360 return ( b.id() == id()
00361 && b.fingerprint() == fingerprint()
00362 && b.created() == created() );
00363 }
00364
00365 bool PublicKey::operator==( std::string sid ) const
00366 {
00367 return sid == id();
00368 }
00369
00370 std::ostream & dumpOn( std::ostream & str, const PublicKey & obj )
00371 {
00372 str << "[" << obj.name() << "]" << endl;
00373 str << " fpr " << obj.fingerprint() << endl;
00374 str << " id " << obj.id() << endl;
00375 str << " cre " << obj.created() << endl;
00376 str << " exp " << obj.expiresAsString() << endl;
00377 str << " ttl " << obj.daysToLive() << endl;
00378 str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
00379 str << "]";
00380 return str;
00381 }
00382
00384 }