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 "certificatewizardimpl.h"
00038 #include "storedtransferjob.h"
00039
00040
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
00049 #include <gpgmepp/keygenerationresult.h>
00050
00051
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
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 #include <qcombobox.h>
00075
00076 #include <assert.h>
00077 #include <dcopref.h>
00078
00079 static const unsigned int keyLengths[] = {
00080 1024, 1532, 2048, 3072, 4096
00081 };
00082 static const unsigned int numKeyLengths = sizeof keyLengths / sizeof *keyLengths;
00083
00084 static QString attributeLabel( const QString & attr, bool required ) {
00085 if ( attr.isEmpty() )
00086 return QString::null;
00087 const QString label = Kleo::DNAttributeMapper::instance()->name2label( attr );
00088 if ( !label.isEmpty() )
00089 if ( required )
00090 return i18n("Format string for the labels in the \"Your Personal Data\" page - required field",
00091 "*%1 (%2):").arg( label, attr );
00092 else
00093 return i18n("Format string for the labels in the \"Your Personal Data\" page",
00094 "%1 (%2):").arg( label, attr );
00095
00096 else if ( required )
00097 return '*' + attr + ':';
00098 else
00099 return attr + ':';
00100 }
00101
00102 static QString attributeFromKey( QString key ) {
00103 return key.remove( '!' );
00104 }
00105
00106 static bool availForMod( const QLineEdit * le ) {
00107 return le && le->isEnabled();
00108 }
00109
00110
00111
00112
00113
00114
00115
00116
00117 CertificateWizardImpl::CertificateWizardImpl( QWidget* parent, const char* name, bool modal, WFlags fl )
00118 : CertificateWizard( parent, name, modal, fl )
00119 {
00120
00121 setNextEnabled( generatePage, false );
00122
00123
00124 createPersonalDataPage();
00125
00126
00127 storeUR->setMode( KFile::File );
00128 storeUR->setFilter( "application/pkcs10" );
00129 connect( storeUR, SIGNAL( urlSelected( const QString& ) ),
00130 this, SLOT( slotURLSelected( const QString& ) ) );
00131
00132 const KConfigGroup config( KGlobal::config(), "CertificateCreationWizard" );
00133 caEmailED->setText( config.readEntry( "CAEmailAddress" ) );
00134
00135 connect( this, SIGNAL( helpClicked() ),
00136 this, SLOT( slotHelpClicked() ) );
00137 connect( insertAddressButton, SIGNAL( clicked() ),
00138 this, SLOT( slotSetValuesFromWhoAmI() ) );
00139
00140 for ( unsigned int i = 0 ; i < numKeyLengths ; ++i )
00141 keyLengthCB->insertItem( i18n("%n bit", "%n bits", keyLengths[i] ) );
00142 }
00143
00144 static bool requirementsAreMet( const CertificateWizardImpl::AttrPairList & list ) {
00145 for ( CertificateWizardImpl::AttrPairList::const_iterator it = list.begin() ;
00146 it != list.end() ; ++it ) {
00147 const QLineEdit * le = (*it).second;
00148 if ( !le )
00149 continue;
00150 const QString key = (*it).first;
00151 #ifndef NDEBUG
00152 kdbgstream s = kdDebug();
00153 #else
00154 kndbgstream s = kdDebug();
00155 #endif
00156 s << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\": ";
00157 if ( key.endsWith("!") && le->text().stripWhiteSpace().isEmpty() ) {
00158 s << "required field is empty!" << endl;
00159 return false;
00160 }
00161 s << "ok" << endl;
00162 }
00163 return true;
00164 }
00165
00166
00167
00168
00169 void CertificateWizardImpl::slotEnablePersonalDataPageExit() {
00170 setNextEnabled( personalDataPage, requirementsAreMet( _attrPairList ) );
00171 }
00172
00173
00174
00175
00176
00177 CertificateWizardImpl::~CertificateWizardImpl()
00178 {
00179
00180 }
00181
00182 static const char * oidForAttributeName( const QString & attr ) {
00183 QCString attrUtf8 = attr.utf8();
00184 for ( unsigned int i = 0 ; i < numOidMaps ; ++i )
00185 if ( qstricmp( attrUtf8, oidmap[i].name ) == 0 )
00186 return oidmap[i].oid;
00187 return 0;
00188 }
00189
00190
00191
00192
00193 void CertificateWizardImpl::slotGenerateCertificate()
00194 {
00195
00196 QString certParms;
00197 certParms += "<GnupgKeyParms format=\"internal\">\n";
00198 certParms += "Key-Type: RSA\n";
00199 certParms += QString( "Key-Length: %1\n" ).arg( keyLengths[keyLengthCB->currentItem()] );
00200 certParms += "Key-Usage: ";
00201 if ( signOnlyCB->isChecked() )
00202 certParms += "Sign";
00203 else if ( encryptOnlyCB->isChecked() )
00204 certParms += "Encrypt";
00205 else
00206 certParms += "Sign, Encrypt";
00207 certParms += "\n";
00208 certParms += "name-dn: ";
00209
00210 QString email;
00211 QStringList rdns;
00212 for( AttrPairList::const_iterator it = _attrPairList.begin(); it != _attrPairList.end(); ++it ) {
00213 const QString attr = attributeFromKey( (*it).first.upper() );
00214 const QLineEdit * le = (*it).second;
00215 if ( !le )
00216 continue;
00217
00218 const QString value = le->text().stripWhiteSpace();
00219 if ( value.isEmpty() )
00220 continue;
00221
00222 if ( attr == "EMAIL" ) {
00223
00224
00225
00226 email = value;
00227 if ( !brokenCA->isChecked() )
00228 continue;
00229 }
00230
00231 if ( const char * oid = oidForAttributeName( attr ) ) {
00232
00233 rdns.push_back( QString::fromUtf8( oid ) + '=' + value );
00234 } else {
00235 rdns.push_back( attr + '=' + value );
00236 }
00237 }
00238 certParms += rdns.join(",");
00239 if( !email.isEmpty() )
00240 certParms += "\nname-email: " + email;
00241 certParms += "\n</GnupgKeyParms>\n";
00242
00243 kdDebug() << certParms << endl;
00244
00245 Kleo::KeyGenerationJob * job =
00246 Kleo::CryptoBackendFactory::instance()->smime()->keyGenerationJob();
00247 assert( job );
00248
00249 connect( job, SIGNAL(result(const GpgME::KeyGenerationResult&,const QByteArray&)),
00250 SLOT(slotResult(const GpgME::KeyGenerationResult&,const QByteArray&)) );
00251
00252 certificateTE->setText( certParms );
00253
00254 const GpgME::Error err = job->start( certParms );
00255 if ( err )
00256 KMessageBox::error( this,
00257 i18n( "Could not start certificate generation: %1" )
00258 .arg( QString::fromLocal8Bit( err.asString() ) ),
00259 i18n( "Certificate Manager Error" ) );
00260 else {
00261 generatePB->setEnabled( false );
00262 setBackEnabled( generatePage, false );
00263 (void)new Kleo::ProgressDialog( job, i18n("Generating key"), this );
00264 }
00265 }
00266
00267
00268 void CertificateWizardImpl::slotResult( const GpgME::KeyGenerationResult & res,
00269 const QByteArray & keyData ) {
00270
00271 _keyData = keyData;
00272
00273 if ( res.error().isCanceled() || res.error() ) {
00274 setNextEnabled( generatePage, false );
00275 setBackEnabled( generatePage, true );
00276 setFinishEnabled( finishPage, false );
00277 generatePB->setEnabled( true );
00278 if ( !res.error().isCanceled() )
00279 KMessageBox::error( this,
00280 i18n( "Could not generate certificate: %1" )
00281 .arg( QString::fromLatin1( res.error().asString() ) ),
00282 i18n( "Certificate Manager Error" ) );
00283 } else {
00284
00285
00286 setNextEnabled( generatePage, true );
00287 setFinishEnabled( finishPage, true );
00288 }
00289 }
00290
00291 void CertificateWizardImpl::slotHelpClicked()
00292 {
00293 kapp->invokeHelp( "newcert" );
00294 }
00295
00296 void CertificateWizardImpl::slotSetValuesFromWhoAmI()
00297 {
00298 const KABC::Addressee a = KABC::StdAddressBook::self( true )->whoAmI();
00299 if ( a.isEmpty() )
00300 return;
00301 const KABC::Address adr = a.address(KABC::Address::Work);
00302
00303 for ( AttrPairList::const_iterator it = _attrPairList.begin() ;
00304 it != _attrPairList.end() ; ++it ) {
00305 QLineEdit * le = (*it).second;
00306 if ( !availForMod( le ) )
00307 continue;
00308
00309 const QString attr = attributeFromKey( (*it).first.upper() );
00310 if ( attr == "CN" )
00311 le->setText( a.formattedName() );
00312 else if ( attr == "EMAIL" )
00313 le->setText( a.preferredEmail() );
00314 else if ( attr == "O" )
00315 le->setText( a.organization() );
00316 else if ( attr == "OU" )
00317 le->setText( a.custom( "KADDRESSBOOK", "X-Department" ) );
00318 else if ( attr == "L" )
00319 le->setText( adr.locality() );
00320 else if ( attr == "SP" )
00321 le->setText( adr.region() );
00322 else if ( attr == "PC" )
00323 le->setText( adr.postalCode() );
00324 else if ( attr == "SN" )
00325 le->setText( a.familyName() );
00326 else if ( attr == "GN" )
00327 le->setText( a.givenName() );
00328 else if ( attr == "T" )
00329 le->setText( a.title() );
00330 else if ( attr == "BC" )
00331 le->setText( a.role() );
00332 }
00333 }
00334
00335 void CertificateWizardImpl::createPersonalDataPage()
00336 {
00337 QGridLayout* grid = new QGridLayout( edContainer, 2, 1,
00338 KDialog::marginHint(), KDialog::spacingHint() );
00339
00340 KConfigGroup config( KGlobal::config(), "CertificateCreationWizard" );
00341 QStringList attrOrder = config.readListEntry( "DNAttributeOrder" );
00342 if ( attrOrder.empty() )
00343 attrOrder << "CN!" << "L" << "OU" << "O!" << "C!" << "EMAIL!";
00344 int row = 0;
00345
00346 for ( QStringList::const_iterator it = attrOrder.begin() ; it != attrOrder.end() ; ++it, ++row ) {
00347 const QString key = (*it).stripWhiteSpace().upper();
00348 const QString attr = attributeFromKey( key );
00349 if ( attr.isEmpty() ) {
00350 --row;
00351 continue;
00352 }
00353 const QString preset = config.readEntry( attr );
00354 const QString label = config.readEntry( attr + "_label",
00355 attributeLabel( attr, key.endsWith("!") ) );
00356
00357 QLineEdit * le = new QLineEdit( edContainer );
00358 grid->addWidget( le, row, 1 );
00359 grid->addWidget( new QLabel( le, label.isEmpty() ? attr : label, edContainer ), row, 0 );
00360
00361 le->setText( preset );
00362 if ( config.entryIsImmutable( attr ) )
00363 le->setEnabled( false );
00364
00365 _attrPairList.append(qMakePair(key, le));
00366
00367 connect( le, SIGNAL(textChanged(const QString&)),
00368 SLOT(slotEnablePersonalDataPageExit()) );
00369 }
00370
00371
00372 if (KABC::StdAddressBook::self( true )->whoAmI().isEmpty() ||
00373 !config.readBoolEntry("ShowSetWhoAmI", true))
00374 insertAddressButton->setEnabled( false );
00375
00376 slotEnablePersonalDataPageExit();
00377 }
00378
00379 bool CertificateWizardImpl::sendToCA() const {
00380 return sendToCARB->isChecked();
00381 }
00382
00383 QString CertificateWizardImpl::caEMailAddress() const {
00384 return caEmailED->text().stripWhiteSpace();
00385 }
00386
00387 void CertificateWizardImpl::slotURLSelected( const QString& _url )
00388 {
00389 KURL url = KURL::fromPathOrURL( _url.stripWhiteSpace() );
00390 #if ! KDE_IS_VERSION(3,2,90)
00391
00392
00393 QString fileName = url.fileName();
00394 int pos = fileName.findRev( '.' );
00395 if ( pos < 0 )
00396 url.setFileName( fileName + ".p10" );
00397 #endif
00398 storeUR->setURL( url.prettyURL() );
00399 }
00400
00401 KURL CertificateWizardImpl::saveFileUrl() const {
00402 return KURL::fromPathOrURL( storeUR->url().stripWhiteSpace() );
00403 }
00404
00405 void CertificateWizardImpl::showPage( QWidget * page )
00406 {
00407 CertificateWizard::showPage( page );
00408 if ( page == generatePage ) {
00409
00410
00411 if ( storeInFileRB->isChecked() ) {
00412 storeUR->setEnabled( true );
00413 caEmailED->setEnabled( false );
00414 storeUR->setFocus();
00415 } else {
00416 storeUR->setEnabled( false );
00417 caEmailED->setEnabled( true );
00418 caEmailED->setFocus();
00419 }
00420 }
00421 }
00422
00423 static const char* const dcopObjectId = "KMailIface";
00427 void CertificateWizardImpl::sendCertificate( const QString& email, const QByteArray& certificateData )
00428 {
00429 QString error;
00430 QCString dcopService;
00431 int result = KDCOPServiceStarter::self()->
00432 findServiceFor( "DCOP/Mailer", QString::null,
00433 QString::null, &error, &dcopService );
00434 if ( result != 0 ) {
00435 kdDebug() << "Couldn't connect to KMail\n";
00436 KMessageBox::error( this,
00437 i18n( "DCOP Communication Error, unable to send certificate using KMail.\n%1" ).arg( error ) );
00438 return;
00439 }
00440
00441 QCString dummy;
00442
00443
00444 if ( !kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00445 DCOPRef ref( dcopService, dcopService );
00446 DCOPReply reply = ref.call( "load()" );
00447 if ( reply.isValid() && (bool)reply ) {
00448 Q_ASSERT( kapp->dcopClient()->findObject( dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00449 } else
00450 kdWarning() << "Error loading " << dcopService << endl;
00451 }
00452
00453 DCOPClient* dcopClient = kapp->dcopClient();
00454 QByteArray data;
00455 QDataStream arg( data, IO_WriteOnly );
00456 arg << email;
00457 arg << certificateData;
00458 if( !dcopClient->send( dcopService, dcopObjectId,
00459 "sendCertificate(QString,QByteArray)", data ) ) {
00460 KMessageBox::error( this,
00461 i18n( "DCOP Communication Error, unable to send certificate using KMail." ) );
00462 return;
00463 }
00464
00465 CertificateWizard::accept();
00466 }
00467
00468
00469
00470
00471 void CertificateWizardImpl::accept()
00472 {
00473 if( sendToCA() ) {
00474
00475 sendCertificate( caEMailAddress(), _keyData );
00476 } else {
00477
00478 KURL url = saveFileUrl();
00479 bool overwrite = false;
00480 if ( KIO::NetAccess::exists( url, false , this ) ) {
00481 if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel(
00482 this,
00483 i18n( "A file named \"%1\" already exists. "
00484 "Are you sure you want to overwrite it?" ).arg( url.prettyURL() ),
00485 i18n( "Overwrite File?" ),
00486 i18n( "&Overwrite" ) ) )
00487 return;
00488 overwrite = true;
00489 }
00490
00491 KIO::Job* uploadJob = KIOext::put( _keyData, url, -1, overwrite, false );
00492 uploadJob->setWindow( this );
00493 connect( uploadJob, SIGNAL( result( KIO::Job* ) ),
00494 this, SLOT( slotUploadResult( KIO::Job* ) ) );
00495
00496 setFinishEnabled( finishPage, false );
00497 }
00498 }
00499
00504 void CertificateWizardImpl::slotUploadResult( KIO::Job* job )
00505 {
00506 if ( job->error() ) {
00507 job->showErrorDialog();
00508 setFinishEnabled( finishPage, true );
00509 } else {
00510
00511 CertificateWizard::accept();
00512 }
00513 }
00514
00515 #include "certificatewizardimpl.moc"