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