00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #ifdef HAVE_CONFIG_H
00034 #include <config.h>
00035 #endif
00036
00037 #include "certificateinfowidgetimpl.h"
00038
00039
00040 #include <kleo/keylistjob.h>
00041 #include <kleo/dn.h>
00042 #include <kleo/cryptobackendfactory.h>
00043
00044 #include <ui/progressdialog.h>
00045
00046
00047 #include <gpgmepp/keylistresult.h>
00048
00049
00050 #include <klocale.h>
00051 #include <kdialogbase.h>
00052 #include <kmessagebox.h>
00053 #include <kdebug.h>
00054 #include <kprocio.h>
00055 #include <kglobalsettings.h>
00056
00057
00058 #include <qlistview.h>
00059 #include <qtextedit.h>
00060 #include <qheader.h>
00061 #include <qpushbutton.h>
00062 #include <qcursor.h>
00063 #include <qapplication.h>
00064 #include <qdatetime.h>
00065
00066
00067 #include <assert.h>
00068 #include <qtextcodec.h>
00069
00070 CertificateInfoWidgetImpl::CertificateInfoWidgetImpl( const GpgME::Key & key, bool external,
00071 QWidget * parent, const char * name )
00072 : CertificateInfoWidget( parent, name ),
00073 mExternal( external ),
00074 mFoundIssuer( true ),
00075 mHaveKeyLocally( false )
00076 {
00077 importButton->setEnabled( false );
00078
00079 listView->setColumnWidthMode( 1, QListView::Maximum );
00080 QFontMetrics fm = fontMetrics();
00081 listView->setColumnWidth( 1, fm.width( i18n("Information") ) * 5 );
00082
00083 listView->header()->setClickEnabled( false );
00084 listView->setSorting( -1 );
00085
00086 connect( listView, SIGNAL( selectionChanged( QListViewItem* ) ),
00087 this, SLOT( slotShowInfo( QListViewItem* ) ) );
00088 pathView->setColumnWidthMode( 0, QListView::Maximum );
00089 pathView->header()->hide();
00090
00091 connect( pathView, SIGNAL( doubleClicked( QListViewItem* ) ),
00092 this, SLOT( slotShowCertPathDetails( QListViewItem* ) ) );
00093 connect( pathView, SIGNAL( returnPressed( QListViewItem* ) ),
00094 this, SLOT( slotShowCertPathDetails( QListViewItem* ) ) );
00095 connect( importButton, SIGNAL( clicked() ),
00096 this, SLOT( slotImportCertificate() ) );
00097
00098 dumpView->setFont( KGlobalSettings::fixedFont() );
00099
00100 if ( !key.isNull() )
00101 setKey( key );
00102 }
00103
00104 static QString time_t2string( time_t t ) {
00105 QDateTime dt;
00106 dt.setTime_t( t );
00107 return dt.toString();
00108 }
00109
00110 void CertificateInfoWidgetImpl::setKey( const GpgME::Key & key ) {
00111 mChain.clear();
00112 mFoundIssuer = true;
00113 mHaveKeyLocally = false;
00114
00115 listView->clear();
00116 pathView->clear();
00117 importButton->setEnabled( false );
00118
00119 if ( key.isNull() )
00120 return;
00121
00122 mChain.push_front( key );
00123 startKeyExistanceCheck();
00124
00125
00126 QListViewItem * item = 0;
00127 item = new QListViewItem( listView, item, i18n("Valid"), QString("From %1 to %2")
00128 .arg( time_t2string( key.subkey(0).creationTime() ),
00129 time_t2string( key.subkey(0).expirationTime() ) ) );
00130 item = new QListViewItem( listView, item, i18n("Can be used for signing"),
00131 key.canSign() ? i18n("Yes") : i18n("No") );
00132 item = new QListViewItem( listView, item, i18n("Can be used for encryption"),
00133 key.canEncrypt() ? i18n("Yes") : i18n("No") );
00134 item = new QListViewItem( listView, item, i18n("Can be used for certification"),
00135 key.canCertify() ? i18n("Yes") : i18n("No") );
00136 item = new QListViewItem( listView, item, i18n("Can be used for authentication"),
00137 key.canAuthenticate() ? i18n("Yes") : i18n("No" ) );
00138 item = new QListViewItem( listView, item, i18n("Fingerprint"), key.primaryFingerprint() );
00139 item = new QListViewItem( listView, item, i18n("Issuer"), Kleo::DN( key.issuerName() ).prettyDN() );
00140 item = new QListViewItem( listView, item, i18n("Serial Number"), key.issuerSerial() );
00141
00142 const Kleo::DN dn = key.userID(0).id();
00143
00144
00145 static QMap<QString,QString> dnComponentNames;
00146 if ( dnComponentNames.isEmpty() ) {
00147 dnComponentNames["C"] = i18n("Country");
00148 dnComponentNames["OU"] = i18n("Organizational Unit");
00149 dnComponentNames["O"] = i18n("Organization");
00150 dnComponentNames["L"] = i18n("Location");
00151 dnComponentNames["CN"] = i18n("Common Name");
00152 dnComponentNames["EMAIL"] = i18n("Email");
00153 }
00154
00155 for ( Kleo::DN::const_iterator dnit = dn.begin() ; dnit != dn.end() ; ++dnit ) {
00156 QString displayName = (*dnit).name();
00157 if( dnComponentNames.contains(displayName) ) displayName = dnComponentNames[displayName];
00158 item = new QListViewItem( listView, item, displayName, (*dnit).value() );
00159 }
00160
00161 const std::vector<GpgME::UserID> uids = key.userIDs();
00162 if ( !uids.empty() ) {
00163 item = new QListViewItem( listView, item, i18n("Subject"),
00164 Kleo::DN( uids.front().id() ).prettyDN() );
00165 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() + 1 ; it != uids.end() ; ++it ) {
00166 if ( !(*it).id() )
00167 continue;
00168 const QString email = QString::fromUtf8( (*it).id() ).stripWhiteSpace();
00169 if ( email.isEmpty() )
00170 continue;
00171 if ( email.startsWith( "<" ) )
00172 item = new QListViewItem( listView, item, i18n("Email"),
00173 email.mid( 1, email.length()-2 ) );
00174 else
00175 item = new QListViewItem( listView, item, i18n("A.k.a."), email );
00176 }
00177 }
00178
00179 updateChainView();
00180 startCertificateChainListing();
00181 startCertificateDump();
00182 }
00183
00184 static void showChainListError( QWidget * parent, const GpgME::Error & err, const char * subject ) {
00185 assert( err );
00186 const QString msg = i18n("<qt><p>An error occurred while fetching "
00187 "the certificate <b>%1</b> from the backend:</p>"
00188 "<p><b>%2</b></p></qt>")
00189 .arg( subject ? QString::fromUtf8( subject ) : QString::null,
00190 QString::fromLocal8Bit( err.asString() ) );
00191 KMessageBox::error( parent, msg, i18n("Certificate Listing Failed" ) );
00192 }
00193
00194 void CertificateInfoWidgetImpl::startCertificateChainListing() {
00195 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing()" << endl;
00196
00197 if ( mChain.empty() ) {
00198
00199 kdWarning() << "CertificateInfoWidgetImpl::startCertificateChainListing(): mChain is empty!" << endl;
00200 return;
00201 }
00202 const char * chainID = mChain.front().chainID();
00203 if ( !chainID || !*chainID ) {
00204
00205 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): empty chain ID - root not found" << endl;
00206 return;
00207 }
00208 const char * fpr = mChain.front().primaryFingerprint();
00209 if ( qstricmp( fpr, chainID ) == 0 ) {
00210 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): chain_id equals fingerprint -> found root" << endl;
00211 return;
00212 }
00213 if ( mChain.size() > 100 ) {
00214
00215 kdWarning() << "CertificateInfoWidgetImpl::startCertificateChainListing(): maximum chain length of 100 exceeded!" << endl;
00216 return;
00217 }
00218 if ( !mFoundIssuer ) {
00219
00220 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): issuer not found - giving up" << endl;
00221 return;
00222 }
00223
00224 mFoundIssuer = false;
00225
00226
00227
00228
00229
00230 Kleo::KeyListJob * job =
00231 Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( false );
00232 assert( job );
00233
00234 connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
00235 SLOT(slotCertificateChainListingResult(const GpgME::KeyListResult&)) );
00236 connect( job, SIGNAL(nextKey(const GpgME::Key&)),
00237 SLOT(slotNextKey(const GpgME::Key&)) );
00238
00239 kdDebug() << "Going to fetch" << endl
00240 << " issuer : \"" << mChain.front().issuerName() << "\"" << endl
00241 << " chain id: " << mChain.front().chainID() << endl
00242 << "for" << endl
00243 << " subject : \"" << mChain.front().userID(0).id() << "\"" << endl
00244 << " subj.fpr: " << mChain.front().primaryFingerprint() << endl;
00245
00246 const GpgME::Error err = job->start( mChain.front().chainID() );
00247
00248 if ( err )
00249 showChainListError( this, err, mChain.front().issuerName() );
00250 else
00251 (void)new Kleo::ProgressDialog( job, i18n("Fetching Certificate Chain"), this );
00252 }
00253
00254 void CertificateInfoWidgetImpl::startCertificateDump() {
00255 KProcess* proc = new KProcess( this );
00256 (*proc) << "gpgsm";
00257 (*proc) << "--dump-keys";
00258 (*proc) << mChain.front().primaryFingerprint();
00259
00260 QObject::connect( proc, SIGNAL( receivedStdout(KProcess *, char *, int) ),
00261 this, SLOT( slotCollectStdout(KProcess *, char *, int) ) );
00262 QObject::connect( proc, SIGNAL( receivedStderr(KProcess *, char *, int) ),
00263 this, SLOT( slotCollectStderr(KProcess *, char *, int) ) );
00264 QObject::connect( proc, SIGNAL( processExited(KProcess*) ),
00265 this, SLOT( slotDumpProcessExited(KProcess*) ) );
00266
00267 if ( !proc->start( KProcess::NotifyOnExit, (KProcess::Communication)(KProcess::Stdout | KProcess::Stderr) ) ) {
00268 QString wmsg = i18n("Failed to execute gpgsm:\n%1").arg( i18n( "program not found" ) );
00269 dumpView->setText( wmsg );
00270 }
00271 }
00272
00273 void CertificateInfoWidgetImpl::slotCollectStdout(KProcess *, char *buffer, int buflen)
00274 {
00275 mDumpOutput += QCString(buffer, buflen+1);
00276 }
00277
00278 void CertificateInfoWidgetImpl::slotCollectStderr(KProcess *, char *buffer, int buflen)
00279 {
00280 mDumpError += QCString(buffer, buflen+1);
00281 }
00282
00283 void CertificateInfoWidgetImpl::slotDumpProcessExited(KProcess* proc) {
00284 int rc = ( proc->normalExit() ) ? proc->exitStatus() : -1 ;
00285
00286 if ( rc == 0 ) {
00287 dumpView->setText( QString::fromUtf8( mDumpOutput ) );
00288 } else {
00289 if ( !mDumpError.isEmpty() ) {
00290 dumpView->setText( QString::fromUtf8( mDumpError ) );
00291 } else
00292 {
00293 QString wmsg = i18n("Failed to execute gpgsm:\n%1");
00294 if ( rc == -1 )
00295 wmsg = wmsg.arg( i18n( "program cannot be executed" ) );
00296 else
00297 wmsg = wmsg.arg( strerror(rc) );
00298 dumpView->setText( wmsg );
00299 }
00300 }
00301
00302 proc->deleteLater();
00303 }
00304
00305 void CertificateInfoWidgetImpl::slotNextKey( const GpgME::Key & key ) {
00306 kdDebug() << "CertificateInfoWidgetImpl::slotNextKey( \""
00307 << key.userID(0).id() << "\" )" << endl;
00308 if ( key.isNull() )
00309 return;
00310
00311 mFoundIssuer = true;
00312 mChain.push_front( key );
00313 updateChainView();
00314
00315 }
00316
00317 void CertificateInfoWidgetImpl::updateChainView() {
00318 pathView->clear();
00319 if ( mChain.empty() )
00320 return;
00321 QListViewItem * item = 0;
00322
00323 QValueList<GpgME::Key>::const_iterator it = mChain.begin();
00324
00325 if ( (*it).chainID() && qstrcmp( (*it).chainID(), (*it).primaryFingerprint() ) == 0 )
00326 item = new QListViewItem( pathView, Kleo::DN( (*it++).userID(0).id() ).prettyDN() );
00327 else {
00328 item = new QListViewItem( pathView, i18n("Issuer certificate not found ( %1)")
00329 .arg( Kleo::DN( (*it).issuerName() ).prettyDN() ) );
00330 item->setOpen( true );
00331 item->setEnabled( false );
00332 }
00333 item->setOpen( true );
00334
00335
00336 while ( it != mChain.end() ) {
00337 item = new QListViewItem( item, Kleo::DN( (*it++).userID(0).id() ).prettyDN() );
00338 item->setOpen( true );
00339 }
00340 }
00341
00342 void CertificateInfoWidgetImpl::slotCertificateChainListingResult( const GpgME::KeyListResult & res ) {
00343 if ( res.error() )
00344 return showChainListError( this, res.error(), mChain.front().issuerName() );
00345 else
00346 startCertificateChainListing();
00347 }
00348
00349 void CertificateInfoWidgetImpl::slotShowInfo( QListViewItem * item ) {
00350 textView->setText( item->text(1) );
00351 }
00352
00353 void CertificateInfoWidgetImpl::slotShowCertPathDetails( QListViewItem * item ) {
00354 if ( !item )
00355 return;
00356
00357
00358
00359
00360
00361 unsigned int totalCount = 0;
00362 int itemIndex = -1;
00363 for ( const QListViewItem * i = pathView->firstChild() ; i ; i = i->firstChild() ) {
00364 if ( i == item )
00365 itemIndex = totalCount;
00366 ++totalCount;
00367 }
00368
00369 assert( totalCount == mChain.size() || totalCount == mChain.size() + 1 );
00370
00371
00372 if ( totalCount == mChain.size() + 1 )
00373 --itemIndex;
00374
00375 assert( itemIndex >= 0 );
00376
00377 KDialogBase * dialog =
00378 new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"),
00379 KDialogBase::Close, KDialogBase::Close );
00380 CertificateInfoWidgetImpl * top =
00381 new CertificateInfoWidgetImpl( mChain[itemIndex], mExternal, dialog );
00382 dialog->setMainWidget( top );
00383
00384 connect( top, SIGNAL(requestCertificateDownload(const QString&, const QString&)),
00385 SIGNAL(requestCertificateDownload(const QString&, const QString&)) );
00386 dialog->show();
00387 }
00388
00389
00390 void CertificateInfoWidgetImpl::slotImportCertificate()
00391 {
00392 if ( mChain.empty() || mChain.back().isNull() )
00393 return;
00394 const Kleo::DN dn = mChain.back().userID( 0 ).id();
00395 emit requestCertificateDownload( mChain.back().primaryFingerprint(), dn.prettyDN() );
00396 importButton->setEnabled( false );
00397 }
00398
00399 void CertificateInfoWidgetImpl::startKeyExistanceCheck() {
00400 if ( !mExternal )
00401
00402 return;
00403 if ( mChain.empty() || mChain.back().isNull() )
00404
00405 return;
00406 const QString fingerprint = mChain.back().primaryFingerprint();
00407 if ( fingerprint.isEmpty() )
00408
00409 return;
00410
00411
00412 Kleo::KeyListJob * job =
00413 Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( false );
00414 assert( job );
00415
00416 connect( job, SIGNAL(nextKey(const GpgME::Key&)),
00417 SLOT(slotKeyExistanceCheckNextCandidate(const GpgME::Key&)) );
00418 connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
00419 SLOT(slotKeyExistanceCheckFinished()) );
00420
00421 job->start( fingerprint );
00422 }
00423
00424 void CertificateInfoWidgetImpl::slotKeyExistanceCheckNextCandidate( const GpgME::Key & key ) {
00425 if ( key.isNull() || mChain.empty() || !key.primaryFingerprint() )
00426 return;
00427
00428 if ( qstrcmp( key.primaryFingerprint(),
00429 mChain.back().primaryFingerprint() ) == 0 )
00430 mHaveKeyLocally = true;
00431 }
00432
00433 void CertificateInfoWidgetImpl::slotKeyExistanceCheckFinished() {
00434 importButton->setEnabled( !mHaveKeyLocally );
00435 }
00436
00437
00438 #include "certificateinfowidgetimpl.moc"