certmanager Library API Documentation

certificateinfowidgetimpl.cpp

00001 /* 00002 certificateinfowidgetimpl.cpp 00003 00004 This file is part of Kleopatra, the KDE keymanager 00005 Copyright (c) 2001,2002,2004 Klarälvdalens Datakonsult AB 00006 00007 Kleopatra is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 2 of the License, or 00010 (at your option) any later version. 00011 00012 Kleopatra is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software 00019 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 00021 In addition, as a special exception, the copyright holders give 00022 permission to link the code of this program with any edition of 00023 the Qt library by Trolltech AS, Norway (or with modified versions 00024 of Qt that use the same license as Qt), and distribute linked 00025 combinations including the two. You must obey the GNU General 00026 Public License in all respects for all of the code used other than 00027 Qt. If you modify this file, you may extend this exception to 00028 your version of the file, but you are not obligated to do so. If 00029 you do not wish to do so, delete this exception statement from 00030 your version. 00031 */ 00032 00033 #ifdef HAVE_CONFIG_H 00034 #include <config.h> 00035 #endif 00036 00037 #include "certificateinfowidgetimpl.h" 00038 00039 // libkleopatra 00040 #include <kleo/keylistjob.h> 00041 #include <kleo/dn.h> 00042 #include <kleo/cryptobackendfactory.h> 00043 00044 #include <ui/progressdialog.h> 00045 00046 // gpgme++ 00047 #include <gpgmepp/keylistresult.h> 00048 00049 // KDE 00050 #include <klocale.h> 00051 #include <kdialogbase.h> 00052 #include <kmessagebox.h> 00053 #include <kdebug.h> 00054 #include <kprocio.h> 00055 00056 // Qt 00057 #include <qlistview.h> 00058 #include <qtextedit.h> 00059 #include <qheader.h> 00060 #include <qpushbutton.h> 00061 #include <qcursor.h> 00062 #include <qapplication.h> 00063 #include <qdatetime.h> 00064 00065 // other 00066 #include <assert.h> 00067 #include <qtextcodec.h> 00068 00069 CertificateInfoWidgetImpl::CertificateInfoWidgetImpl( const GpgME::Key & key, bool external, 00070 QWidget * parent, const char * name ) 00071 : CertificateInfoWidget( parent, name ), 00072 mExternal( external ), 00073 mFoundIssuer( true ), 00074 mHaveKeyLocally( false ) 00075 { 00076 importButton->setEnabled( false ); 00077 00078 listView->setColumnWidthMode( 1, QListView::Maximum ); 00079 QFontMetrics fm = fontMetrics(); 00080 listView->setColumnWidth( 1, fm.width( i18n("Information") ) * 5 ); 00081 00082 listView->header()->setClickEnabled( false ); 00083 listView->setSorting( -1 ); 00084 00085 connect( listView, SIGNAL( selectionChanged( QListViewItem* ) ), 00086 this, SLOT( slotShowInfo( QListViewItem* ) ) ); 00087 pathView->setColumnWidthMode( 0, QListView::Maximum ); 00088 pathView->header()->hide(); 00089 00090 connect( pathView, SIGNAL( doubleClicked( QListViewItem* ) ), 00091 this, SLOT( slotShowCertPathDetails( QListViewItem* ) ) ); 00092 connect( pathView, SIGNAL( returnPressed( QListViewItem* ) ), 00093 this, SLOT( slotShowCertPathDetails( QListViewItem* ) ) ); 00094 connect( importButton, SIGNAL( clicked() ), 00095 this, SLOT( slotImportCertificate() ) ); 00096 00097 if ( !key.isNull() ) 00098 setKey( key ); 00099 } 00100 00101 static QString time_t2string( time_t t ) { 00102 QDateTime dt; 00103 dt.setTime_t( t ); 00104 return dt.toString(); 00105 } 00106 00107 void CertificateInfoWidgetImpl::setKey( const GpgME::Key & key ) { 00108 mChain.clear(); 00109 mFoundIssuer = true; 00110 mHaveKeyLocally = false; 00111 00112 listView->clear(); 00113 pathView->clear(); 00114 importButton->setEnabled( false ); 00115 00116 if ( key.isNull() ) 00117 return; 00118 00119 mChain.push_front( key ); 00120 startKeyExistanceCheck(); // starts a local keylisting to enable the 00121 // importButton if needed 00122 00123 QListViewItem * item = 0; 00124 item = new QListViewItem( listView, item, i18n("Valid"), QString("From %1 to %2") 00125 .arg( time_t2string( key.subkey(0).creationTime() ), 00126 time_t2string( key.subkey(0).expirationTime() ) ) ); 00127 item = new QListViewItem( listView, item, i18n("Can be used for signing"), 00128 key.canSign() ? i18n("Yes") : i18n("No") ); 00129 item = new QListViewItem( listView, item, i18n("Can be used for encryption"), 00130 key.canEncrypt() ? i18n("Yes") : i18n("No") ); 00131 item = new QListViewItem( listView, item, i18n("Can be used for certification"), 00132 key.canCertify() ? i18n("Yes") : i18n("No") ); 00133 item = new QListViewItem( listView, item, i18n("Can be used for authentication"), 00134 key.canAuthenticate() ? i18n("Yes") : i18n("No" ) ); 00135 item = new QListViewItem( listView, item, i18n("Fingerprint"), key.subkey(0).fingerprint() ); 00136 item = new QListViewItem( listView, item, i18n("Issuer"), Kleo::DN( key.issuerName() ).prettyDN() ); 00137 item = new QListViewItem( listView, item, i18n("Serial Number"), key.issuerSerial() ); 00138 00139 const Kleo::DN dn = key.userID(0).id(); 00140 00141 // FIXME: use the attributeLabelMap from certificatewizardimpl.cpp: 00142 static QMap<QString,QString> dnComponentNames; 00143 if ( dnComponentNames.isEmpty() ) { 00144 dnComponentNames["C"] = i18n("Country"); 00145 dnComponentNames["OU"] = i18n("Organizational Unit"); 00146 dnComponentNames["O"] = i18n("Organization"); 00147 dnComponentNames["L"] = i18n("Location"); 00148 dnComponentNames["CN"] = i18n("Common Name"); 00149 dnComponentNames["EMAIL"] = i18n("Email"); 00150 } 00151 00152 for ( Kleo::DN::const_iterator dnit = dn.begin() ; dnit != dn.end() ; ++dnit ) { 00153 QString displayName = (*dnit).name(); 00154 if( dnComponentNames.contains(displayName) ) displayName = dnComponentNames[displayName]; 00155 item = new QListViewItem( listView, item, displayName, (*dnit).value() ); 00156 } 00157 00158 const std::vector<GpgME::UserID> uids = key.userIDs(); 00159 if ( !uids.empty() ) { 00160 item = new QListViewItem( listView, item, i18n("Subject"), 00161 Kleo::DN( uids.front().id() ).prettyDN() ); 00162 for ( std::vector<GpgME::UserID>::const_iterator it = uids.begin() + 1 ; it != uids.end() ; ++it ) { 00163 if ( !(*it).id() ) 00164 continue; 00165 const QString email = QString::fromUtf8( (*it).id() ).stripWhiteSpace(); 00166 if ( email.isEmpty() ) 00167 continue; 00168 if ( email.startsWith( "<" ) ) 00169 item = new QListViewItem( listView, item, i18n("Email"), 00170 email.mid( 1, email.length()-2 ) ); 00171 else 00172 item = new QListViewItem( listView, item, i18n("A.k.a."), email ); 00173 } 00174 } 00175 00176 updateChainView(); 00177 startCertificateChainListing(); 00178 startCertificateDump(); 00179 } 00180 00181 static void showChainListError( QWidget * parent, const GpgME::Error & err, const char * subject ) { 00182 assert( err ); 00183 const QString msg = i18n("<qt><p>An error occurred while fetching " 00184 "the certificate <b>%1</b> from the backend:</p>" 00185 "<p><b>%2</b></p></qt>") 00186 .arg( subject ? QString::fromUtf8( subject ) : QString::null, 00187 QString::fromLocal8Bit( err.asString() ) ); 00188 KMessageBox::error( parent, msg, i18n("Certificate Listing Failed" ) ); 00189 } 00190 00191 void CertificateInfoWidgetImpl::startCertificateChainListing() { 00192 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing()" << endl; 00193 00194 if ( mChain.empty() ) { 00195 // we need a seed... 00196 kdWarning() << "CertificateInfoWidgetImpl::startCertificateChainListing(): mChain is empty!" << endl; 00197 return; 00198 } 00199 const char * chainID = mChain.front().chainID(); 00200 if ( !chainID || !*chainID ) { 00201 // cert not found: 00202 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): empty chain ID - root not found" << endl; 00203 return; 00204 } 00205 const char * fpr = mChain.front().subkey(0).fingerprint(); 00206 if ( qstricmp( fpr, chainID ) == 0 ) { 00207 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): chain_id equals fingerprint -> found root" << endl; 00208 return; 00209 } 00210 if ( mChain.size() > 100 ) { 00211 // safe guard against certificate loops (paranoia factor 8 out of 10)... 00212 kdWarning() << "CertificateInfoWidgetImpl::startCertificateChainListing(): maximum chain length of 100 exceeded!" << endl; 00213 return; 00214 } 00215 if ( !mFoundIssuer ) { 00216 // key listing failed. Don't end up in endless loop 00217 kdDebug() << "CertificateInfoWidgetImpl::startCertificateChainListing(): issuer not found - giving up" << endl; 00218 return; 00219 } 00220 00221 mFoundIssuer = false; 00222 00223 // gpgsm / dirmngr / LDAP / whoever doesn't support looking up 00224 // external keys by fingerprint. Furthermore, since we actually got 00225 // a chain-id set on the key, we know that we have the issuer's cert 00226 // in the local keyring, so just use local keylisting. 00227 Kleo::KeyListJob * job = 00228 Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( false ); 00229 assert( job ); 00230 00231 connect( job, SIGNAL(result(const GpgME::KeyListResult&)), 00232 SLOT(slotCertificateChainListingResult(const GpgME::KeyListResult&)) ); 00233 connect( job, SIGNAL(nextKey(const GpgME::Key&)), 00234 SLOT(slotNextKey(const GpgME::Key&)) ); 00235 00236 kdDebug() << "Going to fetch" << endl 00237 << " issuer : \"" << mChain.front().issuerName() << "\"" << endl 00238 << " chain id: " << mChain.front().chainID() << endl 00239 << "for" << endl 00240 << " subject : \"" << mChain.front().userID(0).id() << "\"" << endl 00241 << " subj.fpr: " << mChain.front().subkey(0).fingerprint() << endl; 00242 00243 const GpgME::Error err = job->start( mChain.front().chainID() ); 00244 00245 if ( err ) 00246 showChainListError( this, err, mChain.front().issuerName() ); 00247 else 00248 (void)new Kleo::ProgressDialog( job, i18n("Fetching Certificate Chain"), this ); 00249 } 00250 00251 void CertificateInfoWidgetImpl::startCertificateDump() { 00252 KProcess* proc = new KProcess( this ); 00253 (*proc) << "gpgsm"; // must be in the PATH 00254 (*proc) << "--dump-keys"; 00255 (*proc) << mChain.front().subkey(0).fingerprint(); 00256 00257 QObject::connect( proc, SIGNAL( receivedStdout(KProcess *, char *, int) ), 00258 this, SLOT( slotCollectStdout(KProcess *, char *, int) ) ); 00259 QObject::connect( proc, SIGNAL( receivedStderr(KProcess *, char *, int) ), 00260 this, SLOT( slotCollectStderr(KProcess *, char *, int) ) ); 00261 QObject::connect( proc, SIGNAL( processExited(KProcess*) ), 00262 this, SLOT( slotDumpProcessExited(KProcess*) ) ); 00263 00264 if ( !proc->start( KProcess::NotifyOnExit, (KProcess::Communication)(KProcess::Stdout | KProcess::Stderr) ) ) { 00265 QString wmsg = i18n("Failed to execute gpgsm:\n%1").arg( i18n( "program not found" ) ); 00266 dumpView->setText( wmsg ); 00267 } 00268 } 00269 00270 void CertificateInfoWidgetImpl::slotCollectStdout(KProcess *, char *buffer, int buflen) 00271 { 00272 mDumpOutput += QCString(buffer, buflen+1); // like KProcIO does 00273 } 00274 00275 void CertificateInfoWidgetImpl::slotCollectStderr(KProcess *, char *buffer, int buflen) 00276 { 00277 mDumpError += QCString(buffer, buflen+1); // like KProcIO does 00278 } 00279 00280 void CertificateInfoWidgetImpl::slotDumpProcessExited(KProcess* proc) { 00281 int rc = ( proc->normalExit() ) ? proc->exitStatus() : -1 ; 00282 00283 if ( rc == 0 ) { 00284 dumpView->setText( QString::fromUtf8( mDumpOutput ) ); 00285 } else { 00286 if ( !mDumpError.isEmpty() ) { 00287 dumpView->setText( QString::fromUtf8( mDumpError ) ); 00288 } else 00289 { 00290 QString wmsg = i18n("Failed to execute gpgsm:\n%1"); 00291 if ( rc == -1 ) 00292 wmsg = wmsg.arg( i18n( "program cannot be executed" ) ); 00293 else 00294 wmsg = wmsg.arg( strerror(rc) ); 00295 dumpView->setText( wmsg ); 00296 } 00297 } 00298 00299 proc->deleteLater(); 00300 } 00301 00302 void CertificateInfoWidgetImpl::slotNextKey( const GpgME::Key & key ) { 00303 kdDebug() << "CertificateInfoWidgetImpl::slotNextKey( \"" 00304 << key.userID(0).id() << "\" )" << endl; 00305 if ( key.isNull() ) 00306 return; 00307 00308 mFoundIssuer = true; 00309 mChain.push_front( key ); 00310 updateChainView(); 00311 // FIXME: cancel the keylisting. We're only interested in _one_ key. 00312 } 00313 00314 void CertificateInfoWidgetImpl::updateChainView() { 00315 pathView->clear(); 00316 if ( mChain.empty() ) 00317 return; 00318 QListViewItem * item = 0; 00319 00320 QValueList<GpgME::Key>::const_iterator it = mChain.begin(); 00321 // root item: 00322 if ( (*it).chainID() && qstrcmp( (*it).chainID(), (*it).subkey(0).fingerprint() ) == 0 ) 00323 item = new QListViewItem( pathView, Kleo::DN( (*it++).userID(0).id() ).prettyDN() ); 00324 else { 00325 item = new QListViewItem( pathView, i18n("Issuer certificate not found ( %1)") 00326 .arg( Kleo::DN( (*it).issuerName() ).prettyDN() ) ); 00327 item->setOpen( true ); // Qt bug: doesn't open after setEnabled( false ) :/ 00328 item->setEnabled( false ); 00329 } 00330 item->setOpen( true ); 00331 00332 // subsequent items: 00333 while ( it != mChain.end() ) { 00334 item = new QListViewItem( item, Kleo::DN( (*it++).userID(0).id() ).prettyDN() ); 00335 item->setOpen( true ); 00336 } 00337 } 00338 00339 void CertificateInfoWidgetImpl::slotCertificateChainListingResult( const GpgME::KeyListResult & res ) { 00340 if ( res.error() ) 00341 return showChainListError( this, res.error(), mChain.front().issuerName() ); 00342 else 00343 startCertificateChainListing(); 00344 } 00345 00346 void CertificateInfoWidgetImpl::slotShowInfo( QListViewItem * item ) { 00347 textView->setText( item->text(1) ); 00348 } 00349 00350 void CertificateInfoWidgetImpl::slotShowCertPathDetails( QListViewItem * item ) { 00351 if ( !item ) 00352 return; 00353 00354 // find the key corresponding to "item". This hack would not be 00355 // necessary if pathView was a Kleo::KeyListView, but it's 00356 // Qt-Designer-generated and I don't feel like creating a custom 00357 // widget spec for Kleo::KeyListView. 00358 unsigned int totalCount = 0; 00359 int itemIndex = -1; 00360 for ( const QListViewItem * i = pathView->firstChild() ; i ; i = i->firstChild() ) { 00361 if ( i == item ) 00362 itemIndex = totalCount; 00363 ++totalCount; 00364 } 00365 00366 assert( totalCount == mChain.size() || totalCount == mChain.size() + 1 ); 00367 00368 // skip pseudo root item with "not found message": 00369 if ( totalCount == mChain.size() + 1 ) 00370 --itemIndex; 00371 00372 assert( itemIndex >= 0 ); 00373 00374 KDialogBase * dialog = 00375 new KDialogBase( this, "dialog", false, i18n("Additional Information for Key"), 00376 KDialogBase::Close, KDialogBase::Close ); 00377 CertificateInfoWidgetImpl * top = 00378 new CertificateInfoWidgetImpl( mChain[itemIndex], mExternal, dialog ); 00379 dialog->setMainWidget( top ); 00380 // proxy the signal to our receiver: 00381 connect( top, SIGNAL(requestCertificateDownload(const QString&, const QString&)), 00382 SIGNAL(requestCertificateDownload(const QString&, const QString&)) ); 00383 dialog->show(); 00384 } 00385 00386 00387 void CertificateInfoWidgetImpl::slotImportCertificate() 00388 { 00389 if ( mChain.empty() || mChain.back().isNull() ) 00390 return; 00391 const Kleo::DN dn = mChain.back().userID( 0 ).id(); 00392 emit requestCertificateDownload( mChain.back().subkey(0).fingerprint(), dn.prettyDN() ); 00393 importButton->setEnabled( false ); 00394 } 00395 00396 void CertificateInfoWidgetImpl::startKeyExistanceCheck() { 00397 if ( !mExternal ) 00398 // we already have it if it's from a local keylisting :) 00399 return; 00400 if ( mChain.empty() || mChain.back().isNull() ) 00401 // need a key to look for 00402 return; 00403 const QString fingerprint = mChain.back().subkey(0).fingerprint(); 00404 if ( fingerprint.isEmpty() ) 00405 // empty pattern means list all keys. We don't want that 00406 return; 00407 00408 // start _local_ keylistjob (no progressdialog needed here): 00409 Kleo::KeyListJob * job = 00410 Kleo::CryptoBackendFactory::instance()->smime()->keyListJob( false ); 00411 assert( job ); 00412 00413 connect( job, SIGNAL(nextKey(const GpgME::Key&)), 00414 SLOT(slotKeyExistanceCheckNextCandidate(const GpgME::Key&)) ); 00415 connect( job, SIGNAL(result(const GpgME::KeyListResult&)), 00416 SLOT(slotKeyExistanceCheckFinished()) ); 00417 // nor to check for errors: 00418 job->start( fingerprint ); 00419 } 00420 00421 void CertificateInfoWidgetImpl::slotKeyExistanceCheckNextCandidate( const GpgME::Key & key ) { 00422 if ( key.isNull() || mChain.empty() || !key.subkey(0).fingerprint() ) 00423 return; 00424 00425 if ( qstrcmp( key.subkey(0).fingerprint(), 00426 mChain.back().subkey(0).fingerprint() ) == 0 ) 00427 mHaveKeyLocally = true; 00428 } 00429 00430 void CertificateInfoWidgetImpl::slotKeyExistanceCheckFinished() { 00431 importButton->setEnabled( !mHaveKeyLocally ); 00432 } 00433 00434 00435 #include "certificateinfowidgetimpl.moc"
KDE Logo
This file is part of the documentation for certmanager Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 27 12:49:28 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003