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