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 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" : "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 )
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
01594 pgpSignedMsg( mEncodedBody, format );
01595
01596 if ( mSignature.isEmpty() ) {
01597 kdDebug() << "signature was empty" << endl;
01598 mRc = false;
01599 return;
01600 }
01601 mRc = processStructuringInfo( QString::null,
01602 mOldBodyPart.contentDescription(),
01603 mOldBodyPart.typeStr(),
01604 mOldBodyPart.subtypeStr(),
01605 mOldBodyPart.contentDisposition(),
01606 mOldBodyPart.contentTransferEncodingStr(),
01607 mEncodedBody, "signature",
01608 mSignature,
01609 *mNewBodyPart, true, format );
01610 if ( mRc ) {
01611 if ( !makeMultiPartSigned( format ) ) {
01612 mNewBodyPart->setCharset( mCharset );
01613 }
01614 } else
01615 KMessageBox::sorry( mComposeWin,
01616 mErrorProcessingStructuringInfo );
01617 }
01618
01619 if ( !mRc )
01620 return;
01621
01622 continueComposeMessage( theMessage, doSign, doEncrypt, format );
01623 }
01624
01625
01626 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
01627 bool doSign, bool doEncrypt,
01628 Kleo::CryptoMessageFormat format )
01629 {
01630
01631 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01632 = mKeyResolver->encryptionItems( format );
01633 kdWarning( splitInfos.empty() )
01634 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
01635 << Kleo::cryptoMessageFormatToString( format ) << endl;
01636
01637 if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
01638 mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
01639 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
01640 Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
01641 false, mEncodedBody,
01642 mPreviousBoundaryLevel,
01643 mNewBodyPart,
01644 format, this ) );
01645 }
01646
01647 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01648 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
01649 doEncrypt, mEncodedBody,
01650 mPreviousBoundaryLevel,
01651 mNewBodyPart,
01652 format, this ) );
01653 }
01654
01655 void MessageComposer::encryptMessage( KMMessage* msg,
01656 const Kleo::KeyResolver::SplitInfo & splitInfo,
01657 bool doSign, bool doEncrypt,
01658 KMMessagePart newBodyPart,
01659 Kleo::CryptoMessageFormat format )
01660 {
01661 if ( doEncrypt && splitInfo.keys.empty() ) {
01662
01663
01664
01665 doEncrypt = false;
01666 }
01667
01668 const bool doEncryptBody = doEncrypt && mEncryptBody;
01669 const bool doSignBody = doSign && mSignBody;
01670
01671 if ( doEncryptBody ) {
01672 QByteArray innerContent;
01673 if ( doSignBody ) {
01674
01675 DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
01676 dwPart->Assemble();
01677 innerContent = KMail::Util::ByteArray( dwPart->AsString() );
01678 delete dwPart;
01679 dwPart = 0;
01680 } else {
01681 innerContent = mEncodedBody;
01682 }
01683
01684
01685
01686
01687
01688 innerContent = KMail::Util::lf2crlf( innerContent );
01689
01690
01691 QByteArray encryptedBody;
01692 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
01693 splitInfo.keys, format );
01694 if ( result != Kpgp::Ok ) {
01695 mRc = false;
01696 return;
01697 }
01698 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01699 newBodyPart.contentDescription(),
01700 newBodyPart.typeStr(),
01701 newBodyPart.subtypeStr(),
01702 newBodyPart.contentDisposition(),
01703 newBodyPart.contentTransferEncodingStr(),
01704 innerContent,
01705 "encrypted data",
01706 encryptedBody,
01707 newBodyPart, false, format );
01708 if ( !mRc )
01709 KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
01710 }
01711
01712
01713 if( mRc ) {
01714 const bool useNewBodyPart = doSignBody || doEncryptBody;
01715 addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
01716 useNewBodyPart ? newBodyPart : mOldBodyPart, format );
01717 }
01718 }
01719
01720 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
01721 const Kleo::KeyResolver::SplitInfo & splitInfo,
01722 bool doSign, bool doEncrypt,
01723 const KMMessagePart& ourFineBodyPart,
01724 Kleo::CryptoMessageFormat format )
01725 {
01726 const bool doEncryptBody = doEncrypt && mEncryptBody;
01727 const bool doSignBody = doSign && mSignBody;
01728
01729 if( !mAttachments.empty()
01730 && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
01731
01732 msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
01733 msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
01734 msg->headers().ContentType().CreateBoundary( 0 );
01735 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
01736
01737
01738 DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
01739 DwHeaders& headers = tmpDwPart->Headers();
01740 DwMediaType& ct = headers.ContentType();
01741 if ( !mSaveBoundary.empty() )
01742 ct.SetBoundary(mSaveBoundary);
01743 tmpDwPart->Assemble();
01744
01745
01746
01747 msg->addDwBodyPart(tmpDwPart);
01748
01749
01750
01751 KMMessagePart newAttachPart;
01752 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01753
01754 const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
01755
01756 if ( !cryptFlagsDifferent && mEarlyAddAttachments )
01757 continue;
01758
01759 const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
01760 const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
01761
01762 if ( !encryptThisNow && !signThisNow ) {
01763 msg->addBodyPart( it->part );
01764
01765 (void)msg->asDwMessage();
01766 continue;
01767 }
01768
01769 KMMessagePart& rEncryptMessagePart( *it->part );
01770
01771 DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
01772 innerDwPart->Assemble();
01773 QByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
01774 delete innerDwPart;
01775 innerDwPart = 0;
01776
01777
01778
01779
01780 encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
01781
01782
01783 if( signThisNow ) {
01784 pgpSignedMsg( encodedAttachment, format );
01785 mRc = !mSignature.isEmpty();
01786 if( mRc ) {
01787 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01788 it->part->contentDescription(),
01789 it->part->typeStr(),
01790 it->part->subtypeStr(),
01791 it->part->contentDisposition(),
01792 it->part->contentTransferEncodingStr(),
01793 encodedAttachment,
01794 "signature",
01795 mSignature,
01796 newAttachPart, true, format );
01797 if( mRc ) {
01798 if( encryptThisNow ) {
01799 rEncryptMessagePart = newAttachPart;
01800 DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
01801 dwPart->Assemble();
01802 encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
01803 delete dwPart;
01804 dwPart = 0;
01805 }
01806 } else
01807 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01808 } else {
01809
01810 break;
01811 }
01812 }
01813 if( encryptThisNow ) {
01814 QByteArray encryptedBody;
01815 Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
01816 encodedAttachment,
01817 splitInfo.keys,
01818 format );
01819
01820 if( Kpgp::Ok == result ) {
01821 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01822 rEncryptMessagePart.contentDescription(),
01823 rEncryptMessagePart.typeStr(),
01824 rEncryptMessagePart.subtypeStr(),
01825 rEncryptMessagePart.contentDisposition(),
01826 rEncryptMessagePart.contentTransferEncodingStr(),
01827 encodedAttachment,
01828 "encrypted data",
01829 encryptedBody,
01830 newAttachPart, false, format );
01831 if ( !mRc )
01832 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01833 } else
01834 mRc = false;
01835 }
01836 msg->addBodyPart( &newAttachPart );
01837 (void)msg->asDwMessage();
01838 }
01839 } else {
01840 if( ourFineBodyPart.originalContentTypeStr() ) {
01841 msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
01842 msg->headers().ContentType().Parse();
01843 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
01844 } else {
01845 QCString ct = ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr();
01846 if ( ct == "multipart/mixed" )
01847 ct += ";\n\tboundary=\"" + mMultipartMixedBoundary + '"';
01848 else if ( ct == "multipart/alternative" )
01849 ct += ";\n\tboundary=\"" + QCString(mSaveBoundary.c_str()) + '"';
01850 msg->headers().ContentType().FromString( ct );
01851 msg->headers().ContentType().Parse();
01852 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
01853 }
01854 if ( !ourFineBodyPart.charset().isEmpty() )
01855 msg->setCharset( ourFineBodyPart.charset() );
01856 msg->setHeaderField( "Content-Transfer-Encoding",
01857 ourFineBodyPart.contentTransferEncodingStr() );
01858 msg->setHeaderField( "Content-Description",
01859 ourFineBodyPart.contentDescription() );
01860 msg->setHeaderField( "Content-Disposition",
01861 ourFineBodyPart.contentDisposition() );
01862
01863 if ( mDebugComposerCrypto )
01864 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
01865
01866
01867 msg->setBody( ourFineBodyPart.dwBody() );
01868
01869 }
01870
01871 msg->setHeaderField( "X-KMail-Recipients",
01872 splitInfo.recipients.join(", "), KMMessage::Address );
01873
01874 if ( mDebugComposerCrypto ) {
01875 kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
01876 msg->headers().Assemble();
01877 kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
01878 }
01879 }
01880
01881
01882
01883 bool MessageComposer::processStructuringInfo( const QString bugURL,
01884 const QString contentDescClear,
01885 const QCString contentTypeClear,
01886 const QCString contentSubtypeClear,
01887 const QCString contentDispClear,
01888 const QCString contentTEncClear,
01889 const QByteArray& clearCStr,
01890 const QString ,
01891 const QByteArray& ciphertext,
01892 KMMessagePart& resultingPart,
01893 bool signing, Kleo::CryptoMessageFormat format )
01894 {
01895 assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' );
01896 bool bOk = true;
01897
01898 if ( makeMimeObject( format, signing ) ) {
01899 QCString mainHeader = "Content-Type: ";
01900 const char * toplevelCT = toplevelContentType( format, signing );
01901 if ( toplevelCT )
01902 mainHeader += toplevelCT;
01903 else {
01904 if( makeMultiMime( format, signing ) )
01905 mainHeader += "text/plain";
01906 else
01907 mainHeader += contentTypeClear + '/' + contentSubtypeClear;
01908 }
01909
01910 const QCString boundaryCStr = KMime::multiPartBoundary();
01911
01912 if ( makeMultiMime( format, signing ) )
01913 mainHeader.replace( "%boundary", boundaryCStr );
01914
01915 if ( toplevelCT ) {
01916 if ( const char * str = toplevelContentDisposition( format, signing ) ) {
01917 mainHeader += "\nContent-Disposition: ";
01918 mainHeader += str;
01919 }
01920 if ( !makeMultiMime( format, signing ) &&
01921 binaryHint( format ) )
01922 mainHeader += "\nContent-Transfer-Encoding: base64";
01923 } else {
01924 if( 0 < contentDispClear.length() ) {
01925 mainHeader += "\nContent-Disposition: ";
01926 mainHeader += contentDispClear;
01927 }
01928 if( 0 < contentTEncClear.length() ) {
01929 mainHeader += "\nContent-Transfer-Encoding: ";
01930 mainHeader += contentTEncClear;
01931 }
01932 }
01933
01934
01935
01936 DwString mainDwStr;
01937 mainDwStr = mainHeader + "\n\n";
01938 DwBodyPart mainDwPa( mainDwStr, 0 );
01939 mainDwPa.Parse();
01940 KMMessage::bodyPart( &mainDwPa, &resultingPart );
01941 if( !makeMultiMime( format, signing ) ) {
01942 if ( signing && includeCleartextWhenSigning( format ) ) {
01943 QByteArray bodyText( clearCStr );
01944 KMail::Util::append( bodyText, "\n" );
01945 KMail::Util::append( bodyText, ciphertext );
01946 resultingPart.setBodyEncodedBinary( bodyText );
01947 } else {
01948 resultingPart.setBodyEncodedBinary( ciphertext );
01949 }
01950 } else {
01951
01952
01953
01954
01955 QCString versCStr, codeCStr;
01956 if ( !signing && format == Kleo::OpenPGPMIMEFormat )
01957 versCStr =
01958 "Content-Type: application/pgp-encrypted\n"
01959 "Content-Disposition: attachment\n"
01960 "\n"
01961 "Version: 1";
01962
01963
01964
01965 const char * nestedCT = nestedContentType( format, signing );
01966 assert( nestedCT );
01967 codeCStr = "Content-Type: ";
01968 codeCStr += nestedCT;
01969 codeCStr += '\n';
01970 if ( const char * str = nestedContentDisposition( format, signing ) ) {
01971 codeCStr += "Content-Disposition: ";
01972 codeCStr += str;
01973 codeCStr += '\n';
01974 }
01975 if ( binaryHint( format ) ) {
01976 codeCStr += "Content-Transfer-Encoding: base64\n\n";
01977 codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext );
01978 } else
01979 codeCStr += '\n' + QCString( ciphertext.data(), ciphertext.size() + 1 );
01980
01981
01982 QByteArray mainStr;
01983 KMail::Util::append( mainStr, "--" );
01984 KMail::Util::append( mainStr, boundaryCStr );
01985 if ( signing && includeCleartextWhenSigning( format ) &&
01986 !clearCStr.isEmpty() ) {
01987 KMail::Util::append( mainStr, "\n" );
01988
01989 KMail::Util::append( mainStr, clearCStr );
01990 KMail::Util::append( mainStr, "\n--" + boundaryCStr );
01991 }
01992 if ( !versCStr.isEmpty() )
01993 KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
01994 if( !codeCStr.isEmpty() )
01995 KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
01996 KMail::Util::append( mainStr, "--\n" );
01997
01998
01999 resultingPart.setBodyEncodedBinary( mainStr );
02000 }
02001
02002 } else {
02003
02004 resultingPart.setContentDescription( contentDescClear );
02005 resultingPart.setTypeStr( contentTypeClear );
02006 resultingPart.setSubtypeStr( contentSubtypeClear );
02007 resultingPart.setContentDisposition( contentDispClear );
02008 resultingPart.setContentTransferEncodingStr( contentTEncClear );
02009 QByteArray resultingBody;
02010
02011 if ( signing && includeCleartextWhenSigning( format ) ) {
02012 if( !clearCStr.isEmpty() )
02013 KMail::Util::append( resultingBody, clearCStr );
02014 }
02015 if ( !ciphertext.isEmpty() )
02016 KMail::Util::append( resultingBody, ciphertext );
02017 else {
02018
02019 KMessageBox::sorry( mComposeWin,
02020 i18n( "<qt><p>Error: The backend did not return "
02021 "any encoded data.</p>"
02022 "<p>Please report this bug:<br>%2</p></qt>" )
02023 .arg( bugURL ) );
02024 bOk = false;
02025 }
02026 resultingPart.setBodyEncodedBinary( resultingBody );
02027 }
02028
02029 return bOk;
02030 }
02031
02032
02033 QCString MessageComposer::plainTextFromMarkup( const QString& markupText )
02034 {
02035 QTextEdit *hackConspiratorTextEdit = new QTextEdit( markupText );
02036 hackConspiratorTextEdit->setTextFormat(Qt::PlainText);
02037 if ( !mDisableBreaking ) {
02038 hackConspiratorTextEdit->setWordWrap( QTextEdit::FixedColumnWidth );
02039 hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
02040 }
02041 QString text = hackConspiratorTextEdit->text();
02042 QCString textbody;
02043
02044 const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02045 if( mCharset == "us-ascii" ) {
02046 textbody = KMMsgBase::toUsAscii( text );
02047 } else if( codec == 0 ) {
02048 kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02049 textbody = text.local8Bit();
02050 } else {
02051 textbody = codec->fromUnicode( text );
02052 }
02053 if (textbody.isNull()) textbody = "";
02054
02055 delete hackConspiratorTextEdit;
02056 return textbody;
02057 }
02058
02059
02060 QByteArray MessageComposer::breakLinesAndApplyCodec()
02061 {
02062 QString text;
02063 QCString cText;
02064
02065 if( mDisableBreaking || mIsRichText )
02066 text = mComposeWin->mEditor->text();
02067 else
02068 text = mComposeWin->mEditor->brokenText();
02069 text.truncate( text.length() );
02070
02071 QString newText;
02072 const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02073
02074 if( mCharset == "us-ascii" ) {
02075 cText = KMMsgBase::toUsAscii( text );
02076 newText = QString::fromLatin1( cText );
02077 } else if( codec == 0 ) {
02078 kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02079 cText = text.local8Bit();
02080 newText = QString::fromLocal8Bit( cText );
02081 } else {
02082 cText = codec->fromUnicode( text );
02083 newText = codec->toUnicode( cText );
02084 }
02085 if (cText.isNull()) cText = "";
02086
02087 if( !text.isEmpty() && (newText != text) ) {
02088 QString oldText = mComposeWin->mEditor->text();
02089 mComposeWin->mEditor->setText( newText );
02090 KCursorSaver idle( KBusyPtr::idle() );
02091 bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
02092 i18n("<qt>Not all characters fit into the chosen"
02093 " encoding.<br><br>Send the message anyway?</qt>"),
02094 i18n("Some Characters Will Be Lost"),
02095 i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
02096 if( !anyway ) {
02097 mComposeWin->mEditor->setText(oldText);
02098 return QByteArray();
02099 }
02100 }
02101
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113 if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
02114 kdDebug(5006) << "Added an <LF> on the last line" << endl;
02115 cText += "\n";
02116 }
02117 return KMail::Util::byteArrayFromQCStringNoDetach( cText );
02118 }
02119
02120
02121
02122 void MessageComposer::pgpSignedMsg( const QByteArray& cText, Kleo::CryptoMessageFormat format ) {
02123
02124 assert( cText.isEmpty() || cText[cText.size()-1] != '\0' );
02125 mSignature = QByteArray();
02126
02127 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
02128
02129 assert( !signingKeys.empty() );
02130
02131
02132 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02133 assert( cpf );
02134 const Kleo::CryptoBackend::Protocol * proto
02135 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02136 assert( proto );
02137
02138 std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
02139 textMode( format ) ) );
02140
02141 if ( !job.get() ) {
02142 KMessageBox::sorry( mComposeWin,
02143 i18n("This message could not be signed, "
02144 "since the chosen backend does not seem to support "
02145 "signing; this should actually never happen, "
02146 "please report this bug.") );
02147 return;
02148 }
02149
02150 QByteArray signature;
02151 const GpgME::SigningResult res =
02152 job->exec( signingKeys, cText, signingMode( format ), signature );
02153 if ( res.error().isCanceled() ) {
02154 kdDebug() << "signing was canceled by user" << endl;
02155 return;
02156 }
02157 if ( res.error() ) {
02158 kdDebug() << "signing failed: " << res.error().asString() << endl;
02159 job->showErrorDialog( mComposeWin );
02160 return;
02161 }
02162
02163 mSignature = signature;
02164 if ( mSignature.isEmpty() ) {
02165 KMessageBox::sorry( mComposeWin,
02166 i18n( "The signing operation failed. "
02167 "Please make sure that the gpg-agent program "
02168 "is running." ) );
02169 }
02170 }
02171
02172
02173 Kpgp::Result MessageComposer::pgpEncryptedMsg( QByteArray & encryptedBody,
02174 const QByteArray& cText,
02175 const std::vector<GpgME::Key> & encryptionKeys,
02176 Kleo::CryptoMessageFormat format )
02177 {
02178
02179 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02180 assert( cpf );
02181 const Kleo::CryptoBackend::Protocol * proto
02182 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02183 assert( proto );
02184
02185 std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
02186 textMode( format ) ) );
02187 if ( !job.get() ) {
02188 KMessageBox::sorry( mComposeWin,
02189 i18n("This message could not be encrypted, "
02190 "since the chosen backend does not seem to support "
02191 "encryption; this should actually never happen, "
02192 "please report this bug.") );
02193 return Kpgp::Failure;
02194 }
02195
02196 const GpgME::EncryptionResult res =
02197 job->exec( encryptionKeys, cText, false, encryptedBody );
02198 if ( res.error().isCanceled() ) {
02199 kdDebug() << "encryption was canceled by user" << endl;
02200 return Kpgp::Canceled;
02201 }
02202 if ( res.error() ) {
02203 kdDebug() << "encryption failed: " << res.error().asString() << endl;
02204 job->showErrorDialog( mComposeWin );
02205 return Kpgp::Failure;
02206 }
02207 return Kpgp::Ok;
02208 }
02209
02210 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( QByteArray & encryptedBody,
02211 const QByteArray& cText,
02212 const std::vector<GpgME::Key> & signingKeys,
02213 const std::vector<GpgME::Key> & encryptionKeys,
02214 Kleo::CryptoMessageFormat format )
02215 {
02216
02217 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02218 assert( cpf );
02219 const Kleo::CryptoBackend::Protocol * proto
02220 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02221 assert( proto );
02222
02223 std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
02224 textMode( format ) ) );
02225 if ( !job.get() ) {
02226 KMessageBox::sorry( mComposeWin,
02227 i18n("This message could not be signed and encrypted, "
02228 "since the chosen backend does not seem to support "
02229 "combined signing and encryption; this should actually never happen, "
02230 "please report this bug.") );
02231 return Kpgp::Failure;
02232 }
02233
02234 const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
02235 job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
02236 if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
02237 kdDebug() << "encrypt/sign was canceled by user" << endl;
02238 return Kpgp::Canceled;
02239 }
02240 if ( res.first.error() || res.second.error() ) {
02241 if ( res.first.error() )
02242 kdDebug() << "signing failed: " << res.first.error().asString() << endl;
02243 else
02244 kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
02245 job->showErrorDialog( mComposeWin );
02246 return Kpgp::Failure;
02247 }
02248 return Kpgp::Ok;
02249 }
02250
02251
02252 #include "messagecomposer.moc"