certmanager Library API Documentation

certificatewizardimpl.cpp

00001 /* 00002 certificatewizardimpl.cpp 00003 00004 This file is part of Kleopatra, the KDE keymanager 00005 Copyright (c) 2001,2002,2004 Klar�vdalens 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 "certificatewizardimpl.h" 00038 #include "storedtransferjob.h" 00039 00040 // libkleopatra 00041 #include <kleo/oidmap.h> 00042 #include <kleo/keygenerationjob.h> 00043 #include <kleo/dn.h> 00044 #include <kleo/cryptobackendfactory.h> 00045 00046 #include <ui/progressdialog.h> 00047 00048 // gpgme++ 00049 #include <gpgmepp/keygenerationresult.h> 00050 00051 // KDE 00052 #include <kabc/stdaddressbook.h> 00053 #include <kabc/addressee.h> 00054 00055 #include <kmessagebox.h> 00056 #include <klocale.h> 00057 #include <kapplication.h> 00058 #include <kdebug.h> 00059 #include <kdialog.h> 00060 #include <kurlrequester.h> 00061 #include <kdcopservicestarter.h> 00062 #include <dcopclient.h> 00063 #include <kio/job.h> 00064 #include <kio/netaccess.h> 00065 00066 // Qt 00067 #include <qlineedit.h> 00068 #include <qtextedit.h> 00069 #include <qpushbutton.h> 00070 #include <qcheckbox.h> 00071 #include <qradiobutton.h> 00072 #include <qlayout.h> 00073 #include <qlabel.h> 00074 00075 #include <assert.h> 00076 00077 static QString attributeLabel( const QString & attr, bool required ) { 00078 if ( attr.isEmpty() ) 00079 return QString::null; 00080 const QString label = Kleo::DNAttributeMapper::instance()->name2label( attr ); 00081 if ( !label.isEmpty() ) 00082 if ( required ) 00083 return i18n("Format string for the labels in the \"Your Personal Data\" page - required field", 00084 "*%1 (%2):").arg( label, attr ); 00085 else 00086 return i18n("Format string for the labels in the \"Your Personal Data\" page", 00087 "%1 (%2):").arg( label, attr ); 00088 00089 else if ( required ) 00090 return '*' + attr + ':'; 00091 else 00092 return attr + ':'; 00093 } 00094 00095 static QString attributeFromKey( QString key ) { 00096 return key.remove( '!' ); 00097 } 00098 00099 static bool availForMod( const QLineEdit * le ) { 00100 return le && le->isEnabled(); 00101 } 00102 00103 /* 00104 * Constructs a CertificateWizardImpl which is a child of 'parent', with the 00105 * name 'name' and widget flags set to 'f' 00106 * 00107 * The wizard will by default be modeless, unless you set 'modal' to 00108 * TRUE to construct a modal wizard. 00109 */ 00110 CertificateWizardImpl::CertificateWizardImpl( QWidget* parent, const char* name, bool modal, WFlags fl ) 00111 : CertificateWizard( parent, name, modal, fl ) 00112 { 00113 // don't allow to go to last page until a key has been generated 00114 setNextEnabled( generatePage, false ); 00115 // setNextEnabled( personalDataPage, false ); // ## disable again once we have a criteria when to enable again 00116 00117 createPersonalDataPage(); 00118 00119 // Allow to select remote URLs 00120 storeUR->setMode( KFile::File ); 00121 storeUR->setFilter( "application/pkcs10" ); 00122 connect( storeUR, SIGNAL( urlSelected( const QString& ) ), 00123 this, SLOT( slotURLSelected( const QString& ) ) ); 00124 00125 const KConfigGroup config( KGlobal::config(), "CertificateCreationWizard" ); 00126 caEmailED->setText( config.readEntry( "CAEmailAddress" ) ); 00127 00128 connect( this, SIGNAL( helpClicked() ), 00129 this, SLOT( slotHelpClicked() ) ); 00130 connect( insertAddressButton, SIGNAL( clicked() ), 00131 this, SLOT( slotSetValuesFromWhoAmI() ) ); 00132 } 00133 00134 static bool requirementsAreMet( const CertificateWizardImpl::AttrPairList & list ) { 00135 for ( CertificateWizardImpl::AttrPairList::const_iterator it = list.begin() ; 00136 it != list.end() ; ++it ) { 00137 const QLineEdit * le = (*it).second; 00138 if ( !le ) 00139 continue; 00140 const QString key = (*it).first; 00141 #ifndef NDEBUG 00142 kdbgstream s = kdDebug(); 00143 #else 00144 kndbgstream s = kdDebug(); 00145 #endif 00146 s << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\": "; 00147 if ( key.endsWith("!") && le->text().stripWhiteSpace().isEmpty() ) { 00148 s << "required field is empty!" << endl; 00149 return false; 00150 } 00151 s << "ok" << endl; 00152 } 00153 return true; 00154 } 00155 00156 /* 00157 This slot is called when the user changes the text. 00158 */ 00159 void CertificateWizardImpl::slotEnablePersonalDataPageExit() { 00160 setNextEnabled( personalDataPage, requirementsAreMet( _attrPairList ) ); 00161 } 00162 00163 00164 /* 00165 * Destroys the object and frees any allocated resources 00166 */ 00167 CertificateWizardImpl::~CertificateWizardImpl() 00168 { 00169 // no need to delete child widgets, Qt does it all for us 00170 } 00171 00172 static const char * oidForAttributeName( const QString & attr ) { 00173 QCString attrUtf8 = attr.utf8(); 00174 for ( unsigned int i = 0 ; i < numOidMaps ; ++i ) 00175 if ( qstricmp( attrUtf8, oidmap[i].name ) == 0 ) 00176 return oidmap[i].oid; 00177 return 0; 00178 } 00179 00180 /* 00181 * protected slot 00182 */ 00183 void CertificateWizardImpl::slotGenerateCertificate() 00184 { 00185 // Ask gpgme to generate a key and return it 00186 QString certParms; 00187 certParms += "<GnupgKeyParms format=\"internal\">\n"; 00188 certParms += "Key-Type: RSA\n"; 00189 certParms += "Key-Length: 1024\n"; // PENDING(NN) Might want to make this user-configurable 00190 certParms += "Key-Usage: "; 00191 if ( signOnlyCB->isChecked() ) 00192 certParms += "Sign"; 00193 else if ( encryptOnlyCB->isChecked() ) 00194 certParms += "Encrypt"; 00195 else 00196 certParms += "Sign, Encrypt"; 00197 certParms += "\n"; 00198 certParms += "name-dn: "; 00199 00200 QString email; 00201 QStringList rdns; 00202 for( AttrPairList::const_iterator it = _attrPairList.begin(); it != _attrPairList.end(); ++it ) { 00203 const QString attr = attributeFromKey( (*it).first.upper() ); 00204 const QLineEdit * le = (*it).second; 00205 if ( !le ) 00206 continue; 00207 00208 const QString value = le->text().stripWhiteSpace(); 00209 if ( value.isEmpty() ) 00210 continue; 00211 00212 if ( attr == "EMAIL" ) { 00213 // EMAIL is special, since it shouldn't be part of the DN, 00214 // except for non-RFC-conformant CAs that require it to be 00215 // there. 00216 email = value; 00217 if ( !brokenCA->isChecked() ) 00218 continue; 00219 } 00220 00221 if ( const char * oid = oidForAttributeName( attr ) ) { 00222 // we need to translate the attribute name for the backend: 00223 rdns.push_back( QString::fromUtf8( oid ) + '=' + value ); 00224 } else { 00225 rdns.push_back( attr + '=' + value ); 00226 } 00227 } 00228 certParms += rdns.join(","); 00229 if( !email.isEmpty() ) 00230 certParms += "\nname-email: " + email; 00231 certParms += "\n</GnupgKeyParms>\n"; 00232 00233 kdDebug() << certParms << endl; 00234 00235 Kleo::KeyGenerationJob * job = 00236 Kleo::CryptoBackendFactory::instance()->smime()->keyGenerationJob(); 00237 assert( job ); 00238 00239 connect( job, SIGNAL(result(const GpgME::KeyGenerationResult&,const QByteArray&)), 00240 SLOT(slotResult(const GpgME::KeyGenerationResult&,const QByteArray&)) ); 00241 00242 certificateTE->setText( certParms ); 00243 00244 const GpgME::Error err = job->start( certParms ); 00245 if ( err ) 00246 KMessageBox::error( this, 00247 i18n( "Could not start certificate generation: %1" ) 00248 .arg( QString::fromLocal8Bit( err.asString() ) ), 00249 i18n( "Certificate Manager Error" ) ); 00250 else { 00251 generatePB->setEnabled( false ); 00252 setBackEnabled( generatePage, false ); 00253 (void)new Kleo::ProgressDialog( job, i18n("Generating key"), this ); 00254 } 00255 } 00256 00257 00258 void CertificateWizardImpl::slotResult( const GpgME::KeyGenerationResult & res, 00259 const QByteArray & keyData ) { 00260 //kdDebug() << "keyData.size(): " << keyData.size() << endl; 00261 _keyData = keyData; 00262 00263 if ( res.error().isCanceled() || res.error() ) { 00264 setNextEnabled( generatePage, false ); 00265 setBackEnabled( generatePage, true ); 00266 setFinishEnabled( finishPage, false ); 00267 generatePB->setEnabled( true ); 00268 if ( !res.error().isCanceled() ) 00269 KMessageBox::error( this, 00270 i18n( "Could not generate certificate: %1" ) 00271 .arg( QString::fromLatin1( res.error().asString() ) ), 00272 i18n( "Certificate Manager Error" ) ); 00273 } else { 00274 // next will stay enabled until the user clicks Generate 00275 // Certificate again 00276 setNextEnabled( generatePage, true ); 00277 setFinishEnabled( finishPage, true ); 00278 } 00279 } 00280 00281 void CertificateWizardImpl::slotHelpClicked() 00282 { 00283 kapp->invokeHelp( "newcert" ); 00284 } 00285 00286 void CertificateWizardImpl::slotSetValuesFromWhoAmI() 00287 { 00288 const KABC::Addressee a = KABC::StdAddressBook::self()->whoAmI(); 00289 if ( a.isEmpty() ) 00290 return; 00291 const KABC::Address adr = a.address(KABC::Address::Work); 00292 00293 for ( AttrPairList::const_iterator it = _attrPairList.begin() ; 00294 it != _attrPairList.end() ; ++it ) { 00295 QLineEdit * le = (*it).second; 00296 if ( !availForMod( le ) ) 00297 continue; 00298 00299 const QString attr = attributeFromKey( (*it).first.upper() ); 00300 if ( attr == "CN" ) 00301 le->setText( a.formattedName() ); 00302 else if ( attr == "EMAIL" ) 00303 le->setText( a.preferredEmail() ); 00304 else if ( attr == "O" ) 00305 le->setText( a.organization() ); 00306 else if ( attr == "OU" ) 00307 le->setText( a.custom( "KADDRESSBOOK", "X-Department" ) ); 00308 else if ( attr == "L" ) 00309 le->setText( adr.locality() ); 00310 else if ( attr == "SP" ) 00311 le->setText( adr.region() ); 00312 else if ( attr == "PC" ) 00313 le->setText( adr.postalCode() ); 00314 else if ( attr == "SN" ) 00315 le->setText( a.familyName() ); 00316 else if ( attr == "GN" ) 00317 le->setText( a.givenName() ); 00318 else if ( attr == "T" ) 00319 le->setText( a.title() ); 00320 else if ( attr == "BC" ) 00321 le->setText( a.role() ); // correct mapping? 00322 } 00323 } 00324 00325 void CertificateWizardImpl::createPersonalDataPage() 00326 { 00327 QGridLayout* grid = new QGridLayout( edContainer, 2, 1, 00328 KDialog::marginHint(), KDialog::spacingHint() ); 00329 00330 KConfigGroup config( KGlobal::config(), "CertificateCreationWizard" ); 00331 QStringList attrOrder = config.readListEntry( "DNAttributeOrder" ); 00332 if ( attrOrder.empty() ) 00333 attrOrder << "CN!" << "L" << "OU" << "O!" << "C!" << "EMAIL!"; 00334 int row = 0; 00335 00336 for ( QStringList::const_iterator it = attrOrder.begin() ; it != attrOrder.end() ; ++it, ++row ) { 00337 const QString key = (*it).stripWhiteSpace().upper(); 00338 const QString attr = attributeFromKey( key ); 00339 if ( attr.isEmpty() ) { 00340 --row; 00341 continue; 00342 } 00343 const QString preset = config.readEntry( attr ); 00344 const QString label = config.readEntry( attr + "_label", 00345 attributeLabel( attr, key.endsWith("!") ) ); 00346 00347 QLineEdit * le = new QLineEdit( edContainer ); 00348 grid->addWidget( le, row, 1 ); 00349 grid->addWidget( new QLabel( le, label.isEmpty() ? attr : label, edContainer ), row, 0 ); 00350 00351 le->setText( preset ); 00352 if ( config.entryIsImmutable( attr ) ) 00353 le->setEnabled( false ); 00354 00355 _attrPairList.append(qMakePair(key, le)); 00356 00357 connect( le, SIGNAL(textChanged(const QString&)), 00358 SLOT(slotEnablePersonalDataPageExit()) ); 00359 } 00360 00361 // enable button only if administrator wants to allow it 00362 if (KABC::StdAddressBook::self()->whoAmI().isEmpty() || 00363 !config.readBoolEntry("ShowSetWhoAmI", true)) 00364 insertAddressButton->setEnabled( false ); 00365 00366 slotEnablePersonalDataPageExit(); 00367 } 00368 00369 bool CertificateWizardImpl::sendToCA() const { 00370 return sendToCARB->isChecked(); 00371 } 00372 00373 QString CertificateWizardImpl::caEMailAddress() const { 00374 return caEmailED->text().stripWhiteSpace(); 00375 } 00376 00377 void CertificateWizardImpl::slotURLSelected( const QString& _url ) 00378 { 00379 KURL url = KURL::fromPathOrURL( _url.stripWhiteSpace() ); 00380 #if ! KDE_IS_VERSION(3,2,90) 00381 // The application/pkcs10 mimetype didn't have a native extension, 00382 // so the filedialog didn't have the checkbox for auto-adding it. 00383 QString fileName = url.fileName(); 00384 int pos = fileName.findRev( '.' ); 00385 if ( pos < 0 ) // no extension 00386 url.setFileName( fileName + ".p10" ); 00387 #endif 00388 storeUR->setURL( url.prettyURL() ); 00389 } 00390 00391 KURL CertificateWizardImpl::saveFileUrl() const { 00392 return KURL::fromPathOrURL( storeUR->url().stripWhiteSpace() ); 00393 } 00394 00395 void CertificateWizardImpl::showPage( QWidget * page ) 00396 { 00397 CertificateWizard::showPage( page ); 00398 if ( page == generatePage ) { 00399 // Initial settings for the generation page: focus the correct lineedit 00400 // and disable the other one 00401 if ( storeInFileRB->isChecked() ) { 00402 storeUR->setEnabled( true ); 00403 caEmailED->setEnabled( false ); 00404 storeUR->setFocus(); 00405 } else { 00406 storeUR->setEnabled( false ); 00407 caEmailED->setEnabled( true ); 00408 caEmailED->setFocus(); 00409 } 00410 } 00411 } 00412 00413 static const char* const dcopObjectId = "KMailIface"; 00417 void CertificateWizardImpl::sendCertificate( const QString& email, const QByteArray& certificateData ) 00418 { 00419 QString error; 00420 QCString dcopService; 00421 int result = KDCOPServiceStarter::self()-> 00422 findServiceFor( "DCOP/Mailer", QString::null, 00423 QString::null, &error, &dcopService ); 00424 if ( result != 0 ) { 00425 kdDebug() << "Couldn't connect to KMail\n"; 00426 KMessageBox::error( this, 00427 i18n( "DCOP Communication Error, unable to send certificate using KMail.\n%1" ).arg( error ) ); 00428 return; 00429 } 00430 00431 QCString dummy; 00432 // OK, so kmail (or kontact) is running. Now ensure the object we want is available. 00433 // This is kind of a limitation of findServiceFor, which should do this by itself, 00434 // for that it needs to know the dcop object ID -> requires kdelibs API change. 00435 if ( !kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dcopService, dummy ) ) { 00436 KDCOPServiceStarter::self()->startServiceFor( "DCOP/Mailer", QString::null, 00437 QString::null, &error, &dcopService ); 00438 assert( kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dcopService, dummy ) ); 00439 } 00440 00441 DCOPClient* dcopClient = kapp->dcopClient(); 00442 QByteArray data; 00443 QDataStream arg( data, IO_WriteOnly ); 00444 arg << email; 00445 arg << certificateData; 00446 if( !dcopClient->send( dcopService, dcopObjectId, 00447 "sendCertificate(QString,QByteArray)", data ) ) { 00448 KMessageBox::error( this, 00449 i18n( "DCOP Communication Error, unable to send certificate using KMail." ) ); 00450 return; 00451 } 00452 // All good, close dialog 00453 CertificateWizard::accept(); 00454 } 00455 00456 // Called when pressing Finish 00457 // We want to do the emailing/uploading first, before closing the dialog, 00458 // in case of errors during the upload. 00459 void CertificateWizardImpl::accept() 00460 { 00461 if( sendToCA() ) { 00462 // Ask KMail to send this key to the CA. 00463 sendCertificate( caEMailAddress(), _keyData ); 00464 } else { 00465 // Save in file/URL 00466 KURL url = saveFileUrl(); 00467 bool overwrite = false; 00468 if ( KIO::NetAccess::exists( url, false /*dest*/, this ) ) { 00469 if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel( 00470 this, 00471 i18n( "A file named \"%1\" already exists. " 00472 "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ), 00473 i18n( "Overwrite File?" ), 00474 i18n( "&Overwrite" ) ) ) 00475 return; 00476 overwrite = true; 00477 } 00478 00479 KIO::Job* uploadJob = KIOext::put( _keyData, url, -1, overwrite, false /*resume*/ ); 00480 uploadJob->setWindow( this ); 00481 connect( uploadJob, SIGNAL( result( KIO::Job* ) ), 00482 this, SLOT( slotUploadResult( KIO::Job* ) ) ); 00483 // Can't press finish again during the upload 00484 setFinishEnabled( finishPage, false ); 00485 } 00486 } 00487 00492 void CertificateWizardImpl::slotUploadResult( KIO::Job* job ) 00493 { 00494 if ( job->error() ) { 00495 job->showErrorDialog(); 00496 setFinishEnabled( finishPage, true ); 00497 } else { 00498 // All good, close dialog 00499 CertificateWizard::accept(); 00500 } 00501 } 00502 00503 #include "certificatewizardimpl.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 Oct 1 15:18:46 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003