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
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
00105
00106
00107
00108
00109
00110 CertificateWizardImpl::CertificateWizardImpl( QWidget* parent, const char* name, bool modal, WFlags fl )
00111 : CertificateWizard( parent, name, modal, fl )
00112 {
00113
00114 setNextEnabled( generatePage, false );
00115
00116
00117 createPersonalDataPage();
00118
00119
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
00158
00159 void CertificateWizardImpl::slotEnablePersonalDataPageExit() {
00160 setNextEnabled( personalDataPage, requirementsAreMet( _attrPairList ) );
00161 }
00162
00163
00164
00165
00166
00167 CertificateWizardImpl::~CertificateWizardImpl()
00168 {
00169
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
00182
00183 void CertificateWizardImpl::slotGenerateCertificate()
00184 {
00185
00186 QString certParms;
00187 certParms += "<GnupgKeyParms format=\"internal\">\n";
00188 certParms += "Key-Type: RSA\n";
00189 certParms += "Key-Length: 1024\n";
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
00214
00215
00216 email = value;
00217 if ( !brokenCA->isChecked() )
00218 continue;
00219 }
00220
00221 if ( const char * oid = oidForAttributeName( attr ) ) {
00222
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
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
00275
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() );
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
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
00382
00383 QString fileName = url.fileName();
00384 int pos = fileName.findRev( '.' );
00385 if ( pos < 0 )
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
00400
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
00433
00434
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
00453 CertificateWizard::accept();
00454 }
00455
00456
00457
00458
00459 void CertificateWizardImpl::accept()
00460 {
00461 if( sendToCA() ) {
00462
00463 sendCertificate( caEMailAddress(), _keyData );
00464 } else {
00465
00466 KURL url = saveFileUrl();
00467 bool overwrite = false;
00468 if ( KIO::NetAccess::exists( url, false , 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 );
00480 uploadJob->setWindow( this );
00481 connect( uploadJob, SIGNAL( result( KIO::Job* ) ),
00482 this, SLOT( slotUploadResult( KIO::Job* ) ) );
00483
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
00499 CertificateWizard::accept();
00500 }
00501 }
00502
00503 #include "certificatewizardimpl.moc"