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 delete proc;
00271 }
00272 }
00273
00274 void CertificateInfoWidgetImpl::slotCollectStdout(KProcess *, char *buffer, int buflen)
00275 {
00276 mDumpOutput += QCString(buffer, buflen+1);
00277 }
00278
00279 void CertificateInfoWidgetImpl::slotCollectStderr(KProcess *, char *buffer, int buflen)
00280 {
00281 mDumpError += QCString(buffer, buflen+1);
00282 }
00283
00284 void CertificateInfoWidgetImpl::slotDumpProcessExited(KProcess* proc) {
00285 int rc = ( proc->normalExit() ) ? proc->exitStatus() : -1 ;
00286
00287 if ( rc == 0 ) {
00288 dumpView->setText( QString::fromUtf8( mDumpOutput ) );
00289 } else {
00290 if ( !mDumpError.isEmpty() ) {
00291 dumpView->setText( QString::fromUtf8( mDumpError ) );
00292 } else
00293 {
00294 QString wmsg = i18n("Failed to execute gpgsm:\n%1");
00295 if ( rc == -1 )
00296 wmsg = wmsg.arg( i18n( "program cannot be executed" ) );
00297 else
00298 wmsg = wmsg.arg( strerror(rc) );
00299 dumpView->setText( wmsg );
00300 }
00301 }
00302
00303 proc->deleteLater();
00304 }
00305
00306 void CertificateInfoWidgetImpl::slotNextKey( const GpgME::Key & key ) {
00307 kdDebug() << "CertificateInfoWidgetImpl::slotNextKey( \""
00308 << key.userID(0).id() << "\" )" << endl;
00309 if ( key.isNull() )
00310 return;
00311
00312 mFoundIssuer = true;
00313 mChain.push_front( key );
00314 updateChainView();
00315
00316 }
00317
00318 void CertificateInfoWidgetImpl::updateChainView() {
00319 pathView->clear();
00320 if ( mChain.empty() )
00321 return;
00322 QListViewItem * item = 0;
00323
00324 QValueList<GpgME::Key>::const_iterator it = mChain.begin();
00325
00326 if ( (*it).chainID() && qstrcmp( (*it).chainID(), (*it).primaryFingerprint() ) == 0 )
00327 item = new QListViewItem( pathView, Kleo::DN( (*it++).userID(0).id() ).prettyDN() );
00328 else {
00329 item = new QListViewItem( pathView, i18n("Issuer certificate not found ( %1)")
00330 .arg( Kleo::DN( (*it).issuerName() ).prettyDN() ) );
00331 item->setOpen( true );
00332 item->setEnabled( false );
00333 }
00334 item->setOpen( true );
00335
00336
00337 while ( it != mChain.end() ) {
00338 item = new QListViewItem( item, Kleo::DN( (*it++).userID(0).id() ).prettyDN() );
00339 item->setOpen( true );
00340 }
00341 }
00342
00343 void CertificateInfoWidgetImpl::slotCertificateChainListingResult( const GpgME::KeyListResult & res ) {
00344 if ( res.error() )
00345 return showChainListError( this, res.error(), mChain.front().issuerName() );
00346 else
00347 startCertificateChainListing();
00348 }
00349
00350 void CertificateInfoWidgetImpl::slotShowInfo( QListViewItem * item ) {
00351 textView->setText( item->text(1) );
00352 }
00353
00354 void CertificateInfoWidgetImpl::slotShowCertPathDetails( QListViewItem * item ) {
00355 if ( !item )
00356 return;
00357
00358
00359
00360
00361
00362 unsigned int totalCount = 0;
00363 int itemIndex = -1;
00364 for ( const QListViewItem * i = pathView->firstChild() ; i ; i = i->firstChild() ) {
00365 if ( i == item )
00366 itemIndex = totalCount;
00367 ++totalCount;
00368 }
00369
00370 assert( totalCount == mChain.size() || totalCount == mChain.size() + 1 );
00371
00372
00373 if ( totalCount == mChain.size() + 1 )
00374 --itemIndex;
00375
00376 assert( itemIndex >= 0 );
00377
00378 KDialogBase * dialog =
00379 new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"),
00380 KDialogBase::Close, KDialogBase::Close );
00381 CertificateInfoWidgetImpl * top =
00382 new CertificateInfoWidgetImpl( mChain[itemIndex], mExternal, dialog );
00383 dialog->setMainWidget( top );
00384
00385 connect( top, SIGNAL(requestCertificateDownload(const QString&, const QString&)),
00386 SIGNAL(requestCertificateDownload(const QString&, const QString&)) );
00387 dialog->show();
00388 }
00389
00390
00391 void CertificateInfoWidgetImpl::slotImportCertificate()
00392 {
00393 if ( mChain.empty() || mChain.back().isNull() )
00394 return;
00395 const Kleo::DN dn = mChain.back().userID( 0 ).id();
00396 emit requestCertificateDownload( mChain.back().primaryFingerprint(), dn.prettyDN() );
00397 importButton->setEnabled( false );
00398 }
00399
00400 void CertificateInfoWidgetImpl::startKeyExistanceCheck() {
00401 if ( !mExternal )
00402
00403 return;
00404 if ( mChain.empty() || mChain.back().isNull() )
00405
00406 return;
00407 const QString fingerprint = mChain.back().primaryFingerprint();
00408 if ( fingerprint.isEmpty() )
00409
00410 return;
00411
00412
00413 Kleo::KeyListJob * job =
00414 Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( false );
00415 assert( job );
00416
00417 connect( job, SIGNAL(nextKey(const GpgME::Key&)),
00418 SLOT(slotKeyExistanceCheckNextCandidate(const GpgME::Key&)) );
00419 connect( job, SIGNAL(result(const GpgME::KeyListResult&)),
00420 SLOT(slotKeyExistanceCheckFinished()) );
00421
00422 job->start( fingerprint );
00423 }
00424
00425 void CertificateInfoWidgetImpl::slotKeyExistanceCheckNextCandidate( const GpgME::Key & key ) {
00426 if ( key.isNull() || mChain.empty() || !key.primaryFingerprint() )
00427 return;
00428
00429 if ( qstrcmp( key.primaryFingerprint(),
00430 mChain.back().primaryFingerprint() ) == 0 )
00431 mHaveKeyLocally = true;
00432 }
00433
00434 void CertificateInfoWidgetImpl::slotKeyExistanceCheckFinished() {
00435 importButton->setEnabled( !mHaveKeyLocally );
00436 }
00437
00438
00439 #include "certificateinfowidgetimpl.moc"