00001
00031 #ifdef HAVE_CONFIG_H
00032 #include <config.h>
00033 #endif
00034
00035 #include "messagecomposer.h"
00036 #include "kmmsgpart.h"
00037 #define REALLY_WANT_KMCOMPOSEWIN_H
00038 #include "kmcomposewin.h"
00039 #undef REALLY_WANT_KMCOMPOSEWIN_H
00040 #include "klistboxdialog.h"
00041 #include "kcursorsaver.h"
00042 #include "messagesender.h"
00043 #include "kmfolder.h"
00044 #include "kmfoldercombobox.h"
00045 #include "keyresolver.h"
00046 #include "kleo_util.h"
00047 #include "globalsettings.h"
00048 #include "custommimeheader.h"
00049 #include "kmedit.h"
00050 #include "util.h"
00051
00052 #include <libkpimidentities/identity.h>
00053 #include <libkpimidentities/identitymanager.h>
00054 #include <libemailfunctions/email.h>
00055
00056 #include <ui/keyselectiondialog.h>
00057 #include <ui/keyapprovaldialog.h>
00058 #include <kleo/cryptobackendfactory.h>
00059 #include <kleo/keylistjob.h>
00060 #include <kleo/encryptjob.h>
00061 #include <kleo/signencryptjob.h>
00062 #include <kleo/signjob.h>
00063 #include <kleo/specialjob.h>
00064
00065 #include <kmime_util.h>
00066 #include <kmime_codecs.h>
00067 #include <kpgpblock.h>
00068
00069 #include <mimelib/mimepp.h>
00070
00071 #include <kmessagebox.h>
00072 #include <klocale.h>
00073 #include <kinputdialog.h>
00074 #include <kdebug.h>
00075 #include <kaction.h>
00076 #include <qfile.h>
00077 #include <qtextcodec.h>
00078 #include <qtextedit.h>
00079 #include <qtimer.h>
00080
00081 #include <gpgmepp/key.h>
00082 #include <gpgmepp/keylistresult.h>
00083 #include <gpgmepp/encryptionresult.h>
00084 #include <gpgmepp/signingresult.h>
00085 #include <gpgmepp/context.h>
00086
00087 #include <algorithm>
00088 #include <memory>
00089
00090
00091
00092
00093 static inline bool warnSendUnsigned() {
00094 KConfigGroup group( KMKernel::config(), "Composer" );
00095 return group.readBoolEntry( "crypto-warning-unsigned", false );
00096 }
00097 static inline bool warnSendUnencrypted() {
00098 KConfigGroup group( KMKernel::config(), "Composer" );
00099 return group.readBoolEntry( "crypto-warning-unencrypted", false );
00100 }
00101 static inline bool saveMessagesEncrypted() {
00102 KConfigGroup group( KMKernel::config(), "Composer" );
00103 return group.readBoolEntry( "crypto-store-encrypted", true );
00104 }
00105 static inline bool encryptToSelf() {
00106
00107 KConfigGroup group( KMKernel::config(), "Composer" );
00108 return group.readBoolEntry( "crypto-encrypt-to-self", true );
00109 }
00110 static inline bool showKeyApprovalDialog() {
00111 KConfigGroup group( KMKernel::config(), "Composer" );
00112 return group.readBoolEntry( "crypto-show-keys-for-approval", true );
00113 }
00114
00115 static inline int encryptKeyNearExpiryWarningThresholdInDays() {
00116 const KConfigGroup composer( KMKernel::config(), "Composer" );
00117 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00118 return -1;
00119 const int num = composer.readNumEntry( "crypto-warn-encr-key-near-expire-int", 14 );
00120 return kMax( 1, num );
00121 }
00122
00123 static inline int signingKeyNearExpiryWarningThresholdInDays() {
00124 const KConfigGroup composer( KMKernel::config(), "Composer" );
00125 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00126 return -1;
00127 const int num = composer.readNumEntry( "crypto-warn-sign-key-near-expire-int", 14 );
00128 return kMax( 1, num );
00129 }
00130
00131 static inline int encryptRootCertNearExpiryWarningThresholdInDays() {
00132 const KConfigGroup composer( KMKernel::config(), "Composer" );
00133 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00134 return -1;
00135 const int num = composer.readNumEntry( "crypto-warn-encr-root-near-expire-int", 14 );
00136 return kMax( 1, num );
00137 }
00138
00139 static inline int signingRootCertNearExpiryWarningThresholdInDays() {
00140 const KConfigGroup composer( KMKernel::config(), "Composer" );
00141 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00142 return -1;
00143 const int num = composer.readNumEntry( "crypto-warn-sign-root-near-expire-int", 14 );
00144 return kMax( 1, num );
00145 }
00146
00147 static inline int encryptChainCertNearExpiryWarningThresholdInDays() {
00148 const KConfigGroup composer( KMKernel::config(), "Composer" );
00149 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00150 return -1;
00151 const int num = composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int", 14 );
00152 return kMax( 1, num );
00153 }
00154
00155 static inline int signingChainCertNearExpiryWarningThresholdInDays() {
00156 const KConfigGroup composer( KMKernel::config(), "Composer" );
00157 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00158 return -1;
00159 const int num = composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int", 14 );
00160 return kMax( 1, num );
00161 }
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220 static QString mErrorProcessingStructuringInfo =
00221 i18n("<qt><p>Structuring information returned by the Crypto plug-in "
00222 "could not be processed correctly; the plug-in might be damaged.</p>"
00223 "<p>Please contact your system administrator.</p></qt>");
00224 static QString mErrorNoCryptPlugAndNoBuildIn =
00225 i18n("<p>No active Crypto Plug-In was found and the built-in OpenPGP code "
00226 "did not run successfully.</p>"
00227 "<p>You can do two things to change this:</p>"
00228 "<ul><li><em>either</em> activate a Plug-In using the "
00229 "Settings->Configure KMail->Plug-In dialog.</li>"
00230 "<li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
00231 "Identity->Advanced tab.</li></ul>");
00232
00233
00234 class MessageComposerJob {
00235 public:
00236 MessageComposerJob( MessageComposer* composer ) : mComposer( composer ) {}
00237 virtual ~MessageComposerJob() {}
00238
00239 virtual void execute() = 0;
00240
00241 protected:
00242
00243
00244 void adjustCryptFlags() { mComposer->adjustCryptFlags(); }
00245 void composeMessage() { mComposer->composeMessage(); }
00246 void continueComposeMessage( KMMessage& msg, bool doSign, bool doEncrypt,
00247 Kleo::CryptoMessageFormat format )
00248 {
00249 mComposer->continueComposeMessage( msg, doSign, doEncrypt, format );
00250 }
00251 void chiasmusEncryptAllAttachments() {
00252 mComposer->chiasmusEncryptAllAttachments();
00253 }
00254
00255 MessageComposer* mComposer;
00256 };
00257
00258 class ChiasmusBodyPartEncryptJob : public MessageComposerJob {
00259 public:
00260 ChiasmusBodyPartEncryptJob( MessageComposer * composer )
00261 : MessageComposerJob( composer ) {}
00262
00263 void execute() {
00264 chiasmusEncryptAllAttachments();
00265 }
00266 };
00267
00268 class AdjustCryptFlagsJob : public MessageComposerJob {
00269 public:
00270 AdjustCryptFlagsJob( MessageComposer* composer )
00271 : MessageComposerJob( composer ) {}
00272
00273 void execute() {
00274 adjustCryptFlags();
00275 }
00276 };
00277
00278 class ComposeMessageJob : public MessageComposerJob {
00279 public:
00280 ComposeMessageJob( MessageComposer* composer )
00281 : MessageComposerJob( composer ) {}
00282
00283 void execute() {
00284 composeMessage();
00285 }
00286 };
00287
00288 MessageComposer::MessageComposer( KMComposeWin* win, const char* name )
00289 : QObject( win, name ), mComposeWin( win ), mCurrentJob( 0 ),
00290 mKeyResolver( 0 ), mIdentityUid( 0 )
00291 {
00292 }
00293
00294 MessageComposer::~MessageComposer()
00295 {
00296 delete mKeyResolver; mKeyResolver = 0;
00297 }
00298
00299 void MessageComposer::applyChanges( bool disableCrypto )
00300 {
00301
00302 if( getenv("KMAIL_DEBUG_COMPOSER_CRYPTO") != 0 ) {
00303 QCString cE = getenv("KMAIL_DEBUG_COMPOSER_CRYPTO");
00304 mDebugComposerCrypto = cE == "1" || cE.upper() == "ON" || cE.upper() == "TRUE";
00305 kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = TRUE" << endl;
00306 } else {
00307 mDebugComposerCrypto = false;
00308 kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl;
00309 }
00310
00311 mHoldJobs = false;
00312 mRc = true;
00313
00314 mDisableCrypto = disableCrypto;
00315
00316
00317
00318 readFromComposeWin();
00319
00320
00321
00322
00323 mJobs.push_back( new ChiasmusBodyPartEncryptJob( this ) );
00324
00325
00326 mJobs.push_back( new AdjustCryptFlagsJob( this ) );
00327
00328
00329 mJobs.push_back( new ComposeMessageJob( this ) );
00330
00331
00332 doNextJob();
00333 }
00334
00335 void MessageComposer::doNextJob()
00336 {
00337 delete mCurrentJob; mCurrentJob = 0;
00338
00339 if( mJobs.isEmpty() ) {
00340
00341 emit done( mRc );
00342 return;
00343 }
00344
00345 if( !mRc ) {
00346
00347 while( !mJobs.isEmpty() ) {
00348 delete mJobs.front();
00349 mJobs.pop_front();
00350 }
00351 emit done( false );
00352 return;
00353 }
00354
00355
00356 QTimer::singleShot( 0, this, SLOT( slotDoNextJob() ) );
00357 }
00358
00359 void MessageComposer::slotDoNextJob()
00360 {
00361 assert( !mCurrentJob );
00362 if( mHoldJobs )
00363
00364
00365 mHoldJobs = false;
00366 else {
00367 assert( !mJobs.empty() );
00368
00369 mCurrentJob = mJobs.front();
00370 assert( mCurrentJob );
00371 mJobs.pop_front();
00372
00373
00374 mCurrentJob->execute();
00375 }
00376
00377
00378 if( !mHoldJobs )
00379 doNextJob();
00380 }
00381
00382 void MessageComposer::readFromComposeWin()
00383 {
00384
00385 mDisableBreaking = false;
00386
00387 mSignBody = mComposeWin->mSignAction->isChecked();
00388 mSigningRequested = mSignBody;
00389 mEncryptBody = mComposeWin->mEncryptAction->isChecked();
00390 mEncryptionRequested = mEncryptBody;
00391
00392 mAutoCharset = mComposeWin->mAutoCharset;
00393 mCharset = mComposeWin->mCharset;
00394 mReferenceMessage = mComposeWin->mMsg;
00395
00396
00397
00398
00399 if ( mComposeWin->isModified() )
00400 mReferenceMessage->setHeaderField( "Content-Type", "text/plain" );
00401 mUseOpportunisticEncryption = GlobalSettings::self()->pgpAutoEncrypt();
00402 mAllowedCryptoMessageFormats = mComposeWin->cryptoMessageFormat();
00403
00404 if( mAutoCharset ) {
00405 QCString charset = KMMsgBase::autoDetectCharset( mCharset, KMMessage::preferredCharsets(), mComposeWin->mEditor->text() );
00406 if( charset.isEmpty() )
00407 {
00408 KMessageBox::sorry( mComposeWin,
00409 i18n( "No suitable encoding could be found for "
00410 "your message.\nPlease set an encoding "
00411 "using the 'Options' menu." ) );
00412 mRc = false;
00413 return;
00414 }
00415 mCharset = charset;
00416
00417 mComposeWin->mCharset = charset;
00418 }
00419 mReferenceMessage->setCharset(mCharset);
00420
00421 mReferenceMessage->setTo(mComposeWin->to());
00422 mReferenceMessage->setFrom(mComposeWin->from());
00423 mReferenceMessage->setCc(mComposeWin->cc());
00424 mReferenceMessage->setSubject(mComposeWin->subject());
00425 mReferenceMessage->setReplyTo(mComposeWin->replyTo());
00426 mReferenceMessage->setBcc(mComposeWin->bcc());
00427
00428 const KPIM::Identity & id = mComposeWin->identity();
00429
00430 KMFolder *f = mComposeWin->mFcc->getFolder();
00431 assert( f != 0 );
00432 if ( f->idString() == id.fcc() )
00433 mReferenceMessage->removeHeaderField("X-KMail-Fcc");
00434 else
00435 mReferenceMessage->setFcc( f->idString() );
00436
00437
00438 mReferenceMessage->setDrafts( id.drafts() );
00439
00440 if (id.isDefault())
00441 mReferenceMessage->removeHeaderField("X-KMail-Identity");
00442 else mReferenceMessage->setHeaderField("X-KMail-Identity", QString::number( id.uoid() ));
00443
00444 QString replyAddr;
00445 if (!mComposeWin->replyTo().isEmpty()) replyAddr = mComposeWin->replyTo();
00446 else replyAddr = mComposeWin->from();
00447
00448 if (mComposeWin->mRequestMDNAction->isChecked())
00449 mReferenceMessage->setHeaderField("Disposition-Notification-To", replyAddr);
00450 else
00451 mReferenceMessage->removeHeaderField("Disposition-Notification-To");
00452
00453 if (mComposeWin->mUrgentAction->isChecked()) {
00454 mReferenceMessage->setHeaderField("X-PRIORITY", "2 (High)");
00455 mReferenceMessage->setHeaderField("Priority", "urgent");
00456 } else {
00457 mReferenceMessage->removeHeaderField("X-PRIORITY");
00458 mReferenceMessage->removeHeaderField("Priority");
00459 }
00460
00461 int num = GlobalSettings::self()->custHeaderCount();
00462 for(int ix=0; ix<num; ix++) {
00463 CustomMimeHeader customMimeHeader( QString::number(ix) );
00464 customMimeHeader.readConfig();
00465 mReferenceMessage->setHeaderField(
00466 KMMsgBase::toUsAscii( customMimeHeader.custHeaderName() ),
00467 customMimeHeader.custHeaderValue() );
00468 }
00469
00470
00471
00472
00473
00474
00475
00476 mBcc = mComposeWin->bcc();
00477 mTo = KPIM::splitEmailAddrList( mComposeWin->to().stripWhiteSpace() );
00478 mCc = KPIM::splitEmailAddrList( mComposeWin->cc().stripWhiteSpace() );
00479 mBccList = KPIM::splitEmailAddrList( mBcc.stripWhiteSpace() );
00480
00481 for ( unsigned int i = 0 ; i < mComposeWin->mAtmList.count() ; ++i )
00482 mAttachments.push_back( Attachment( mComposeWin->mAtmList.at(i),
00483 mComposeWin->signFlagOfAttachment( i ),
00484 mComposeWin->encryptFlagOfAttachment( i ) ) );
00485
00486 mEncryptWithChiasmus = mComposeWin->mEncryptWithChiasmus;
00487
00488 mIsRichText = mComposeWin->mEditor->textFormat() == Qt::RichText;
00489 mIdentityUid = mComposeWin->identityUid();
00490 mText = breakLinesAndApplyCodec();
00491
00492
00493
00494 mLineBreakColumn = mComposeWin->mEditor->lineBreakColumn();
00495 }
00496 static QCString escape_quoted_string( const QCString & str ) {
00497 QCString result;
00498 const unsigned int str_len = str.length();
00499 result.resize( 2*str_len + 1 );
00500 char * d = result.data();
00501 for ( unsigned int i = 0 ; i < str_len ; ++i )
00502 switch ( const char ch = str[i] ) {
00503 case '\\':
00504 case '"':
00505 *d++ = '\\';
00506 default:
00507 *d++ = ch;
00508 }
00509 result.truncate( d - result.begin() );
00510 return result;
00511 }
00512
00513 bool MessageComposer::encryptWithChiasmus( const Kleo::CryptoBackend::Protocol * chiasmus,
00514 const QByteArray& body,
00515 QByteArray& resultData )
00516 {
00517 std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-encrypt", QMap<QString,QVariant>() ) );
00518 if ( !job.get() ) {
00519 const QString msg = i18n( "Chiasmus backend does not offer the "
00520 "\"x-encrypt\" function. Please report this bug." );
00521 KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00522 return false;
00523 }
00524 if ( !job->setProperty( "key", GlobalSettings::chiasmusKey() ) ||
00525 !job->setProperty( "options", GlobalSettings::chiasmusOptions() ) ||
00526 !job->setProperty( "input", body ) ) {
00527 const QString msg = i18n( "The \"x-encrypt\" function does not accept "
00528 "the expected parameters. Please report this bug." );
00529 KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00530 return false;
00531 }
00532 const GpgME::Error err = job->exec();
00533 if ( err.isCanceled() || err ) {
00534 if ( err )
00535 job->showErrorDialog( mComposeWin, i18n( "Chiasmus Encryption Error" ) );
00536 return false;
00537 }
00538 const QVariant result = job->property( "result" );
00539 if ( result.type() != QVariant::ByteArray ) {
00540 const QString msg = i18n( "Unexpected return value from Chiasmus backend: "
00541 "The \"x-encrypt\" function did not return a "
00542 "byte array. Please report this bug." );
00543 KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00544 return false;
00545 }
00546 resultData = result.toByteArray();
00547 return true;
00548 }
00549
00550 void MessageComposer::chiasmusEncryptAllAttachments() {
00551 if ( !mEncryptWithChiasmus )
00552 return;
00553 assert( !GlobalSettings::chiasmusKey().isEmpty() );
00554 if ( mAttachments.empty() )
00555 return;
00556 const Kleo::CryptoBackend::Protocol * chiasmus
00557 = Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
00558 assert( chiasmus );
00559
00560
00561 for ( QValueVector<Attachment>::iterator it = mAttachments.begin(), end = mAttachments.end() ; it != end ; ++it ) {
00562 KMMessagePart * part = it->part;
00563 const QString filename = part->fileName();
00564 if ( filename.endsWith( ".xia", false ) )
00565 continue;
00566 const QByteArray body = part->bodyDecodedBinary();
00567 QByteArray resultData;
00568 if ( !encryptWithChiasmus( chiasmus, body, resultData ) ) {
00569 mRc = false;
00570 return;
00571 }
00572
00573 QValueList<int> dummy;
00574 part->setBodyAndGuessCte( resultData, dummy );
00575 part->setTypeStr( "application" );
00576 part->setSubtypeStr( "vnd.de.bund.bsi.chiasmus" );
00577 part->setName( filename + ".xia" );
00578
00579 QCString encoding = KMMsgBase::autoDetectCharset( part->charset(), KMMessage::preferredCharsets(), filename );
00580 if ( encoding.isEmpty() )
00581 encoding = "utf-8";
00582 const QCString enc_name = KMMsgBase::encodeRFC2231String( filename + ".xia", encoding );
00583 const QCString cDisp = "attachment;\n\tfilename"
00584 + ( QString( enc_name ) != filename + ".xia"
00585 ? "*=" + enc_name
00586 : "=\"" + escape_quoted_string( enc_name ) + '\"' );
00587 part->setContentDisposition( cDisp );
00588 }
00589 }
00590
00591 void MessageComposer::adjustCryptFlags()
00592 {
00593 if ( !mDisableCrypto &&
00594 mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat &&
00595 !mAttachments.empty() &&
00596 ( mSigningRequested || mEncryptionRequested ) )
00597 {
00598 int ret;
00599 if ( mAllowedCryptoMessageFormats == Kleo::InlineOpenPGPFormat ) {
00600 ret = KMessageBox::warningYesNoCancel( mComposeWin,
00601 i18n("The inline OpenPGP crypto message format "
00602 "does not support encryption or signing "
00603 "of attachments.\n"
00604 "Really use deprecated inline OpenPGP?"),
00605 i18n("Insecure Message Format"),
00606 i18n("Use Inline OpenPGP"),
00607 i18n("Use OpenPGP/MIME") );
00608 }
00609 else {
00610
00611
00612 ret = KMessageBox::No;
00613 }
00614
00615 if ( ret == KMessageBox::Cancel ) {
00616 mRc = false;
00617 return;
00618 } else if ( ret == KMessageBox::No ) {
00619 mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat;
00620 mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat;
00621 if ( mSigningRequested ) {
00622
00623 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00624 mAttachments[idx].sign = true;
00625 }
00626 if ( mEncryptionRequested ) {
00627
00628
00629 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00630 mAttachments[idx].encrypt = true;
00631 }
00632 }
00633 }
00634
00635 mKeyResolver =
00636 new Kleo::KeyResolver( encryptToSelf(), showKeyApprovalDialog(),
00637 mUseOpportunisticEncryption, mAllowedCryptoMessageFormats,
00638 encryptKeyNearExpiryWarningThresholdInDays(),
00639 signingKeyNearExpiryWarningThresholdInDays(),
00640 encryptRootCertNearExpiryWarningThresholdInDays(),
00641 signingRootCertNearExpiryWarningThresholdInDays(),
00642 encryptChainCertNearExpiryWarningThresholdInDays(),
00643 signingChainCertNearExpiryWarningThresholdInDays() );
00644
00645 if ( !mDisableCrypto ) {
00646 const KPIM::Identity & id =
00647 kmkernel->identityManager()->identityForUoidOrDefault( mIdentityUid );
00648
00649 QStringList encryptToSelfKeys;
00650 if ( !id.pgpEncryptionKey().isEmpty() )
00651 encryptToSelfKeys.push_back( id.pgpEncryptionKey() );
00652 if ( !id.smimeEncryptionKey().isEmpty() )
00653 encryptToSelfKeys.push_back( id.smimeEncryptionKey() );
00654 if ( mKeyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) {
00655 mRc = false;
00656 return;
00657 }
00658
00659 QStringList signKeys;
00660 if ( !id.pgpSigningKey().isEmpty() )
00661 signKeys.push_back( mPGPSigningKey = id.pgpSigningKey() );
00662 if ( !id.smimeSigningKey().isEmpty() )
00663 signKeys.push_back( mSMIMESigningKey = id.smimeSigningKey() );
00664 if ( mKeyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) {
00665 mRc = false;
00666 return;
00667 }
00668 }
00669
00670 mKeyResolver->setPrimaryRecipients( mTo + mCc );
00671 mKeyResolver->setSecondaryRecipients( mBccList );
00672
00673
00674 bool doSignCompletely = mSigningRequested;
00675 bool doEncryptCompletely = mEncryptionRequested;
00676 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) {
00677 if ( mAttachments[idx].encrypt )
00678 mEncryptionRequested = true;
00679 else
00680 doEncryptCompletely = false;
00681 if ( mAttachments[idx].sign )
00682 mSigningRequested = true;
00683 else
00684 doSignCompletely = false;
00685 }
00686
00687 mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely );
00688
00689 if ( !mRc )
00690 return;
00691
00692 mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely );
00693
00694 if ( !mRc )
00695 return;
00696
00697
00698
00699
00700 if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok )
00701 mRc = false;
00702 }
00703
00704 bool MessageComposer::determineWhetherToSign( bool doSignCompletely ) {
00705 bool sign = false;
00706 switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) {
00707 case Kleo::DoIt:
00708 if ( !mSigningRequested ) {
00709 markAllAttachmentsForSigning( true );
00710 return true;
00711 }
00712 sign = true;
00713 break;
00714 case Kleo::DontDoIt:
00715 sign = false;
00716 break;
00717 case Kleo::AskOpportunistic:
00718 assert( 0 );
00719 case Kleo::Ask:
00720 {
00721
00722 const KCursorSaver idle( KBusyPtr::idle() );
00723 const QString msg = i18n("Examination of the recipient's signing preferences "
00724 "yielded that you be asked whether or not to sign "
00725 "this message.\n"
00726 "Sign this message?");
00727 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00728 i18n("Sign Message?"),
00729 i18n("to sign","&Sign"),
00730 i18n("Do &Not Sign") ) ) {
00731 case KMessageBox::Cancel:
00732 mRc = false;
00733 return false;
00734 case KMessageBox::Yes:
00735 markAllAttachmentsForSigning( true );
00736 return true;
00737 case KMessageBox::No:
00738 markAllAttachmentsForSigning( false );
00739 return false;
00740 }
00741 }
00742 break;
00743 case Kleo::Conflict:
00744 {
00745
00746 const KCursorSaver idle( KBusyPtr::idle() );
00747 const QString msg = i18n("There are conflicting signing preferences "
00748 "for these recipients.\n"
00749 "Sign this message?");
00750 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00751 i18n("Sign Message?"),
00752 i18n("to sign","&Sign"),
00753 i18n("Do &Not Sign") ) ) {
00754 case KMessageBox::Cancel:
00755 mRc = false;
00756 return false;
00757 case KMessageBox::Yes:
00758 markAllAttachmentsForSigning( true );
00759 return true;
00760 case KMessageBox::No:
00761 markAllAttachmentsForSigning( false );
00762 return false;
00763 }
00764 }
00765 break;
00766 case Kleo::Impossible:
00767 {
00768 const KCursorSaver idle( KBusyPtr::idle() );
00769 const QString msg = i18n("You have requested to sign this message, "
00770 "but no valid signing keys have been configured "
00771 "for this identity.");
00772 if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00773 i18n("Send Unsigned?"),
00774 i18n("Send &Unsigned") )
00775 == KMessageBox::Cancel ) {
00776 mRc = false;
00777 return false;
00778 } else {
00779 markAllAttachmentsForSigning( false );
00780 return false;
00781 }
00782 }
00783 }
00784
00785 if ( !sign || !doSignCompletely ) {
00786 if ( warnSendUnsigned() ) {
00787 const KCursorSaver idle( KBusyPtr::idle() );
00788 const QString msg = sign && !doSignCompletely
00789 ? i18n("Some parts of this message will not be signed.\n"
00790 "Sending only partially signed messages might violate site policy.\n"
00791 "Sign all parts instead?")
00792 : i18n("This message will not be signed.\n"
00793 "Sending unsigned message might violate site policy.\n"
00794 "Sign message instead?") ;
00795 const QString buttonText = sign && !doSignCompletely
00796 ? i18n("&Sign All Parts") : i18n("&Sign") ;
00797 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00798 i18n("Unsigned-Message Warning"),
00799 buttonText,
00800 i18n("Send &As Is") ) ) {
00801 case KMessageBox::Cancel:
00802 mRc = false;
00803 return false;
00804 case KMessageBox::Yes:
00805 markAllAttachmentsForSigning( true );
00806 return true;
00807 case KMessageBox::No:
00808 return sign || doSignCompletely;
00809 }
00810 }
00811 }
00812
00813 return sign || doSignCompletely ;
00814 }
00815
00816 bool MessageComposer::determineWhetherToEncrypt( bool doEncryptCompletely ) {
00817 bool encrypt = false;
00818 bool opportunistic = false;
00819 switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) {
00820 case Kleo::DoIt:
00821 if ( !mEncryptionRequested ) {
00822 markAllAttachmentsForEncryption( true );
00823 return true;
00824 }
00825 encrypt = true;
00826 break;
00827 case Kleo::DontDoIt:
00828 encrypt = false;
00829 break;
00830 case Kleo::AskOpportunistic:
00831 opportunistic = true;
00832
00833 case Kleo::Ask:
00834 {
00835
00836 const KCursorSaver idle( KBusyPtr::idle() );
00837 const QString msg = opportunistic
00838 ? i18n("Valid trusted encryption keys were found for all recipients.\n"
00839 "Encrypt this message?")
00840 : i18n("Examination of the recipient's encryption preferences "
00841 "yielded that you be asked whether or not to encrypt "
00842 "this message.\n"
00843 "Encrypt this message?");
00844 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00845 i18n("Encrypt Message?"),
00846 mDoSign
00847 ? i18n("Sign && &Encrypt")
00848 : i18n("&Encrypt"),
00849 mDoSign
00850 ? i18n("&Sign Only")
00851 : i18n("&Send As-Is") ) ) {
00852 case KMessageBox::Cancel:
00853 mRc = false;
00854 return false;
00855 case KMessageBox::Yes:
00856 markAllAttachmentsForEncryption( true );
00857 return true;
00858 case KMessageBox::No:
00859 markAllAttachmentsForEncryption( false );
00860 return false;
00861 }
00862 }
00863 break;
00864 case Kleo::Conflict:
00865 {
00866
00867 const KCursorSaver idle( KBusyPtr::idle() );
00868 const QString msg = i18n("There are conflicting encryption preferences "
00869 "for these recipients.\n"
00870 "Encrypt this message?");
00871 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00872 i18n("Encrypt Message?"),
00873 i18n("&Encrypt"),
00874 i18n("Do &Not Encrypt") ) ) {
00875 case KMessageBox::Cancel:
00876 mRc = false;
00877 return false;
00878 case KMessageBox::Yes:
00879 markAllAttachmentsForEncryption( true );
00880 return true;
00881 case KMessageBox::No:
00882 markAllAttachmentsForEncryption( false );
00883 return false;
00884 }
00885 }
00886 break;
00887 case Kleo::Impossible:
00888 {
00889 const KCursorSaver idle( KBusyPtr::idle() );
00890 const QString msg = i18n("You have requested to encrypt this message, "
00891 "and to encrypt a copy to yourself, "
00892 "but no valid trusted encryption keys have been "
00893 "configured for this identity.");
00894 if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00895 i18n("Send Unencrypted?"),
00896 i18n("Send &Unencrypted") )
00897 == KMessageBox::Cancel ) {
00898 mRc = false;
00899 return false;
00900 } else {
00901 markAllAttachmentsForEncryption( false );
00902 return false;
00903 }
00904 }
00905 }
00906
00907 if ( !encrypt || !doEncryptCompletely ) {
00908 if ( warnSendUnencrypted() ) {
00909 const KCursorSaver idle( KBusyPtr::idle() );
00910 const QString msg = !doEncryptCompletely
00911 ? i18n("Some parts of this message will not be encrypted.\n"
00912 "Sending only partially encrypted messages might violate site policy "
00913 "and/or leak sensitive information.\n"
00914 "Encrypt all parts instead?")
00915 : i18n("This message will not be encrypted.\n"
00916 "Sending unencrypted messages might violate site policy and/or "
00917 "leak sensitive information.\n"
00918 "Encrypt messages instead?") ;
00919 const QString buttonText = !doEncryptCompletely
00920 ? i18n("&Encrypt All Parts") : i18n("&Encrypt") ;
00921 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00922 i18n("Unencrypted Message Warning"),
00923 buttonText,
00924 mDoSign
00925 ? i18n("&Sign Only")
00926 : i18n("&Send As-Is") ) ) {
00927 case KMessageBox::Cancel:
00928 mRc = false;
00929 return false;
00930 case KMessageBox::Yes:
00931 markAllAttachmentsForEncryption( true );
00932 return true;
00933 case KMessageBox::No:
00934 return encrypt || doEncryptCompletely;
00935 }
00936 }
00937 }
00938
00939 return encrypt || doEncryptCompletely ;
00940 }
00941
00942 void MessageComposer::markAllAttachmentsForSigning( bool sign ) {
00943 mSignBody = sign;
00944 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00945 it->sign = sign;
00946 }
00947
00948 void MessageComposer::markAllAttachmentsForEncryption( bool enc ) {
00949 mEncryptBody = enc;
00950 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00951 it->encrypt = enc;
00952 }
00953
00954
00955 void MessageComposer::composeMessage()
00956 {
00957 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
00958 if ( mKeyResolver->encryptionItems( concreteCryptoMessageFormats[i] ).empty() )
00959 continue;
00960 KMMessage * msg = new KMMessage( *mReferenceMessage );
00961 composeMessage( *msg, mDoSign, mDoEncrypt, concreteCryptoMessageFormats[i] );
00962 if ( !mRc )
00963 return;
00964 }
00965 }
00966
00967
00968
00969
00970
00971
00972 static inline bool makeMultiMime( Kleo::CryptoMessageFormat f, bool sign ) {
00973 switch ( f ) {
00974 default:
00975 case Kleo::InlineOpenPGPFormat:
00976 case Kleo::SMIMEOpaqueFormat: return false;
00977 case Kleo::OpenPGPMIMEFormat: return true;
00978 case Kleo::SMIMEFormat: return sign;
00979 }
00980 }
00981 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
00982 return makeMultiMime( f, true );
00983 }
00984 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
00985 return makeMultiMime( f, false );
00986 }
00987
00988 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool ) {
00989 return f != Kleo::InlineOpenPGPFormat;
00990 }
00991
00992 static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f, bool signing ) {
00993 switch ( f ) {
00994 default:
00995 case Kleo::InlineOpenPGPFormat: return 0;
00996 case Kleo::OpenPGPMIMEFormat:
00997 return signing ?
00998 "multipart/signed;\n\t"
00999 "boundary=\"%boundary\";\n\t"
01000 "protocol=\"application/pgp-signature\";\n\t"
01001 "micalg=pgp-sha1"
01002 :
01003 "multipart/encrypted;\n\t"
01004 "boundary=\"%boundary\";\n\t"
01005 "protocol=\"application/pgp-encrypted\""
01006 ;
01007 case Kleo::SMIMEFormat:
01008 if ( signing )
01009 return
01010 "multipart/signed;\n\t"
01011 "boundary=\"%boundary\";\n\t"
01012 "protocol=\"application/pkcs7-signature\";\n\t"
01013 "micalg=sha1";
01014
01015
01016
01017 case Kleo::SMIMEOpaqueFormat:
01018 return signing ?
01019 "application/pkcs7-mime;\n\t"
01020 "smime-type=signed-data;\n\t"
01021 "name=\"smime.p7m\";\n\t"
01022 :
01023 "application/pkcs7-mime;\n\t"
01024 "smime-type=enveloped-data;\n\t"
01025 "name=\"smime.p7m\";\n\t"
01026 ;
01027 }
01028 }
01029
01030 static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01031 switch ( f ) {
01032 default:
01033 case Kleo::InlineOpenPGPFormat:
01034 case Kleo::OpenPGPMIMEFormat:
01035 return 0;
01036 case Kleo::SMIMEFormat:
01037 if ( signing )
01038 return 0;
01039 case Kleo::SMIMEOpaqueFormat:
01040 return "attachment; filename=\"smime.p7m\"";
01041 }
01042 }
01043
01044 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
01045 return makeMultiPartSigned( f );
01046 }
01047
01048 static inline const char * nestedContentType( Kleo::CryptoMessageFormat f, bool signing ) {
01049 switch ( f ) {
01050 case Kleo::OpenPGPMIMEFormat:
01051 return signing ? "application/pgp-signature" : "application/octet-stream" ;
01052 case Kleo::SMIMEFormat:
01053 if ( signing )
01054 return "application/pkcs7-signature; name=\"smime.p7s\"";
01055
01056 default:
01057 case Kleo::InlineOpenPGPFormat:
01058 case Kleo::SMIMEOpaqueFormat:
01059 return 0;
01060 }
01061 }
01062
01063 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01064 if ( !signing && f == Kleo::OpenPGPMIMEFormat )
01065 return "inline; filename=\"msg.asc\"";
01066 if ( signing && f == Kleo::SMIMEFormat )
01067 return "attachment; filename=\"smime.p7s\"";
01068 return 0;
01069 }
01070
01071 static inline bool binaryHint( Kleo::CryptoMessageFormat f ) {
01072 switch ( f ) {
01073 case Kleo::SMIMEFormat:
01074 case Kleo::SMIMEOpaqueFormat:
01075 return true;
01076 default:
01077 case Kleo::OpenPGPMIMEFormat:
01078 case Kleo::InlineOpenPGPFormat:
01079 return false;
01080 }
01081 }
01082
01083 static inline bool armor( Kleo::CryptoMessageFormat f ) {
01084 return !binaryHint( f );
01085 }
01086
01087 static inline bool textMode( Kleo::CryptoMessageFormat f ) {
01088 return f == Kleo::InlineOpenPGPFormat;
01089 }
01090
01091 static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) {
01092 switch ( f ) {
01093 case Kleo::SMIMEOpaqueFormat:
01094 return GpgME::Context::Normal;
01095 case Kleo::InlineOpenPGPFormat:
01096 return GpgME::Context::Clearsigned;
01097 default:
01098 case Kleo::SMIMEFormat:
01099 case Kleo::OpenPGPMIMEFormat:
01100 return GpgME::Context::Detached;
01101 }
01102 }
01103
01104
01105
01106
01107
01108 class EncryptMessageJob : public MessageComposerJob {
01109 public:
01110 EncryptMessageJob( KMMessage* msg, const Kleo::KeyResolver::SplitInfo & si,
01111 bool doSign, bool doEncrypt, const QCString& encodedBody,
01112 int boundaryLevel, const KMMessagePart& oldBodyPart,
01113 KMMessagePart* newBodyPart, Kleo::CryptoMessageFormat format,
01114 MessageComposer* composer )
01115 : MessageComposerJob( composer ), mMsg( msg ), mSplitInfo( si ),
01116 mDoSign( doSign ), mDoEncrypt( doEncrypt ), mEncodedBody( encodedBody ),
01117 mBoundaryLevel( boundaryLevel ), mOldBodyPart( oldBodyPart ),
01118 mNewBodyPart( newBodyPart ), mFormat( format ) {}
01119
01120 void execute() {
01121 KMMessagePart tmpNewBodyPart;
01122 tmpNewBodyPart.duplicate( *mNewBodyPart );
01123
01124
01125
01126 mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt,
01127 tmpNewBodyPart, mFormat );
01128 if ( !mComposer->mRc ) {
01129 delete mMsg; mMsg = 0;
01130 return;
01131 }
01132 mComposer->mMessageList.push_back( mMsg );
01133 }
01134
01135 private:
01136 KMMessage* mMsg;
01137 Kleo::KeyResolver::SplitInfo mSplitInfo;
01138 bool mDoSign, mDoEncrypt;
01139 QCString mEncodedBody;
01140 int mBoundaryLevel;
01141 KMMessagePart mOldBodyPart;
01142 KMMessagePart* mNewBodyPart;
01143 Kleo::CryptoMessageFormat mFormat;
01144 };
01145
01146 class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
01147 public:
01148 SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer )
01149 : MessageComposerJob( composer ) {}
01150
01151 void execute() {
01152 KMMessage * last = mComposer->mMessageList.back();
01153 mComposer->mMessageList.pop_back();
01154 mComposer->mMessageList.back()->setUnencryptedMsg( last );
01155 }
01156 };
01157
01158 QCString MessageComposer::bodyText()
01159 {
01160 QCString body = mText;
01161
01162 if (body.isNull()) return body;
01163
01164 if (body.isEmpty()) body = "\n";
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177 if( body[body.length()-1] != '\n' ) {
01178 kdDebug(5006) << "Added an <LF> on the last line" << endl;
01179 body += "\n";
01180 }
01181 return body;
01182 }
01183
01184 void MessageComposer::composeInlineOpenPGPMessage( KMMessage& theMessage,
01185 bool doSign, bool doEncrypt )
01186 {
01187
01188 QCString body = bodyText();
01189 if (body.isNull()) {
01190 mRc = false;
01191 return;
01192 }
01193
01194 mNewBodyPart = 0;
01195 mEarlyAddAttachments = false;
01196 mAllAttachmentsAreInBody = false;
01197
01198
01199 theMessage.deleteBodyParts();
01200 QString oldContentType = theMessage.headerField( "Content-Type" );
01201 theMessage.removeHeaderField("Content-Type");
01202 theMessage.removeHeaderField("Content-Transfer-Encoding");
01203
01204 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01205 = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat );
01206 kdWarning( splitInfos.empty() )
01207 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat"
01208 << endl;
01209 std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it;
01210 for ( it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) {
01211 const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01212 KMMessage* msg = new KMMessage( theMessage );
01213 if ( doEncrypt ) {
01214 Kpgp::Result result;
01215 QByteArray encryptedBody;
01216 if ( doSign ) {
01217 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat );
01218 result = pgpSignedAndEncryptedMsg( encryptedBody, body, signingKeys,
01219 splitInfo.keys, Kleo::InlineOpenPGPFormat );
01220 } else {
01221 result = pgpEncryptedMsg( encryptedBody, body,
01222 splitInfo.keys, Kleo::InlineOpenPGPFormat );
01223 }
01224 if ( result != Kpgp::Ok ) {
01225 mRc = false;
01226 return;
01227 }
01228 assert( !encryptedBody.isNull() );
01229 mOldBodyPart.setBodyEncodedBinary( encryptedBody );
01230 } else {
01231 if ( doSign ) {
01232 pgpSignedMsg( body, Kleo::InlineOpenPGPFormat );
01233 if ( mSignature.isNull() ) {
01234 mRc = false;
01235 return;
01236 }
01237 mOldBodyPart.setBodyEncodedBinary( mSignature );
01238 } else {
01239 assert( !body.isNull() );
01240 mOldBodyPart.setBodyEncoded( body );
01241 }
01242 }
01243 mOldBodyPart.setContentDisposition( "inline" );
01244 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01245 mOldBodyPart.setCharset(mCharset);
01246 addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01247 mMessageList.push_back( msg );
01248 if ( it == splitInfos.begin() ) {
01249 if ( doEncrypt && !saveMessagesEncrypted() ) {
01250 mOldBodyPart.setBodyEncoded( body );
01251 KMMessage* msgUnenc = new KMMessage( theMessage );
01252 addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01253 msg->setUnencryptedMsg( msgUnenc );
01254 }
01255 }
01256 }
01257 }
01258
01259
01260 void MessageComposer::composeChiasmusMessage( KMMessage& theMessage, Kleo::CryptoMessageFormat format )
01261 {
01262 assert( !GlobalSettings::chiasmusKey().isEmpty() );
01263 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
01264 assert( cpf );
01265 const Kleo::CryptoBackend::Protocol * chiasmus
01266 = cpf->protocol( "Chiasmus" );
01267 assert( chiasmus );
01268
01269
01270 QCString body = bodyText();
01271 if (body.isNull()) {
01272 mRc = false;
01273 return;
01274 }
01275
01276 mNewBodyPart = 0;
01277 mEarlyAddAttachments = false;
01278 mAllAttachmentsAreInBody = false;
01279
01280
01281 theMessage.deleteBodyParts();
01282 QString oldContentType = theMessage.headerField( "Content-Type" );
01283 theMessage.removeHeaderField("Content-Type");
01284 theMessage.removeHeaderField("Content-Transfer-Encoding");
01285
01286 QByteArray plainText;
01287 plainText.duplicate( body.data(), body.length() );
01288
01289
01290
01291 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01292 = mKeyResolver->encryptionItems( format );
01293 assert( splitInfos.size() == 1 );
01294 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01295 {
01296 const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01297 KMMessage* msg = new KMMessage( theMessage );
01298 QByteArray encryptedBody;
01299
01300 if ( !encryptWithChiasmus( chiasmus, plainText, encryptedBody ) ) {
01301 mRc = false;
01302 return;
01303 }
01304 assert( !encryptedBody.isNull() );
01305
01306
01307
01308 bool doSign = false;
01309 QValueList<int> allowedCTEs;
01310 mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
01311 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01312 doSign );
01313
01314
01315 mOldBodyPart.setContentDisposition( "inline" );
01316
01317 mOldBodyPart.setOriginalContentTypeStr( "application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
01318
01319 mOldBodyPart.setTypeStr( "application" );
01320 mOldBodyPart.setSubtypeStr( "vnd.de.bund.bsi.chiasmus-text" );
01321 mOldBodyPart.setAdditionalCTypeParamStr( QCString( "chiasmus-charset=" + mCharset ) );
01322 addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01323 mMessageList.push_back( msg );
01324
01325 if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
01326 mOldBodyPart.setBodyEncoded( body );
01327 KMMessage* msgUnenc = new KMMessage( theMessage );
01328 addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01329 msg->setUnencryptedMsg( msgUnenc );
01330 }
01331 }
01332 }
01333
01334 void MessageComposer::composeMessage( KMMessage& theMessage,
01335 bool doSign, bool doEncrypt,
01336 Kleo::CryptoMessageFormat format )
01337 {
01338 #ifdef DEBUG
01339 kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl;
01340 #endif
01341 if ( format == Kleo::InlineOpenPGPFormat ) {
01342 composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
01343 return;
01344 }
01345
01346 if ( mEncryptWithChiasmus )
01347 {
01348 composeChiasmusMessage( theMessage, format );
01349 return;
01350 }
01351
01352
01353
01354 theMessage.setBody( "This message is in MIME format." );
01355
01356
01357 QCString body = bodyText();
01358 if (body.isNull()) {
01359 mRc = false;
01360 return;
01361 }
01362
01363
01364 QString oldContentType = theMessage.headerField( "Content-Type" );
01365 theMessage.deleteBodyParts();
01366 theMessage.removeHeaderField("Content-Type");
01367 theMessage.removeHeaderField("Content-Transfer-Encoding");
01368 theMessage.setAutomaticFields(TRUE);
01369
01370
01371 mNewBodyPart = new KMMessagePart;
01372
01373
01374 mPreviousBoundaryLevel = 0;
01375
01376
01377 const bool doEncryptBody = doEncrypt && mEncryptBody;
01378 const bool doSignBody = doSign && mSignBody;
01379
01380
01381
01382 mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
01383
01384 mAllAttachmentsAreInBody = mEarlyAddAttachments;
01385
01386
01387 if( mEarlyAddAttachments ) {
01388 bool someOk = false;
01389 for ( QValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01390 if ( it->encrypt == doEncryptBody && it->sign == doSignBody )
01391 someOk = true;
01392 else
01393 mAllAttachmentsAreInBody = false;
01394 }
01395 if( !mAllAttachmentsAreInBody && !someOk )
01396 mEarlyAddAttachments = false;
01397 }
01398
01399 kdDebug(5006) << "mEarlyAddAttachments=" << mEarlyAddAttachments << " mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
01400
01401
01402 if ( mIsRichText ) {
01403 mOldBodyPart.setTypeStr( "multipart");
01404 mOldBodyPart.setSubtypeStr(mEarlyAddAttachments ? "mixed" : "alternative");
01405 }
01406 else if( mEarlyAddAttachments ) {
01407 mOldBodyPart.setTypeStr( "multipart" );
01408 mOldBodyPart.setSubtypeStr( "mixed" );
01409 } else
01410 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01411
01412 mOldBodyPart.setContentDisposition( "inline" );
01413
01414 QCString boundaryCStr;
01415 if ( mIsRichText ) {
01416
01417 QCString boundaryCStr;
01418 QCString newbody;
01419 DwMediaType tmpCT;
01420 tmpCT.CreateBoundary( mPreviousBoundaryLevel++ );
01421 boundaryCStr = tmpCT.Boundary().c_str();
01422 QValueList<int> allowedCTEs;
01423
01424 KMMessagePart textBodyPart;
01425 textBodyPart.setTypeStr("text");
01426 textBodyPart.setSubtypeStr("plain");
01427
01428 QCString textbody = plainTextFromMarkup( mText );
01429
01430
01431 textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
01432 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01433 doSign );
01434 textBodyPart.setCharset( mCharset );
01435 textBodyPart.setBodyEncoded( textbody );
01436 DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart );
01437 textDwPart->Assemble();
01438 newbody += "--";
01439 newbody += boundaryCStr;
01440 newbody += "\n";
01441 newbody += textDwPart->AsString().c_str();
01442 delete textDwPart;
01443 textDwPart = 0;
01444
01445 KMMessagePart htmlBodyPart;
01446 htmlBodyPart.setTypeStr("text");
01447 htmlBodyPart.setSubtypeStr("html");
01448 QCString htmlbody = mText;
01449
01450 htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
01451 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01452 doSign );
01453 htmlBodyPart.setCharset( mCharset );
01454 htmlBodyPart.setBodyEncoded( htmlbody );
01455 DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart );
01456 htmlDwPart->Assemble();
01457 newbody += "\n--";
01458 newbody += boundaryCStr;
01459 newbody += "\n";
01460 newbody += htmlDwPart->AsString().c_str();
01461 delete htmlDwPart;
01462 htmlDwPart = 0;
01463
01464 newbody += "--";
01465 newbody += boundaryCStr;
01466 newbody += "--\n";
01467 body = newbody;
01468 mOldBodyPart.setBodyEncoded( newbody );
01469
01470 mSaveBoundary = tmpCT.Boundary();
01471 }
01472
01473
01474 for ( QValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01475
01476
01477
01478
01479
01480
01481
01482 if( it->sign || it->encrypt ) {
01483 QCString cte = it->part->cteStr().lower();
01484 if( ( "8bit" == cte )
01485 || ( ( it->part->type() == DwMime::kTypeText )
01486 && ( "7bit" == cte ) ) ) {
01487 const QByteArray body = it->part->bodyDecodedBinary();
01488 QValueList<int> dummy;
01489 it->part->setBodyAndGuessCte(body, dummy, false, it->sign);
01490 kdDebug(5006) << "Changed encoding of message part from "
01491 << cte << " to " << it->part->cteStr() << endl;
01492 }
01493 }
01494 }
01495
01496 if( mEarlyAddAttachments ) {
01497
01498 ++mPreviousBoundaryLevel;
01499 DwMediaType tmpCT;
01500 tmpCT.CreateBoundary( mPreviousBoundaryLevel );
01501 boundaryCStr = tmpCT.Boundary().c_str();
01502
01503 KMMessagePart innerBodyPart;
01504 if ( mIsRichText ) {
01505 innerBodyPart.setTypeStr( "multipart");
01506 innerBodyPart.setSubtypeStr("alternative");
01507 }
01508 else {
01509 innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01510 }
01511 innerBodyPart.setContentDisposition( "inline" );
01512 QValueList<int> allowedCTEs;
01513
01514 innerBodyPart.setBodyAndGuessCte( body, allowedCTEs,
01515 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01516 doSign );
01517 if ( !mIsRichText )
01518 innerBodyPart.setCharset( mCharset );
01519 innerBodyPart.setBodyEncoded( body );
01520 DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
01521 innerDwPart->Assemble();
01522 if ( mIsRichText ) {
01523 QCString tmpbody = innerDwPart->AsString().c_str();
01524 int boundPos = tmpbody.find( '\n' );
01525 if( -1 < boundPos ) {
01526 QCString bStr( ";\n boundary=\"" );
01527 bStr += mSaveBoundary.c_str();
01528 bStr += "\"";
01529 body = innerDwPart->AsString().c_str();
01530 body.insert( boundPos, bStr );
01531 body = "--" + boundaryCStr + "\n" + body;
01532 }
01533 }
01534 else
01535 body = "--" + boundaryCStr + "\n" + innerDwPart->AsString().c_str();
01536 delete innerDwPart;
01537 innerDwPart = 0;
01538
01539
01540 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01541 if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
01542 innerDwPart = theMessage.createDWBodyPart( it->part );
01543 innerDwPart->Assemble();
01544 body += "\n--" + boundaryCStr + "\n" + innerDwPart->AsString().c_str();
01545 delete innerDwPart;
01546 innerDwPart = 0;
01547 }
01548 }
01549 body += "\n--" + boundaryCStr + "--\n";
01550 } else {
01551 QValueList<int> allowedCTEs;
01552
01553 mOldBodyPart.setBodyAndGuessCte(body, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01554 doSign);
01555 if ( !mIsRichText )
01556 mOldBodyPart.setCharset(mCharset);
01557 }
01558
01559 mOldBodyPart.setBodyEncoded( body );
01560
01561 if( doSignBody || doEncryptBody ) {
01562
01563
01564 DwBodyPart* dwPart;
01565 if ( mIsRichText && !mEarlyAddAttachments ) {
01566
01567
01568 dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01569 DwHeaders& headers = dwPart->Headers();
01570 DwMediaType& ct = headers.ContentType();
01571 ct.SetBoundary(mSaveBoundary);
01572 dwPart->Assemble();
01573 mEncodedBody = dwPart->AsString().c_str();
01574 }
01575 else {
01576 dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01577 dwPart->Assemble();
01578 mEncodedBody = dwPart->AsString().c_str();
01579 }
01580 delete dwPart;
01581 dwPart = 0;
01582
01583
01584 if( !boundaryCStr.isEmpty() ) {
01585 int boundPos = mEncodedBody.find( '\n' );
01586 if( -1 < boundPos ) {
01587
01588 QCString bStr( ";\n boundary=\"" );
01589 bStr += boundaryCStr;
01590 bStr += "\"";
01591 mEncodedBody.insert( boundPos, bStr );
01592 }
01593 }
01594
01595
01596
01597
01598 mEncodedBody = KMail::Util::lf2crlf( mEncodedBody );
01599 }
01600
01601 if ( doSignBody ) {
01602 pgpSignedMsg( mEncodedBody, format );
01603
01604 if ( mSignature.isEmpty() ) {
01605 kdDebug() << "signature was empty" << endl;
01606 mRc = false;
01607 return;
01608 }
01609 mRc = processStructuringInfo( QString::null,
01610 mOldBodyPart.contentDescription(),
01611 mOldBodyPart.typeStr(),
01612 mOldBodyPart.subtypeStr(),
01613 mOldBodyPart.contentDisposition(),
01614 mOldBodyPart.contentTransferEncodingStr(),
01615 mEncodedBody, "signature",
01616 mSignature,
01617 *mNewBodyPart, true, format );
01618 if ( mRc ) {
01619 if ( !makeMultiPartSigned( format ) ) {
01620 mNewBodyPart->setCharset( mCharset );
01621 }
01622 } else
01623 KMessageBox::sorry( mComposeWin,
01624 mErrorProcessingStructuringInfo );
01625 }
01626
01627 if ( !mRc )
01628 return;
01629
01630 continueComposeMessage( theMessage, doSign, doEncrypt, format );
01631 }
01632
01633
01634 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
01635 bool doSign, bool doEncrypt,
01636 Kleo::CryptoMessageFormat format )
01637 {
01638
01639 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01640 = mKeyResolver->encryptionItems( format );
01641 kdWarning( splitInfos.empty() )
01642 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
01643 << Kleo::cryptoMessageFormatToString( format ) << endl;
01644
01645 if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
01646 mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
01647 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
01648 Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
01649 false, mEncodedBody,
01650 mPreviousBoundaryLevel,
01651 mOldBodyPart, mNewBodyPart,
01652 format, this ) );
01653 }
01654
01655 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01656 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
01657 doEncrypt, mEncodedBody,
01658 mPreviousBoundaryLevel,
01659 mOldBodyPart, mNewBodyPart,
01660 format, this ) );
01661 }
01662
01663 void MessageComposer::encryptMessage( KMMessage* msg,
01664 const Kleo::KeyResolver::SplitInfo & splitInfo,
01665 bool doSign, bool doEncrypt,
01666 KMMessagePart newBodyPart,
01667 Kleo::CryptoMessageFormat format )
01668 {
01669 if ( doEncrypt && splitInfo.keys.empty() ) {
01670
01671
01672
01673 doEncrypt = false;
01674 }
01675
01676 const bool doEncryptBody = doEncrypt && mEncryptBody;
01677 const bool doSignBody = doSign && mSignBody;
01678
01679 if ( doEncryptBody ) {
01680 QCString innerContent;
01681 if ( doSignBody ) {
01682
01683 DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
01684 dwPart->Assemble();
01685 innerContent = dwPart->AsString().c_str();
01686 delete dwPart;
01687 dwPart = 0;
01688 } else
01689 innerContent = mEncodedBody;
01690
01691
01692
01693
01694
01695 innerContent = KMail::Util::lf2crlf( innerContent );
01696
01697
01698 QByteArray encryptedBody;
01699 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
01700 splitInfo.keys, format );
01701 if ( result != Kpgp::Ok ) {
01702 mRc = false;
01703 return;
01704 }
01705 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01706 newBodyPart.contentDescription(),
01707 newBodyPart.typeStr(),
01708 newBodyPart.subtypeStr(),
01709 newBodyPart.contentDisposition(),
01710 newBodyPart.contentTransferEncodingStr(),
01711 innerContent,
01712 "encrypted data",
01713 encryptedBody,
01714 newBodyPart, false, format );
01715 if ( !mRc )
01716 KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
01717 }
01718
01719
01720 if( mRc ) {
01721 const bool useNewBodyPart = doSignBody || doEncryptBody;
01722 addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
01723 useNewBodyPart ? newBodyPart : mOldBodyPart, format );
01724 }
01725 }
01726
01727 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
01728 const Kleo::KeyResolver::SplitInfo & splitInfo,
01729 bool doSign, bool doEncrypt,
01730 const KMMessagePart& ourFineBodyPart,
01731 Kleo::CryptoMessageFormat format )
01732 {
01733 const bool doEncryptBody = doEncrypt && mEncryptBody;
01734 const bool doSignBody = doSign && mSignBody;
01735
01736 if( !mAttachments.empty()
01737 && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
01738
01739 msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
01740 msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
01741 msg->headers().ContentType().CreateBoundary( 0 );
01742 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
01743
01744
01745 DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
01746 DwHeaders& headers = tmpDwPart->Headers();
01747 DwMediaType& ct = headers.ContentType();
01748 if ( !mSaveBoundary.empty() )
01749 ct.SetBoundary(mSaveBoundary);
01750 tmpDwPart->Assemble();
01751
01752
01753
01754 msg->addDwBodyPart(tmpDwPart);
01755
01756
01757
01758 KMMessagePart newAttachPart;
01759 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01760
01761 const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
01762
01763 if ( !cryptFlagsDifferent && mEarlyAddAttachments )
01764 continue;
01765
01766 const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
01767 const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
01768
01769 if ( !encryptThisNow && !signThisNow ) {
01770 msg->addBodyPart( it->part );
01771
01772 (void)msg->asDwMessage();
01773 continue;
01774 }
01775
01776 KMMessagePart& rEncryptMessagePart( *it->part );
01777
01778 DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
01779 innerDwPart->Assemble();
01780 QCString encodedAttachment = innerDwPart->AsString().c_str();
01781 delete innerDwPart;
01782 innerDwPart = 0;
01783
01784
01785
01786
01787 encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
01788
01789
01790 if( signThisNow ) {
01791
01792 pgpSignedMsg( encodedAttachment, format );
01793 QByteArray signature = mSignature;
01794 mRc = !signature.isEmpty();
01795 if( mRc ) {
01796 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01797 it->part->contentDescription(),
01798 it->part->typeStr(),
01799 it->part->subtypeStr(),
01800 it->part->contentDisposition(),
01801 it->part->contentTransferEncodingStr(),
01802 encodedAttachment,
01803 "signature",
01804 signature,
01805 newAttachPart, true, format );
01806 if( mRc ) {
01807 if( encryptThisNow ) {
01808 rEncryptMessagePart = newAttachPart;
01809 DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
01810 dwPart->Assemble();
01811 encodedAttachment = dwPart->AsString().c_str();
01812 delete dwPart;
01813 dwPart = 0;
01814 }
01815 } else
01816 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01817 } else {
01818
01819 break;
01820 }
01821 }
01822 if( encryptThisNow ) {
01823 QByteArray encryptedBody;
01824 Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
01825 encodedAttachment,
01826 splitInfo.keys,
01827 format );
01828
01829 if( Kpgp::Ok == result ) {
01830 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01831 rEncryptMessagePart.contentDescription(),
01832 rEncryptMessagePart.typeStr(),
01833 rEncryptMessagePart.subtypeStr(),
01834 rEncryptMessagePart.contentDisposition(),
01835 rEncryptMessagePart.contentTransferEncodingStr(),
01836 encodedAttachment,
01837 "encrypted data",
01838 encryptedBody,
01839 newAttachPart, false, format );
01840 if ( !mRc )
01841 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01842 } else
01843 mRc = false;
01844 }
01845 msg->addBodyPart( &newAttachPart );
01846 (void)msg->asDwMessage();
01847 }
01848 } else {
01849 if( ourFineBodyPart.originalContentTypeStr() ) {
01850 msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
01851 msg->headers().ContentType().Parse();
01852 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
01853 } else {
01854 msg->headers().ContentType().FromString( ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr() );
01855 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ourFineBodyPart.typeStr() << "/" << ourFineBodyPart.subtypeStr() << endl;
01856 }
01857 if ( !ourFineBodyPart.charset().isEmpty() )
01858 msg->setCharset( ourFineBodyPart.charset() );
01859 msg->setHeaderField( "Content-Transfer-Encoding",
01860 ourFineBodyPart.contentTransferEncodingStr() );
01861 msg->setHeaderField( "Content-Description",
01862 ourFineBodyPart.contentDescription() );
01863 msg->setHeaderField( "Content-Disposition",
01864 ourFineBodyPart.contentDisposition() );
01865
01866 if ( mDebugComposerCrypto )
01867 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
01868
01869
01870 if ( mIsRichText && !(doSign || doEncrypt) ) {
01871 msg->headers().ContentType().SetBoundary( mSaveBoundary );
01872 msg->headers().ContentType().Assemble();
01873 }
01874 msg->setBody(ourFineBodyPart.body() );
01875
01876 }
01877
01878 msg->setHeaderField( "X-KMail-Recipients",
01879 splitInfo.recipients.join(", "), KMMessage::Address );
01880
01881 if ( mDebugComposerCrypto ) {
01882 kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
01883 msg->headers().Assemble();
01884 kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
01885 }
01886 }
01887
01888
01889
01890 bool MessageComposer::processStructuringInfo( const QString bugURL,
01891 const QString contentDescClear,
01892 const QCString contentTypeClear,
01893 const QCString contentSubtypeClear,
01894 const QCString contentDispClear,
01895 const QCString contentTEncClear,
01896 const QCString& clearCStr,
01897 const QString ,
01898 const QByteArray& ciphertext,
01899 KMMessagePart& resultingPart,
01900 bool signing, Kleo::CryptoMessageFormat format )
01901 {
01902 bool bOk = true;
01903
01904 if ( makeMimeObject( format, signing ) ) {
01905 QCString mainHeader = "Content-Type: ";
01906 const char * toplevelCT = toplevelContentType( format, signing );
01907 if ( toplevelCT )
01908 mainHeader += toplevelCT;
01909 else {
01910 if( makeMultiMime( format, signing ) )
01911 mainHeader += "text/plain";
01912 else
01913 mainHeader += contentTypeClear + '/' + contentSubtypeClear;
01914 }
01915
01916 const QCString boundaryCStr = KMime::multiPartBoundary();
01917
01918 if ( makeMultiMime( format, signing ) )
01919 mainHeader.replace( "%boundary", boundaryCStr );
01920
01921 if ( toplevelCT ) {
01922 if ( const char * str = toplevelContentDisposition( format, signing ) ) {
01923 mainHeader += "\nContent-Disposition: ";
01924 mainHeader += str;
01925 }
01926 if ( !makeMultiMime( format, signing ) &&
01927 binaryHint( format ) )
01928 mainHeader += "\nContent-Transfer-Encoding: base64";
01929 } else {
01930 if( 0 < contentDispClear.length() ) {
01931 mainHeader += "\nContent-Disposition: ";
01932 mainHeader += contentDispClear;
01933 }
01934 if( 0 < contentTEncClear.length() ) {
01935 mainHeader += "\nContent-Transfer-Encoding: ";
01936 mainHeader += contentTEncClear;
01937 }
01938 }
01939
01940
01941
01942 DwString mainDwStr;
01943 mainDwStr = mainHeader + "\n\n";
01944 DwBodyPart mainDwPa( mainDwStr, 0 );
01945 mainDwPa.Parse();
01946 KMMessage::bodyPart( &mainDwPa, &resultingPart );
01947 if( !makeMultiMime( format, signing ) ) {
01948 if ( signing && includeCleartextWhenSigning( format ) ) {
01949 QCString bodyText( clearCStr );
01950 bodyText += '\n';
01951 bodyText += QCString( ciphertext.data(), ciphertext.size() + 1 );
01952 resultingPart.setBodyEncoded( bodyText );
01953 } else
01954 resultingPart.setBodyEncodedBinary( ciphertext );
01955 } else {
01956
01957
01958
01959
01960 QCString versCStr, codeCStr;
01961 if ( !signing && format == Kleo::OpenPGPMIMEFormat )
01962 versCStr =
01963 "Content-Type: application/pgp-encrypted\n"
01964 "Content-Disposition: attachment\n"
01965 "\n"
01966 "Version: 1";
01967
01968
01969
01970 const char * nestedCT = nestedContentType( format, signing );
01971 assert( nestedCT );
01972 codeCStr = "Content-Type: ";
01973 codeCStr += nestedCT;
01974 codeCStr += '\n';
01975 if ( const char * str = nestedContentDisposition( format, signing ) ) {
01976 codeCStr += "Content-Disposition: ";
01977 codeCStr += str;
01978 codeCStr += '\n';
01979 }
01980 if ( binaryHint( format ) ) {
01981 codeCStr += "Content-Transfer-Encoding: base64\n\n";
01982 codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext );
01983 } else
01984 codeCStr += '\n' + QCString( ciphertext.data(), ciphertext.size() + 1 );
01985
01986
01987 QCString mainStr = "--" + boundaryCStr;
01988 if ( signing && includeCleartextWhenSigning( format ) &&
01989 !clearCStr.isEmpty() )
01990 mainStr += "\n" + clearCStr + "\n--" + boundaryCStr;
01991 if ( !versCStr.isEmpty() )
01992 mainStr += "\n" + versCStr + "\n--" + boundaryCStr;
01993 if( !codeCStr.isEmpty() )
01994 mainStr += "\n" + codeCStr + "\n--" + boundaryCStr;
01995 mainStr += "--\n";
01996
01997
01998 resultingPart.setBodyEncoded( mainStr );
01999 }
02000
02001 } else {
02002
02003 resultingPart.setContentDescription( contentDescClear );
02004 resultingPart.setTypeStr( contentTypeClear );
02005 resultingPart.setSubtypeStr( contentSubtypeClear );
02006 resultingPart.setContentDisposition( contentDispClear );
02007 resultingPart.setContentTransferEncodingStr( contentTEncClear );
02008 QCString resultingBody;
02009
02010 if ( signing && includeCleartextWhenSigning( format ) ) {
02011 if( !clearCStr.isEmpty() )
02012 resultingBody += clearCStr;
02013 }
02014 if ( !ciphertext.isEmpty() )
02015 resultingBody += QCString( ciphertext.data(), ciphertext.size() + 1 );
02016 else {
02017
02018 KMessageBox::sorry( mComposeWin,
02019 i18n( "<qt><p>Error: The backend did not return "
02020 "any encoded data.</p>"
02021 "<p>Please report this bug:<br>%2</p></qt>" )
02022 .arg( bugURL ) );
02023 bOk = false;
02024 }
02025 resultingPart.setBodyEncoded( resultingBody );
02026 }
02027
02028 return bOk;
02029 }
02030
02031
02032 QCString MessageComposer::plainTextFromMarkup( const QString& markupText )
02033 {
02034 QTextEdit *hackConspiratorTextEdit = new QTextEdit( markupText );
02035 hackConspiratorTextEdit->setTextFormat(Qt::PlainText);
02036 if ( !mDisableBreaking ) {
02037 hackConspiratorTextEdit->setWordWrap( QTextEdit::FixedColumnWidth );
02038 hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
02039 }
02040 QString text = hackConspiratorTextEdit->text();
02041 QCString textbody;
02042
02043 const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02044 if( mCharset == "us-ascii" ) {
02045 textbody = KMMsgBase::toUsAscii( text );
02046 } else if( codec == 0 ) {
02047 kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02048 textbody = text.local8Bit();
02049 } else {
02050 textbody = codec->fromUnicode( text );
02051 }
02052 if (textbody.isNull()) textbody = "";
02053
02054 delete hackConspiratorTextEdit;
02055 return textbody;
02056 }
02057
02058
02059 QCString MessageComposer::breakLinesAndApplyCodec()
02060 {
02061 QString text;
02062 QCString cText;
02063
02064 if( mDisableBreaking || mIsRichText )
02065 text = mComposeWin->mEditor->text();
02066 else
02067 text = mComposeWin->mEditor->brokenText();
02068 text.truncate( text.length() );
02069
02070 QString newText;
02071 const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02072
02073 if( mCharset == "us-ascii" ) {
02074 cText = KMMsgBase::toUsAscii( text );
02075 newText = QString::fromLatin1( cText );
02076 } else if( codec == 0 ) {
02077 kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02078 cText = text.local8Bit();
02079 newText = QString::fromLocal8Bit( cText );
02080 } else {
02081 cText = codec->fromUnicode( text );
02082 newText = codec->toUnicode( cText );
02083 }
02084 if (cText.isNull()) cText = "";
02085
02086 if( !text.isEmpty() && (newText != text) ) {
02087 QString oldText = mComposeWin->mEditor->text();
02088 mComposeWin->mEditor->setText( newText );
02089 KCursorSaver idle( KBusyPtr::idle() );
02090 bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
02091 i18n("<qt>Not all characters fit into the chosen"
02092 " encoding.<br><br>Send the message anyway?</qt>"),
02093 i18n("Some Characters Will Be Lost"),
02094 i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
02095 if( !anyway ) {
02096 mComposeWin->mEditor->setText(oldText);
02097 return QCString();
02098 }
02099 }
02100
02101 return cText;
02102 }
02103
02104
02105
02106 void MessageComposer::pgpSignedMsg( const QCString & cText, Kleo::CryptoMessageFormat format ) {
02107
02108 mSignature = QByteArray();
02109
02110 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
02111
02112 assert( !signingKeys.empty() );
02113
02114
02115 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02116 assert( cpf );
02117 const Kleo::CryptoBackend::Protocol * proto
02118 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02119 assert( proto );
02120
02121 std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
02122 textMode( format ) ) );
02123
02124 if ( !job.get() ) {
02125 KMessageBox::sorry( mComposeWin,
02126 i18n("This message could not be signed, "
02127 "since the chosen backend does not seem to support "
02128 "signing; this should actually never happen, "
02129 "please report this bug.") );
02130 return;
02131 }
02132
02133 QByteArray plainText;
02134 plainText.duplicate( cText.data(), cText.length() );
02135 QByteArray signature;
02136 const GpgME::SigningResult res =
02137 job->exec( signingKeys, plainText, signingMode( format ), signature );
02138 if ( res.error().isCanceled() ) {
02139 kdDebug() << "signing was canceled by user" << endl;
02140 return;
02141 }
02142 if ( res.error() ) {
02143 kdDebug() << "signing failed: " << res.error().asString() << endl;
02144 job->showErrorDialog( mComposeWin );
02145 return;
02146 }
02147
02148 mSignature = signature;
02149 Q_ASSERT( !mSignature.isNull() );
02150 if ( mSignature.isNull() ) {
02151 KMessageBox::error( mComposeWin, i18n( "The signing operation failed for an unknown reason." ) );
02152 }
02153 }
02154
02155
02156 Kpgp::Result MessageComposer::pgpEncryptedMsg( QByteArray & encryptedBody,
02157 const QCString & cText,
02158 const std::vector<GpgME::Key> & encryptionKeys,
02159 Kleo::CryptoMessageFormat format )
02160 {
02161
02162 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02163 assert( cpf );
02164 const Kleo::CryptoBackend::Protocol * proto
02165 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02166 assert( proto );
02167
02168 std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
02169 textMode( format ) ) );
02170 if ( !job.get() ) {
02171 KMessageBox::sorry( mComposeWin,
02172 i18n("This message could not be encrypted, "
02173 "since the chosen backend does not seem to support "
02174 "encryption; this should actually never happen, "
02175 "please report this bug.") );
02176 return Kpgp::Failure;
02177 }
02178
02179 QByteArray plainText;
02180 plainText.duplicate( cText.data(), cText.length() );
02181
02182 const GpgME::EncryptionResult res =
02183 job->exec( encryptionKeys, plainText, false, encryptedBody );
02184 if ( res.error().isCanceled() ) {
02185 kdDebug() << "encryption was canceled by user" << endl;
02186 return Kpgp::Canceled;
02187 }
02188 if ( res.error() ) {
02189 kdDebug() << "encryption failed: " << res.error().asString() << endl;
02190 job->showErrorDialog( mComposeWin );
02191 return Kpgp::Failure;
02192 }
02193 return Kpgp::Ok;
02194 }
02195
02196 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( QByteArray & encryptedBody,
02197 const QCString & cText,
02198 const std::vector<GpgME::Key> & signingKeys,
02199 const std::vector<GpgME::Key> & encryptionKeys,
02200 Kleo::CryptoMessageFormat format )
02201 {
02202
02203 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02204 assert( cpf );
02205 const Kleo::CryptoBackend::Protocol * proto
02206 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02207 assert( proto );
02208
02209 std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
02210 textMode( format ) ) );
02211 if ( !job.get() ) {
02212 KMessageBox::sorry( mComposeWin,
02213 i18n("This message could not be signed and encrypted, "
02214 "since the chosen backend does not seem to support "
02215 "combined signing and encryption; this should actually never happen, "
02216 "please report this bug.") );
02217 return Kpgp::Failure;
02218 }
02219
02220 QByteArray plainText;
02221 plainText.duplicate( cText.data(), cText.length() );
02222
02223 const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
02224 job->exec( signingKeys, encryptionKeys, plainText, false, encryptedBody );
02225 if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
02226 kdDebug() << "encrypt/sign was canceled by user" << endl;
02227 return Kpgp::Canceled;
02228 }
02229 if ( res.first.error() || res.second.error() ) {
02230 if ( res.first.error() )
02231 kdDebug() << "signing failed: " << res.first.error().asString() << endl;
02232 else
02233 kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
02234 job->showErrorDialog( mComposeWin );
02235 return Kpgp::Failure;
02236 }
02237 return Kpgp::Ok;
02238 }
02239
02240
02241 #include "messagecomposer.moc"