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