35 #include "messagecomposer.h"
36 #include "kmmsgpart.h"
37 #define REALLY_WANT_KMCOMPOSEWIN_H
38 #include "kmcomposewin.h"
39 #undef REALLY_WANT_KMCOMPOSEWIN_H
40 #include "tdelistboxdialog.h"
41 #include "kcursorsaver.h"
42 #include "messagesender.h"
44 #include "kmfoldercombobox.h"
45 #include "keyresolver.h"
46 #include "kleo_util.h"
47 #include "globalsettings.h"
48 #include "custommimeheader.h"
52 #include <libkpimidentities/identity.h>
53 #include <libkpimidentities/identitymanager.h>
54 #include <libemailfunctions/email.h>
56 #include <ui/keyselectiondialog.h>
57 #include <ui/keyapprovaldialog.h>
58 #include <ui/messagebox.h>
59 #include <kleo/cryptobackendfactory.h>
60 #include <kleo/keylistjob.h>
61 #include <kleo/encryptjob.h>
62 #include <kleo/signencryptjob.h>
63 #include <kleo/signjob.h>
64 #include <kleo/specialjob.h>
66 #include <kmime_util.h>
67 #include <kmime_codecs.h>
68 #include <kpgpblock.h>
70 #include <mimelib/mimepp.h>
72 #include <tdemessagebox.h>
73 #include <tdelocale.h>
74 #include <kinputdialog.h>
76 #include <tdeaction.h>
78 #include <tqtextcodec.h>
79 #include <tqtextedit.h>
82 #include <gpgmepp/key.h>
83 #include <gpgmepp/keylistresult.h>
84 #include <gpgmepp/encryptionresult.h>
85 #include <gpgmepp/signingresult.h>
86 #include <gpgmepp/context.h>
95 static inline bool warnSendUnsigned() {
96 TDEConfigGroup group( KMKernel::config(),
"Composer" );
97 return group.readBoolEntry(
"crypto-warning-unsigned",
false );
99 static inline bool warnSendUnencrypted() {
100 TDEConfigGroup group( KMKernel::config(),
"Composer" );
101 return group.readBoolEntry(
"crypto-warning-unencrypted",
false );
103 static inline bool saveMessagesEncrypted() {
104 TDEConfigGroup group( KMKernel::config(),
"Composer" );
105 return group.readBoolEntry(
"crypto-store-encrypted",
true );
107 static inline bool encryptToSelf() {
109 TDEConfigGroup group( KMKernel::config(),
"Composer" );
110 return group.readBoolEntry(
"crypto-encrypt-to-self",
true );
112 static inline bool showKeyApprovalDialog() {
113 TDEConfigGroup group( KMKernel::config(),
"Composer" );
114 return group.readBoolEntry(
"crypto-show-keys-for-approval",
true );
117 static inline int encryptKeyNearExpiryWarningThresholdInDays() {
118 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
119 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
121 const int num = composer.readNumEntry(
"crypto-warn-encr-key-near-expire-int", 14 );
122 return kMax( 1, num );
125 static inline int signingKeyNearExpiryWarningThresholdInDays() {
126 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
127 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
129 const int num = composer.readNumEntry(
"crypto-warn-sign-key-near-expire-int", 14 );
130 return kMax( 1, num );
133 static inline int encryptRootCertNearExpiryWarningThresholdInDays() {
134 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
135 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
137 const int num = composer.readNumEntry(
"crypto-warn-encr-root-near-expire-int", 14 );
138 return kMax( 1, num );
141 static inline int signingRootCertNearExpiryWarningThresholdInDays() {
142 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
143 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
145 const int num = composer.readNumEntry(
"crypto-warn-sign-root-near-expire-int", 14 );
146 return kMax( 1, num );
149 static inline int encryptChainCertNearExpiryWarningThresholdInDays() {
150 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
151 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
153 const int num = composer.readNumEntry(
"crypto-warn-encr-chaincert-near-expire-int", 14 );
154 return kMax( 1, num );
157 static inline int signingChainCertNearExpiryWarningThresholdInDays() {
158 const TDEConfigGroup composer( KMKernel::config(),
"Composer" );
159 if ( ! composer.readBoolEntry(
"crypto-warn-when-near-expire",
true ) )
161 const int num = composer.readNumEntry(
"crypto-warn-sign-chaincert-near-expire-int", 14 );
162 return kMax( 1, num );
222 static TQString mErrorProcessingStructuringInfo =
223 i18n(
"<qt><p>Structuring information returned by the Crypto plug-in "
224 "could not be processed correctly; the plug-in might be damaged.</p>"
225 "<p>Please contact your system administrator.</p></qt>");
226 static TQString mErrorNoCryptPlugAndNoBuildIn =
227 i18n(
"<p>No active Crypto Plug-In was found and the built-in OpenPGP code "
228 "did not run successfully.</p>"
229 "<p>You can do two things to change this:</p>"
230 "<ul><li><em>either</em> activate a Plug-In using the "
231 "Settings->Configure KMail->Plug-In dialog.</li>"
232 "<li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
233 "Identity->Advanced tab.</li></ul>");
236 class MessageComposerJob {
238 MessageComposerJob( MessageComposer* composer ) : mComposer( composer ) {}
239 virtual ~MessageComposerJob() {}
241 virtual void execute() = 0;
246 void adjustCryptFlags() { mComposer->adjustCryptFlags(); }
247 void composeMessage() { mComposer->composeMessage(); }
248 void continueComposeMessage(
KMMessage& msg,
bool doSign,
bool doEncrypt,
249 Kleo::CryptoMessageFormat format )
251 mComposer->continueComposeMessage( msg, doSign, doEncrypt, format );
253 void chiasmusEncryptAllAttachments() {
254 mComposer->chiasmusEncryptAllAttachments();
257 MessageComposer* mComposer;
260 class ChiasmusBodyPartEncryptJob :
public MessageComposerJob {
262 ChiasmusBodyPartEncryptJob( MessageComposer * composer )
263 : MessageComposerJob( composer ) {}
266 chiasmusEncryptAllAttachments();
270 class AdjustCryptFlagsJob :
public MessageComposerJob {
272 AdjustCryptFlagsJob( MessageComposer* composer )
273 : MessageComposerJob( composer ) {}
280 class ComposeMessageJob :
public MessageComposerJob {
282 ComposeMessageJob( MessageComposer* composer )
283 : MessageComposerJob( composer ) {}
290 MessageComposer::MessageComposer( KMComposeWin* win,
const char* name )
291 : TQObject( win, name ), mComposeWin( win ), mCurrentJob( 0 ),
292 mReferenceMessage( 0 ), mKeyResolver( 0 ),
293 mUseOpportunisticEncryption( false ),
294 mSignBody( false ), mEncryptBody( false ),
295 mSigningRequested( false ), mEncryptionRequested( false ),
296 mDoSign( false ), mDoEncrypt( false ),
297 mAllowedCryptoMessageFormats( 0 ),
298 mDisableCrypto( false ),
299 mDisableBreaking( false ),
300 mDebugComposerCrypto( false ),
301 mAutoCharset( true ),
302 mIsRichText( false ),
303 mIdentityUid( 0 ), mRc( true ),
306 mEarlyAddAttachments( false ), mAllAttachmentsAreInBody( false ),
307 mPreviousBoundaryLevel( 0 ),
308 mEncryptWithChiasmus( false ),
309 mPerformingSignOperation( false )
313 MessageComposer::~MessageComposer()
315 delete mKeyResolver; mKeyResolver = 0;
316 delete mNewBodyPart; mNewBodyPart = 0;
319 void MessageComposer::applyChanges(
bool disableCrypto )
322 if( getenv(
"KMAIL_DEBUG_COMPOSER_CRYPTO") != 0 ) {
323 TQCString cE = getenv(
"KMAIL_DEBUG_COMPOSER_CRYPTO");
324 mDebugComposerCrypto = cE ==
"1" || cE.upper() ==
"ON" || cE.upper() ==
"TRUE";
325 kdDebug(5006) <<
"KMAIL_DEBUG_COMPOSER_CRYPTO = TRUE" << endl;
327 mDebugComposerCrypto =
false;
328 kdDebug(5006) <<
"KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl;
334 mDisableCrypto = disableCrypto;
338 readFromComposeWin();
343 mJobs.push_back(
new ChiasmusBodyPartEncryptJob(
this ) );
346 mJobs.push_back(
new AdjustCryptFlagsJob(
this ) );
349 mJobs.push_back(
new ComposeMessageJob(
this ) );
355 void MessageComposer::doNextJob()
357 delete mCurrentJob; mCurrentJob = 0;
359 if( mJobs.isEmpty() ) {
367 while( !mJobs.isEmpty() ) {
368 delete mJobs.front();
376 TQTimer::singleShot( 0,
this, TQ_SLOT( slotDoNextJob() ) );
379 void MessageComposer::emitDone(
bool b )
382 mEncodedBody = TQByteArray();
383 delete mNewBodyPart; mNewBodyPart = 0;
384 mOldBodyPart.clear();
388 void MessageComposer::slotDoNextJob()
390 assert( !mCurrentJob );
396 assert( !mJobs.empty() );
398 mCurrentJob = mJobs.front();
399 assert( mCurrentJob );
403 mCurrentJob->execute();
411 void MessageComposer::readFromComposeWin()
414 mDisableBreaking =
false;
416 mSignBody = mComposeWin->mSignAction->isChecked();
417 mSigningRequested = mSignBody;
418 mEncryptBody = mComposeWin->mEncryptAction->isChecked();
419 mEncryptionRequested = mEncryptBody;
421 mAutoCharset = mComposeWin->mAutoCharset;
422 mCharset = mComposeWin->mCharset;
423 mReferenceMessage = mComposeWin->mMsg;
435 if ( mReferenceMessage->type() == DwMime::kTypeMultipart )
436 mReferenceMessage->setHeaderField(
"Content-Type",
"text/plain" );
437 mUseOpportunisticEncryption = GlobalSettings::self()->pgpAutoEncrypt();
438 mAllowedCryptoMessageFormats = mComposeWin->cryptoMessageFormat();
442 if( charset.isEmpty() )
444 KMessageBox::sorry( mComposeWin,
445 i18n(
"No suitable encoding could be found for "
446 "your message.\nPlease set an encoding "
447 "using the 'Options' menu." ) );
453 mComposeWin->mCharset = charset;
455 mReferenceMessage->setCharset(mCharset);
457 mReferenceMessage->setTo(mComposeWin->to());
458 mReferenceMessage->setFrom(mComposeWin->from());
459 mReferenceMessage->setCc(mComposeWin->cc());
460 mReferenceMessage->setSubject(mComposeWin->subject());
461 mReferenceMessage->setReplyTo(mComposeWin->replyTo());
462 mReferenceMessage->setBcc(mComposeWin->bcc());
464 const KPIM::Identity &
id = mComposeWin->identity();
466 KMFolder *f = mComposeWin->mFcc->getFolder();
469 mReferenceMessage->removeHeaderField(
"X-KMail-Fcc");
471 mReferenceMessage->setFcc( f->
idString() );
474 mReferenceMessage->setDrafts(
id.drafts() );
477 mReferenceMessage->removeHeaderField(
"X-KMail-Identity");
478 else mReferenceMessage->setHeaderField(
"X-KMail-Identity", TQString::number(
id.uoid() ));
481 if (!mComposeWin->replyTo().isEmpty()) replyAddr = mComposeWin->replyTo();
482 else replyAddr = mComposeWin->from();
484 if (mComposeWin->mRequestMDNAction->isChecked())
485 mReferenceMessage->setHeaderField(
"Disposition-Notification-To", replyAddr);
487 mReferenceMessage->removeHeaderField(
"Disposition-Notification-To");
489 if (mComposeWin->mUrgentAction->isChecked()) {
490 mReferenceMessage->setHeaderField(
"X-PRIORITY",
"2 (High)");
491 mReferenceMessage->setHeaderField(
"Priority",
"urgent");
493 mReferenceMessage->removeHeaderField(
"X-PRIORITY");
494 mReferenceMessage->removeHeaderField(
"Priority");
497 int num = GlobalSettings::self()->custHeaderCount();
498 for(
int ix=0; ix<num; ix++) {
499 CustomMimeHeader customMimeHeader( TQString::number(ix) );
500 customMimeHeader.readConfig();
501 mReferenceMessage->setHeaderField(
502 KMMsgBase::toUsAscii( customMimeHeader.custHeaderName() ),
503 customMimeHeader.custHeaderValue() );
512 mBcc = mComposeWin->bcc();
513 mTo = KPIM::splitEmailAddrList( mComposeWin->to().stripWhiteSpace() );
514 mCc = KPIM::splitEmailAddrList( mComposeWin->cc().stripWhiteSpace() );
515 mBccList = KPIM::splitEmailAddrList( mBcc.stripWhiteSpace() );
517 for (
unsigned int i = 0 ; i < mComposeWin->mAtmList.count() ; ++i )
518 mAttachments.push_back( Attachment( mComposeWin->mAtmList.at(i),
519 mComposeWin->signFlagOfAttachment( i ),
520 mComposeWin->encryptFlagOfAttachment( i ) ) );
522 mEncryptWithChiasmus = mComposeWin->mEncryptWithChiasmus;
524 mIsRichText = mComposeWin->mEditor->textFormat() == TQt::RichText;
525 mIdentityUid = mComposeWin->identityUid();
526 mText = breakLinesAndApplyCodec();
527 assert( mText.isEmpty() || mText[mText.size()-1] ==
'\n' );
531 mLineBreakColumn = mComposeWin->mEditor->lineBreakColumn();
534 static TQCString escape_quoted_string(
const TQCString & str ) {
536 const unsigned int str_len = str.length();
537 result.resize( 2*str_len + 1 );
538 char * d = result.data();
539 for (
unsigned int i = 0 ; i < str_len ; ++i )
540 switch (
const char ch = str[i] ) {
547 result.truncate( d - result.begin() );
551 bool MessageComposer::encryptWithChiasmus(
const Kleo::CryptoBackend::Protocol * chiasmus,
552 const TQByteArray& body,
553 TQByteArray& resultData )
555 std::unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob(
"x-encrypt", TQStringVariantMap() ) );
557 const TQString msg = i18n(
"Chiasmus backend does not offer the "
558 "\"x-encrypt\" function. Please report this bug." );
559 KMessageBox::error( mComposeWin, msg, i18n(
"Chiasmus Backend Error" ) );
562 if ( !job->setProperty(
"key", GlobalSettings::chiasmusKey() ) ||
563 !job->setProperty(
"options", GlobalSettings::chiasmusOptions() ) ||
564 !job->setProperty(
"input", body ) ) {
565 const TQString msg = i18n(
"The \"x-encrypt\" function does not accept "
566 "the expected parameters. Please report this bug." );
567 KMessageBox::error( mComposeWin, msg, i18n(
"Chiasmus Backend Error" ) );
570 const GpgME::Error err = job->exec();
571 if ( err.isCanceled() || err ) {
573 job->showErrorDialog( mComposeWin, i18n(
"Chiasmus Encryption Error" ) );
576 const TQVariant result = job->property(
"result" );
577 if ( result.type() != TQVariant::ByteArray ) {
578 const TQString msg = i18n(
"Unexpected return value from Chiasmus backend: "
579 "The \"x-encrypt\" function did not return a "
580 "byte array. Please report this bug." );
581 KMessageBox::error( mComposeWin, msg, i18n(
"Chiasmus Backend Error" ) );
584 resultData = result.toByteArray();
588 void MessageComposer::chiasmusEncryptAllAttachments() {
589 if ( !mEncryptWithChiasmus )
591 assert( !GlobalSettings::chiasmusKey().isEmpty() );
592 if ( mAttachments.empty() )
594 const Kleo::CryptoBackend::Protocol * chiasmus
595 = Kleo::CryptoBackendFactory::instance()->protocol(
"Chiasmus" );
599 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin(), end = mAttachments.end() ; it != end ; ++it ) {
600 KMMessagePart * part = it->part;
601 const TQString filename = part->fileName();
602 if ( filename.endsWith(
".xia",
false ) )
604 const TQByteArray body = part->bodyDecodedBinary();
605 TQByteArray resultData;
606 if ( !encryptWithChiasmus( chiasmus, body, resultData ) ) {
611 TQValueList<int> dummy;
612 part->setBodyAndGuessCte( resultData, dummy );
613 part->setTypeStr(
"application" );
614 part->setSubtypeStr(
"vnd.de.bund.bsi.chiasmus" );
615 part->setName( filename +
".xia" );
616 const TQCString enc_name = KMMsgBase::encodeRFC2231StringAutoDetectCharset(
617 filename +
".xia", part->charset() );
618 const TQCString cDisp =
"attachment;\n\tfilename"
619 + ( TQString( enc_name ) != filename +
".xia"
621 :
"=\"" + escape_quoted_string( enc_name ) +
'\"' );
622 part->setContentDisposition( cDisp );
626 void MessageComposer::adjustCryptFlags()
628 if ( !mDisableCrypto &&
629 mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat &&
630 !mAttachments.empty() &&
631 ( mSigningRequested || mEncryptionRequested ) )
634 if ( mAllowedCryptoMessageFormats == Kleo::InlineOpenPGPFormat ) {
635 ret = KMessageBox::warningYesNoCancel( mComposeWin,
636 i18n(
"The inline OpenPGP crypto message format "
637 "does not support encryption or signing "
639 "Really use deprecated inline OpenPGP?"),
640 i18n(
"Insecure Message Format"),
641 i18n(
"Use Inline OpenPGP"),
642 i18n(
"Use OpenPGP/MIME") );
647 ret = KMessageBox::No;
650 if ( ret == KMessageBox::Cancel ) {
653 }
else if ( ret == KMessageBox::No ) {
654 mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat;
655 mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat;
656 if ( mSigningRequested ) {
658 for (
unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
659 mAttachments[idx].sign =
true;
661 if ( mEncryptionRequested ) {
664 for (
unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
665 mAttachments[idx].encrypt =
true;
672 mUseOpportunisticEncryption, mAllowedCryptoMessageFormats,
673 encryptKeyNearExpiryWarningThresholdInDays(),
674 signingKeyNearExpiryWarningThresholdInDays(),
675 encryptRootCertNearExpiryWarningThresholdInDays(),
676 signingRootCertNearExpiryWarningThresholdInDays(),
677 encryptChainCertNearExpiryWarningThresholdInDays(),
678 signingChainCertNearExpiryWarningThresholdInDays() );
680 if ( !mDisableCrypto ) {
681 const KPIM::Identity &
id =
682 kmkernel->identityManager()->identityForUoidOrDefault( mIdentityUid );
684 TQStringList encryptToSelfKeys;
685 if ( !
id.pgpEncryptionKey().isEmpty() )
686 encryptToSelfKeys.push_back(
id.pgpEncryptionKey() );
687 if ( !
id.smimeEncryptionKey().isEmpty() )
688 encryptToSelfKeys.push_back(
id.smimeEncryptionKey() );
689 if ( mKeyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) {
694 TQStringList signKeys;
695 if ( !
id.pgpSigningKey().isEmpty() )
696 signKeys.push_back( mPGPSigningKey =
id.pgpSigningKey() );
697 if ( !
id.smimeSigningKey().isEmpty() )
698 signKeys.push_back( mSMIMESigningKey =
id.smimeSigningKey() );
699 if ( mKeyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) {
705 mKeyResolver->setPrimaryRecipients( mTo + mCc );
706 mKeyResolver->setSecondaryRecipients( mBccList );
709 bool doSignCompletely = mSigningRequested;
710 bool doEncryptCompletely = mEncryptionRequested;
711 for (
unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) {
712 if ( mAttachments[idx].encrypt )
713 mEncryptionRequested =
true;
715 doEncryptCompletely =
false;
716 if ( mAttachments[idx].sign )
717 mSigningRequested =
true;
719 doSignCompletely =
false;
722 mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely );
727 mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely );
735 if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok )
739 bool MessageComposer::determineWhetherToSign(
bool doSignCompletely ) {
741 switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) {
743 if ( !mSigningRequested ) {
744 markAllAttachmentsForSigning(
true );
752 case Kleo::AskOpportunistic:
758 const TQString msg = i18n(
"Examination of the recipient's signing preferences "
759 "yielded that you be asked whether or not to sign "
761 "Sign this message?");
762 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
763 i18n(
"Sign Message?"),
764 i18n(
"to sign",
"&Sign"),
765 i18n(
"Do &Not Sign") ) ) {
766 case KMessageBox::Cancel:
769 case KMessageBox::Yes:
770 markAllAttachmentsForSigning(
true );
772 case KMessageBox::No:
773 markAllAttachmentsForSigning(
false );
782 const TQString msg = i18n(
"There are conflicting signing preferences "
783 "for these recipients.\n"
784 "Sign this message?");
785 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
786 i18n(
"Sign Message?"),
787 i18n(
"to sign",
"&Sign"),
788 i18n(
"Do &Not Sign") ) ) {
789 case KMessageBox::Cancel:
792 case KMessageBox::Yes:
793 markAllAttachmentsForSigning(
true );
795 case KMessageBox::No:
796 markAllAttachmentsForSigning(
false );
801 case Kleo::Impossible:
804 const TQString msg = i18n(
"You have requested to sign this message, "
805 "but no valid signing keys have been configured "
806 "for this identity.");
807 if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
808 i18n(
"Send Unsigned?"),
809 i18n(
"Send &Unsigned") )
810 == KMessageBox::Cancel ) {
814 markAllAttachmentsForSigning(
false );
820 if ( !sign || !doSignCompletely ) {
821 if ( warnSendUnsigned() ) {
823 const TQString msg = sign && !doSignCompletely
824 ? i18n(
"Some parts of this message will not be signed.\n"
825 "Sending only partially signed messages might violate site policy.\n"
826 "Sign all parts instead?")
827 : i18n(
"This message will not be signed.\n"
828 "Sending unsigned message might violate site policy.\n"
829 "Sign message instead?") ;
830 const TQString buttonText = sign && !doSignCompletely
831 ? i18n(
"&Sign All Parts") : i18n(
"&Sign") ;
832 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
833 i18n(
"Unsigned-Message Warning"),
835 i18n(
"Send &As Is") ) ) {
836 case KMessageBox::Cancel:
839 case KMessageBox::Yes:
840 markAllAttachmentsForSigning(
true );
842 case KMessageBox::No:
843 return sign || doSignCompletely;
848 return sign || doSignCompletely ;
851 bool MessageComposer::determineWhetherToEncrypt(
bool doEncryptCompletely ) {
852 bool encrypt =
false;
853 bool opportunistic =
false;
854 switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) {
856 if ( !mEncryptionRequested ) {
857 markAllAttachmentsForEncryption(
true );
865 case Kleo::AskOpportunistic:
866 opportunistic =
true;
872 const TQString msg = opportunistic
873 ? i18n(
"Valid trusted encryption keys were found for all recipients.\n"
874 "Encrypt this message?")
875 : i18n(
"Examination of the recipient's encryption preferences "
876 "yielded that you be asked whether or not to encrypt "
878 "Encrypt this message?");
879 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
880 i18n(
"Encrypt Message?"),
882 ? i18n(
"Sign && &Encrypt")
886 : i18n(
"&Send As-Is") ) ) {
887 case KMessageBox::Cancel:
890 case KMessageBox::Yes:
891 markAllAttachmentsForEncryption(
true );
893 case KMessageBox::No:
894 markAllAttachmentsForEncryption(
false );
903 const TQString msg = i18n(
"There are conflicting encryption preferences "
904 "for these recipients.\n"
905 "Encrypt this message?");
906 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
907 i18n(
"Encrypt Message?"),
909 i18n(
"Do &Not Encrypt") ) ) {
910 case KMessageBox::Cancel:
913 case KMessageBox::Yes:
914 markAllAttachmentsForEncryption(
true );
916 case KMessageBox::No:
917 markAllAttachmentsForEncryption(
false );
922 case Kleo::Impossible:
925 const TQString msg = i18n(
"You have requested to encrypt this message, "
926 "and to encrypt a copy to yourself, "
927 "but no valid trusted encryption keys have been "
928 "configured for this identity.");
929 if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
930 i18n(
"Send Unencrypted?"),
931 i18n(
"Send &Unencrypted") )
932 == KMessageBox::Cancel ) {
936 markAllAttachmentsForEncryption(
false );
942 if ( !encrypt || !doEncryptCompletely ) {
943 if ( warnSendUnencrypted() ) {
945 const TQString msg = !doEncryptCompletely
946 ? i18n(
"Some parts of this message will not be encrypted.\n"
947 "Sending only partially encrypted messages might violate site policy "
948 "and/or leak sensitive information.\n"
949 "Encrypt all parts instead?")
950 : i18n(
"This message will not be encrypted.\n"
951 "Sending unencrypted messages might violate site policy and/or "
952 "leak sensitive information.\n"
953 "Encrypt messages instead?") ;
954 const TQString buttonText = !doEncryptCompletely
955 ? i18n(
"&Encrypt All Parts") : i18n(
"&Encrypt") ;
956 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
957 i18n(
"Unencrypted Message Warning"),
961 : i18n(
"&Send As-Is") ) ) {
962 case KMessageBox::Cancel:
965 case KMessageBox::Yes:
966 markAllAttachmentsForEncryption(
true );
968 case KMessageBox::No:
969 return encrypt || doEncryptCompletely;
974 return encrypt || doEncryptCompletely ;
977 void MessageComposer::markAllAttachmentsForSigning(
bool sign ) {
979 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
983 void MessageComposer::markAllAttachmentsForEncryption(
bool enc ) {
985 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
990 void MessageComposer::composeMessage()
992 for (
unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
993 if ( mKeyResolver->encryptionItems( concreteCryptoMessageFormats[i] ).empty() )
996 composeMessage( *msg, mDoSign, mDoEncrypt, concreteCryptoMessageFormats[i] );
1007 static inline bool makeMultiMime( Kleo::CryptoMessageFormat f,
bool sign ) {
1010 case Kleo::InlineOpenPGPFormat:
1011 case Kleo::SMIMEOpaqueFormat:
return false;
1012 case Kleo::OpenPGPMIMEFormat:
return true;
1013 case Kleo::SMIMEFormat:
return sign;
1016 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
1017 return makeMultiMime( f,
true );
1019 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
1020 return makeMultiMime( f,
false );
1023 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f,
bool ) {
1024 return f != Kleo::InlineOpenPGPFormat;
1027 static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f,
bool signing ) {
1030 case Kleo::InlineOpenPGPFormat:
return 0;
1031 case Kleo::OpenPGPMIMEFormat:
1033 "multipart/signed;\n\t"
1034 "boundary=\"%boundary\";\n\t"
1035 "protocol=\"application/pgp-signature\";\n\t"
1038 "multipart/encrypted;\n\t"
1039 "boundary=\"%boundary\";\n\t"
1040 "protocol=\"application/pgp-encrypted\""
1042 case Kleo::SMIMEFormat:
1045 "multipart/signed;\n\t"
1046 "boundary=\"%boundary\";\n\t"
1047 "protocol=\"application/pkcs7-signature\";\n\t"
1052 case Kleo::SMIMEOpaqueFormat:
1054 "application/pkcs7-mime;\n\t"
1055 "smime-type=signed-data;\n\t"
1056 "name=\"smime.p7m\";\n\t"
1058 "application/pkcs7-mime;\n\t"
1059 "smime-type=enveloped-data;\n\t"
1060 "name=\"smime.p7m\";\n\t"
1065 static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f,
bool signing ) {
1068 case Kleo::InlineOpenPGPFormat:
1069 case Kleo::OpenPGPMIMEFormat:
1071 case Kleo::SMIMEFormat:
1074 case Kleo::SMIMEOpaqueFormat:
1075 return "attachment; filename=\"smime.p7m\"";
1079 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
1080 return makeMultiPartSigned( f );
1083 static inline const char * nestedContentType( Kleo::CryptoMessageFormat f,
bool signing ) {
1085 case Kleo::OpenPGPMIMEFormat:
1086 return signing ?
"application/pgp-signature; name=signature.asc \nContent-Description: This is a digitally signed message part." :
"application/octet-stream" ;
1087 case Kleo::SMIMEFormat:
1089 return "application/pkcs7-signature; name=\"smime.p7s\"";
1092 case Kleo::InlineOpenPGPFormat:
1093 case Kleo::SMIMEOpaqueFormat:
1098 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f,
bool signing ) {
1099 if ( !signing && f == Kleo::OpenPGPMIMEFormat )
1100 return "inline; filename=\"msg.asc\"";
1101 if ( signing && f == Kleo::SMIMEFormat )
1102 return "attachment; filename=\"smime.p7s\"";
1106 static inline bool binaryHint( Kleo::CryptoMessageFormat f ) {
1108 case Kleo::SMIMEFormat:
1109 case Kleo::SMIMEOpaqueFormat:
1112 case Kleo::OpenPGPMIMEFormat:
1113 case Kleo::InlineOpenPGPFormat:
1118 static inline bool armor( Kleo::CryptoMessageFormat f ) {
1119 return !binaryHint( f );
1122 static inline bool textMode( Kleo::CryptoMessageFormat f ) {
1123 return f == Kleo::InlineOpenPGPFormat;
1126 static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) {
1128 case Kleo::SMIMEOpaqueFormat:
1129 return GpgME::Context::Normal;
1130 case Kleo::InlineOpenPGPFormat:
1131 return GpgME::Context::Clearsigned;
1133 case Kleo::SMIMEFormat:
1134 case Kleo::OpenPGPMIMEFormat:
1135 return GpgME::Context::Detached;
1143 class EncryptMessageJob :
public MessageComposerJob {
1145 EncryptMessageJob(
KMMessage* msg,
const Kleo::KeyResolver::SplitInfo & si,
1146 bool doSign,
bool doEncrypt,
const TQByteArray& encodedBody,
1148 KMMessagePart* newBodyPart, Kleo::CryptoMessageFormat format,
1149 MessageComposer* composer )
1150 : MessageComposerJob( composer ), mMsg( msg ), mSplitInfo( si ),
1151 mDoSign( doSign ), mDoEncrypt( doEncrypt ), mEncodedBody( encodedBody ),
1152 mBoundaryLevel( boundaryLevel ),
1153 mNewBodyPart( newBodyPart ), mFormat( format ) {}
1156 KMMessagePart tmpNewBodyPart;
1157 tmpNewBodyPart.duplicate( *mNewBodyPart );
1161 mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt,
1162 tmpNewBodyPart, mFormat );
1163 if ( !mComposer->mRc ) {
1164 delete mMsg; mMsg = 0;
1167 mComposer->mMessageList.push_back( mMsg );
1172 Kleo::KeyResolver::SplitInfo mSplitInfo;
1173 bool mDoSign, mDoEncrypt;
1174 TQByteArray mEncodedBody;
1177 KMMessagePart* mNewBodyPart;
1178 Kleo::CryptoMessageFormat mFormat;
1181 class SetLastMessageAsUnencryptedVersionOfLastButOne :
public MessageComposerJob {
1183 SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer )
1184 : MessageComposerJob( composer ) {}
1187 KMMessage * last = mComposer->mMessageList.back();
1188 mComposer->mMessageList.pop_back();
1193 void MessageComposer::composeInlineOpenPGPMessage(
KMMessage& theMessage,
1194 bool doSign,
bool doEncrypt )
1197 const TQByteArray bodyData = mText;
1198 if (bodyData.isNull()) {
1204 mEarlyAddAttachments =
false;
1205 mAllAttachmentsAreInBody =
false;
1209 TQString oldContentType = theMessage.
headerField(
"Content-Type" );
1213 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1214 = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat );
1215 kdWarning( splitInfos.empty() )
1216 <<
"MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat"
1218 std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it;
1219 for ( it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) {
1220 const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
1223 Kpgp::Result result;
1224 TQByteArray encryptedBody;
1226 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat );
1227 result = pgpSignedAndEncryptedMsg( encryptedBody, bodyData, signingKeys,
1228 splitInfo.keys, Kleo::InlineOpenPGPFormat );
1230 result = pgpEncryptedMsg( encryptedBody, bodyData,
1231 splitInfo.keys, Kleo::InlineOpenPGPFormat );
1233 if ( result != Kpgp::Ok ) {
1237 assert( !encryptedBody.isNull() );
1238 mOldBodyPart.setBodyEncodedBinary( encryptedBody );
1241 pgpSignedMsg( bodyData, Kleo::InlineOpenPGPFormat );
1242 if ( mSignature.isNull() ) {
1246 mOldBodyPart.setBodyEncodedBinary( mSignature );
1248 assert( !bodyData.isNull() );
1249 mOldBodyPart.setBodyEncodedBinary( bodyData );
1252 mOldBodyPart.setContentDisposition(
"inline" );
1253 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1254 if (mOldBodyPart.type() == DwMime::kTypeText) {
1255 mOldBodyPart.setCharset(mCharset);
1257 addBodyAndAttachments( msg, splitInfo,
false,
false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1258 mMessageList.push_back( msg );
1259 if ( it == splitInfos.begin() ) {
1260 if ( doEncrypt && !saveMessagesEncrypted() ) {
1261 mOldBodyPart.setBodyEncodedBinary( bodyData );
1263 addBodyAndAttachments( msgUnenc, splitInfo,
false,
false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1271 void MessageComposer::composeChiasmusMessage(
KMMessage& theMessage, Kleo::CryptoMessageFormat format )
1273 assert( !GlobalSettings::chiasmusKey().isEmpty() );
1274 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
1276 const Kleo::CryptoBackend::Protocol * chiasmus
1277 = cpf->protocol(
"Chiasmus" );
1281 const TQByteArray bodyData = mText;
1282 if (bodyData.isNull()) {
1288 mEarlyAddAttachments =
false;
1289 mAllAttachmentsAreInBody =
false;
1293 TQString oldContentType = theMessage.
headerField(
"Content-Type" );
1299 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1300 = mKeyResolver->encryptionItems( format );
1301 assert( splitInfos.size() == 1 );
1302 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
1304 const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
1306 TQByteArray encryptedBody;
1308 if ( !encryptWithChiasmus( chiasmus, bodyData, encryptedBody ) ) {
1312 assert( !encryptedBody.isNull() );
1316 bool doSign =
false;
1317 TQValueList<int> allowedCTEs;
1318 mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
1319 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1323 mOldBodyPart.setContentDisposition(
"inline" );
1325 mOldBodyPart.setOriginalContentTypeStr(
"application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
1327 mOldBodyPart.setTypeStr(
"application" );
1328 mOldBodyPart.setSubtypeStr(
"vnd.de.bund.bsi.chiasmus-text" );
1329 mOldBodyPart.setAdditionalCTypeParamStr( TQCString(
"chiasmus-charset=" + mCharset ) );
1330 addBodyAndAttachments( msg, splitInfo,
false,
false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1331 mMessageList.push_back( msg );
1333 if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
1334 mOldBodyPart.setBodyEncodedBinary( bodyData );
1336 addBodyAndAttachments( msgUnenc, splitInfo,
false,
false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1342 void MessageComposer::composeMessage(
KMMessage& theMessage,
1343 bool doSign,
bool doEncrypt,
1344 Kleo::CryptoMessageFormat format )
1347 kdDebug(5006) <<
"entering KMComposeWin::composeMessage" << endl;
1349 if ( format == Kleo::InlineOpenPGPFormat ) {
1350 composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
1354 if ( mEncryptWithChiasmus )
1356 composeChiasmusMessage( theMessage, format );
1362 theMessage.
setBody(
"This message is in MIME format." );
1365 TQByteArray bodyData = mText;
1366 if (bodyData.isNull()) {
1372 TQString oldContentType = theMessage.
headerField(
"Content-Type" );
1379 mNewBodyPart =
new KMMessagePart;
1382 mPreviousBoundaryLevel = 0;
1385 const bool doEncryptBody = doEncrypt && mEncryptBody;
1386 const bool doSignBody = doSign && mSignBody;
1390 mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
1392 mAllAttachmentsAreInBody = mEarlyAddAttachments;
1395 if( mEarlyAddAttachments ) {
1396 bool someOk =
false;
1397 for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1398 if ( it->encrypt == doEncryptBody && it->sign == doSignBody )
1401 mAllAttachmentsAreInBody =
false;
1403 if( !mAllAttachmentsAreInBody && !someOk )
1404 mEarlyAddAttachments =
false;
1407 kdDebug(5006) <<
"mEarlyAddAttachments=" << mEarlyAddAttachments <<
" mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
1410 mMultipartMixedBoundary =
"";
1411 if ( mEarlyAddAttachments ) {
1412 mOldBodyPart.setTypeStr(
"multipart" );
1413 mOldBodyPart.setSubtypeStr(
"mixed" );
1416 tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1417 mMultipartMixedBoundary = tmpCT.Boundary().c_str();
1419 else if ( mIsRichText ) {
1420 mOldBodyPart.setTypeStr(
"multipart" );
1421 mOldBodyPart.setSubtypeStr(
"alternative" );
1424 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1426 mOldBodyPart.setContentDisposition(
"inline" );
1428 if ( mIsRichText ) {
1430 TQCString boundaryCStr;
1433 tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1435 TQValueList<int> allowedCTEs;
1437 KMMessagePart textBodyPart;
1438 textBodyPart.setTypeStr(
"text");
1439 textBodyPart.setSubtypeStr(
"plain");
1441 TQCString textbody = plainTextFromMarkup( mText );
1444 textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
1445 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1447 textBodyPart.setCharset( mCharset );
1448 textBodyPart.setBodyEncoded( textbody );
1450 textDwPart->Assemble();
1452 newbody += boundaryCStr;
1454 newbody += textDwPart->AsString().c_str();
1458 KMMessagePart htmlBodyPart;
1459 htmlBodyPart.setTypeStr(
"text");
1460 htmlBodyPart.setSubtypeStr(
"html");
1461 TQByteArray htmlbody = mText;
1463 htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
1464 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1466 htmlBodyPart.setCharset( mCharset );
1467 htmlBodyPart.setBodyEncodedBinary( htmlbody );
1469 htmlDwPart->Assemble();
1471 newbody += boundaryCStr;
1473 newbody += htmlDwPart->AsString().c_str();
1478 newbody += boundaryCStr;
1481 mOldBodyPart.setBodyEncodedBinary( bodyData );
1483 mSaveBoundary = tmpCT.Boundary();
1487 for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1495 if( it->sign || it->encrypt ) {
1496 TQCString cte = it->part->cteStr().lower();
1497 if( (
"8bit" == cte && it->part->type() != DwMime::kTypeMessage )
1498 || ( ( it->part->type() == DwMime::kTypeText )
1499 && (
"7bit" == cte ) ) ) {
1500 const TQByteArray body = it->part->bodyDecodedBinary();
1501 TQValueList<int> dummy;
1502 it->part->setBodyAndGuessCte(body, dummy,
false, it->sign);
1503 kdDebug(5006) <<
"Changed encoding of message part from "
1504 << cte <<
" to " << it->part->cteStr() << endl;
1509 if( mEarlyAddAttachments ) {
1511 KMMessagePart innerBodyPart;
1512 if ( mIsRichText ) {
1513 innerBodyPart.setTypeStr(
"multipart");
1514 innerBodyPart.setSubtypeStr(
"alternative");
1517 innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1519 innerBodyPart.setContentDisposition(
"inline" );
1520 TQValueList<int> allowedCTEs;
1522 innerBodyPart.setBodyAndGuessCte( bodyData, allowedCTEs,
1523 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1526 innerBodyPart.setCharset( mCharset );
1527 innerBodyPart.setBodyEncodedBinary( bodyData );
1529 innerDwPart->Assemble();
1531 if ( mIsRichText ) {
1532 int boundPos = tmpbody.find(
'\n' );
1533 if( -1 < boundPos ) {
1534 TQCString bStr(
";\n boundary=\"" );
1535 bStr += mSaveBoundary.c_str();
1538 KMail::Util::insert( bodyData, boundPos, bStr );
1539 KMail::Util::insert( bodyData, 0,
"--" + mMultipartMixedBoundary +
"\n" );
1544 KMail::Util::insert( bodyData, 0,
"--" + mMultipartMixedBoundary +
"\n" );
1550 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1551 if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
1553 innerDwPart->Assemble();
1562 TQValueList<int> allowedCTEs;
1564 mOldBodyPart.setBodyAndGuessCte(bodyData, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1567 mOldBodyPart.setCharset(mCharset);
1570 mOldBodyPart.setBodyEncodedBinary( bodyData );
1572 if( doSignBody || doEncryptBody ) {
1576 if ( mIsRichText && !mEarlyAddAttachments ) {
1580 DwHeaders& headers = dwPart->Headers();
1581 DwMediaType& ct = headers.ContentType();
1582 ct.SetBoundary(mSaveBoundary);
1594 if( !mMultipartMixedBoundary.isEmpty() ) {
1595 int boundPos = mEncodedBody.find(
'\n' );
1596 if( -1 < boundPos ) {
1598 TQCString bStr(
";\n boundary=\"" );
1599 bStr += mMultipartMixedBoundary;
1601 KMail::Util::insert( mEncodedBody, boundPos, bStr.data() );
1612 mPerformingSignOperation =
true;
1613 pgpSignedMsg( mEncodedBody, format );
1614 mPerformingSignOperation =
false;
1616 if ( mSignature.isEmpty() ) {
1617 kdDebug() <<
"signature was empty" << endl;
1621 mRc = processStructuringInfo( TQString(),
1622 mOldBodyPart.contentDescription(),
1623 mOldBodyPart.typeStr(),
1624 mOldBodyPart.subtypeStr(),
1625 mOldBodyPart.contentDisposition(),
1626 mOldBodyPart.contentTransferEncodingStr(),
1627 mEncodedBody,
"signature",
1629 *mNewBodyPart,
true, format );
1631 if ( !makeMultiPartSigned( format ) ) {
1632 mNewBodyPart->setCharset( mCharset );
1635 KMessageBox::sorry( mComposeWin,
1636 mErrorProcessingStructuringInfo );
1642 continueComposeMessage( theMessage, doSign, doEncrypt, format );
1646 void MessageComposer::continueComposeMessage(
KMMessage& theMessage,
1647 bool doSign,
bool doEncrypt,
1648 Kleo::CryptoMessageFormat format )
1651 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1652 = mKeyResolver->encryptionItems( format );
1653 kdWarning( splitInfos.empty() )
1654 <<
"MessageComposer::continueComposeMessage(): splitInfos.empty() for "
1655 << Kleo::cryptoMessageFormatToString( format ) << endl;
1657 if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
1658 mJobs.push_front(
new SetLastMessageAsUnencryptedVersionOfLastButOne(
this ) );
1659 mJobs.push_front(
new EncryptMessageJob(
new KMMessage( theMessage ),
1660 Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
1661 false, mEncodedBody,
1662 mPreviousBoundaryLevel,
1667 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
1668 mJobs.push_front(
new EncryptMessageJob(
new KMMessage( theMessage ), *it, doSign,
1669 doEncrypt, mEncodedBody,
1670 mPreviousBoundaryLevel,
1675 void MessageComposer::encryptMessage(
KMMessage* msg,
1676 const Kleo::KeyResolver::SplitInfo & splitInfo,
1677 bool doSign,
bool doEncrypt,
1678 KMMessagePart newBodyPart,
1679 Kleo::CryptoMessageFormat format )
1681 if ( doEncrypt && splitInfo.keys.empty() ) {
1688 const bool doEncryptBody = doEncrypt && mEncryptBody;
1689 const bool doSignBody = doSign && mSignBody;
1691 if ( doEncryptBody ) {
1692 TQByteArray innerContent;
1701 innerContent = mEncodedBody;
1711 TQByteArray encryptedBody;
1712 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
1713 splitInfo.keys, format );
1714 if ( result != Kpgp::Ok ) {
1718 mRc = processStructuringInfo(
"http://www.gnupg.org/aegypten/",
1719 newBodyPart.contentDescription(),
1720 newBodyPart.typeStr(),
1721 newBodyPart.subtypeStr(),
1722 newBodyPart.contentDisposition(),
1723 newBodyPart.contentTransferEncodingStr(),
1727 newBodyPart,
false, format );
1729 KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
1734 const bool useNewBodyPart = doSignBody || doEncryptBody;
1735 addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
1736 useNewBodyPart ? newBodyPart : mOldBodyPart, format );
1740 void MessageComposer::addBodyAndAttachments(
KMMessage* msg,
1741 const Kleo::KeyResolver::SplitInfo & splitInfo,
1742 bool doSign,
bool doEncrypt,
1743 const KMMessagePart& ourFineBodyPart,
1744 Kleo::CryptoMessageFormat format )
1746 const bool doEncryptBody = doEncrypt && mEncryptBody;
1747 const bool doSignBody = doSign && mSignBody;
1749 if( !mAttachments.empty()
1750 && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
1752 msg->
headers().ContentType().SetType( DwMime::kTypeMultipart );
1753 msg->
headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
1754 msg->
headers().ContentType().CreateBoundary( 0 );
1755 kdDebug(5006) <<
"MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
1759 DwHeaders& headers = tmpDwPart->Headers();
1760 DwMediaType& ct = headers.ContentType();
1761 if ( !mSaveBoundary.empty() )
1762 ct.SetBoundary(mSaveBoundary);
1763 tmpDwPart->Assemble();
1771 KMMessagePart newAttachPart;
1772 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1774 const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
1776 if ( !cryptFlagsDifferent && mEarlyAddAttachments )
1779 const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
1780 const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
1782 if ( !encryptThisNow && !signThisNow ) {
1785 (void)msg->asDwMessage();
1789 KMMessagePart& rEncryptMessagePart( *it->part );
1792 innerDwPart->Assemble();
1804 pgpSignedMsg( encodedAttachment, format );
1805 mRc = !mSignature.isEmpty();
1807 mRc = processStructuringInfo(
"http://www.gnupg.org/aegypten/",
1808 it->part->contentDescription(),
1809 it->part->typeStr(),
1810 it->part->subtypeStr(),
1811 it->part->contentDisposition(),
1812 it->part->contentTransferEncodingStr(),
1816 newAttachPart,
true, format );
1818 if( encryptThisNow ) {
1819 rEncryptMessagePart = newAttachPart;
1827 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1833 if( encryptThisNow ) {
1834 TQByteArray encryptedBody;
1835 Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
1840 if( Kpgp::Ok == result ) {
1841 mRc = processStructuringInfo(
"http://www.gnupg.org/aegypten/",
1842 rEncryptMessagePart.contentDescription(),
1843 rEncryptMessagePart.typeStr(),
1844 rEncryptMessagePart.subtypeStr(),
1845 rEncryptMessagePart.contentDisposition(),
1846 rEncryptMessagePart.contentTransferEncodingStr(),
1850 newAttachPart,
false, format );
1852 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1857 (void)msg->asDwMessage();
1860 if( !ourFineBodyPart.originalContentTypeStr().isNull() ) {
1861 msg->
headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
1862 msg->
headers().ContentType().Parse();
1863 kdDebug(5006) <<
"MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
1865 TQCString ct = ourFineBodyPart.typeStr() +
"/" + ourFineBodyPart.subtypeStr();
1866 if ( ct ==
"multipart/mixed" )
1867 ct +=
";\n\tboundary=\"" + mMultipartMixedBoundary +
'"';
1868 else if ( ct ==
"multipart/alternative" )
1869 ct +=
";\n\tboundary=\"" + TQCString(mSaveBoundary.c_str()) +
'"';
1870 msg->
headers().ContentType().FromString( ct );
1871 msg->
headers().ContentType().Parse();
1872 kdDebug(5006) <<
"MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
1874 if ( !ourFineBodyPart.charset().isEmpty() )
1875 msg->
setCharset( ourFineBodyPart.charset() );
1877 ourFineBodyPart.contentTransferEncodingStr() );
1879 ourFineBodyPart.contentDescription() );
1881 ourFineBodyPart.contentDisposition() );
1883 if ( mDebugComposerCrypto )
1884 kdDebug(5006) <<
"MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
1887 msg->
setBody( ourFineBodyPart.dwBody() );
1892 splitInfo.recipients.join(
", "), KMMessage::Address );
1894 if ( mDebugComposerCrypto ) {
1895 kdDebug(5006) <<
"MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->
asString() <<
"|||\n\n" << endl;
1897 kdDebug(5006) <<
"\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->
headerAsString() <<
"|||\n\n\n\n\n" << endl;
1903 bool MessageComposer::processStructuringInfo(
const TQString bugURL,
1904 const TQString contentDescClear,
1905 const TQCString contentTypeClear,
1906 const TQCString contentSubtypeClear,
1907 const TQCString contentDispClear,
1908 const TQCString contentTEncClear,
1909 const TQByteArray& clearCStr,
1911 const TQByteArray& ciphertext,
1912 KMMessagePart& resultingPart,
1913 bool signing, Kleo::CryptoMessageFormat format )
1915 assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] !=
'\0' );
1918 if ( makeMimeObject( format, signing ) ) {
1919 TQCString mainHeader =
"Content-Type: ";
1920 const char * toplevelCT = toplevelContentType( format, signing );
1922 mainHeader += toplevelCT;
1924 if( makeMultiMime( format, signing ) )
1925 mainHeader +=
"text/plain";
1927 mainHeader += contentTypeClear +
'/' + contentSubtypeClear;
1930 const TQCString boundaryCStr = KMime::multiPartBoundary();
1932 if ( makeMultiMime( format, signing ) )
1933 mainHeader.replace(
"%boundary", boundaryCStr );
1936 if (
const char * str = toplevelContentDisposition( format, signing ) ) {
1937 mainHeader +=
"\nContent-Disposition: ";
1940 if ( !makeMultiMime( format, signing ) &&
1941 binaryHint( format ) )
1942 mainHeader +=
"\nContent-Transfer-Encoding: base64";
1944 if( 0 < contentDispClear.length() ) {
1945 mainHeader +=
"\nContent-Disposition: ";
1946 mainHeader += contentDispClear;
1948 if( 0 < contentTEncClear.length() ) {
1949 mainHeader +=
"\nContent-Transfer-Encoding: ";
1950 mainHeader += contentTEncClear;
1957 mainDwStr = TQCString(mainHeader +
"\n\n").data();
1958 DwBodyPart mainDwPa( mainDwStr, 0 );
1961 if( !makeMultiMime( format, signing ) ) {
1962 if ( signing && includeCleartextWhenSigning( format ) ) {
1963 TQByteArray bodyText( clearCStr );
1966 resultingPart.setBodyEncodedBinary( bodyText );
1968 resultingPart.setBodyEncodedBinary( ciphertext );
1975 TQCString versCStr, codeCStr;
1976 if ( !signing && format == Kleo::OpenPGPMIMEFormat )
1978 "Content-Type: application/pgp-encrypted\n"
1979 "Content-Disposition: attachment\n"
1985 const char * nestedCT = nestedContentType( format, signing );
1987 codeCStr =
"Content-Type: ";
1988 codeCStr += nestedCT;
1990 if (
const char * str = nestedContentDisposition( format, signing ) ) {
1991 codeCStr +=
"Content-Disposition: ";
1995 if ( binaryHint( format ) ) {
1996 codeCStr +=
"Content-Transfer-Encoding: base64\n\n";
1997 codeCStr += KMime::Codec::codecForName(
"base64" )->encodeToTQCString( ciphertext );
1999 codeCStr +=
'\n' + TQCString( ciphertext.data(), ciphertext.size() + 1 );
2002 TQByteArray mainStr;
2005 if ( signing && includeCleartextWhenSigning( format ) &&
2006 !clearCStr.isEmpty() ) {
2012 if ( !versCStr.isEmpty() )
2014 if( !codeCStr.isEmpty() )
2019 resultingPart.setBodyEncodedBinary( mainStr );
2024 resultingPart.setContentDescription( contentDescClear );
2025 resultingPart.setTypeStr( contentTypeClear );
2026 resultingPart.setSubtypeStr( contentSubtypeClear );
2027 resultingPart.setContentDisposition( contentDispClear );
2028 resultingPart.setContentTransferEncodingStr( contentTEncClear );
2029 TQByteArray resultingBody;
2031 if ( signing && includeCleartextWhenSigning( format ) ) {
2032 if( !clearCStr.isEmpty() )
2035 if ( !ciphertext.isEmpty() )
2039 KMessageBox::sorry( mComposeWin,
2040 i18n(
"<qt><p>Error: The backend did not return "
2041 "any encoded data.</p>"
2042 "<p>Please report this bug:<br>%2</p></qt>" )
2046 resultingPart.setBodyEncodedBinary( resultingBody );
2053 TQCString MessageComposer::plainTextFromMarkup(
const TQString& markupText )
2055 TQTextEdit *hackConspiratorTextEdit =
new TQTextEdit( markupText );
2056 hackConspiratorTextEdit->setTextFormat(TQt::PlainText);
2057 if ( !mDisableBreaking ) {
2058 hackConspiratorTextEdit->setWordWrap( TQTextEdit::FixedColumnWidth );
2059 hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
2061 TQString text = hackConspiratorTextEdit->text();
2064 const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2065 if( mCharset ==
"us-ascii" ) {
2066 textbody = KMMsgBase::toUsAscii( text );
2067 }
else if( codec == 0 ) {
2068 kdDebug(5006) <<
"Something is wrong and I can not get a codec." << endl;
2069 textbody = text.local8Bit();
2071 text = codec->toUnicode( text.latin1(), text.length() );
2072 textbody = codec->fromUnicode( text );
2074 if (textbody.isNull()) textbody =
"";
2076 delete hackConspiratorTextEdit;
2081 TQByteArray MessageComposer::breakLinesAndApplyCodec()
2086 if( mDisableBreaking || mIsRichText || !GlobalSettings::self()->wordWrap() )
2087 text = mComposeWin->mEditor->text();
2089 text = mComposeWin->mEditor->brokenText();
2090 text.truncate( text.length() );
2093 const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2095 if( mCharset ==
"us-ascii" ) {
2096 cText = KMMsgBase::toUsAscii( text );
2097 newText = TQString::fromLatin1( cText );
2098 }
else if( codec == 0 ) {
2099 kdDebug(5006) <<
"Something is wrong and I can not get a codec." << endl;
2100 cText = text.local8Bit();
2101 newText = TQString::fromLocal8Bit( cText );
2103 cText = codec->fromUnicode( text );
2104 newText = codec->toUnicode( cText );
2106 if (cText.isNull()) cText =
"";
2108 if( !text.isEmpty() && (newText != text) ) {
2109 TQString oldText = mComposeWin->mEditor->text();
2110 mComposeWin->mEditor->setText( newText );
2112 bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
2113 i18n(
"<qt>Not all characters fit into the chosen"
2114 " encoding.<br><br>Send the message anyway?</qt>"),
2115 i18n(
"Some Characters Will Be Lost"),
2116 i18n(
"Lose Characters"), i18n(
"Change Encoding") ) == KMessageBox::Yes );
2118 mComposeWin->mEditor->setText(oldText);
2119 return TQByteArray();
2134 if( cText.isEmpty() || cText[cText.length()-1] !=
'\n' ) {
2135 kdDebug(5006) <<
"Added an <LF> on the last line" << endl;
2143 void MessageComposer::pgpSignedMsg(
const TQByteArray& cText, Kleo::CryptoMessageFormat format ) {
2145 assert( cText.isEmpty() || cText[cText.size()-1] !=
'\0' );
2146 mSignature = TQByteArray();
2148 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
2149 if ( signingKeys.empty() ) {
2150 KMessageBox::sorry( mComposeWin,
2151 i18n(
"This message could not be signed, "
2152 "since no valid signing keys have been found; "
2153 "this should actually never happen, "
2154 "please report this bug.") );
2159 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2161 const Kleo::CryptoBackend::Protocol * proto
2162 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2165 std::unique_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
2166 textMode( format ) ) );
2169 KMessageBox::sorry( mComposeWin,
2170 i18n(
"This message could not be signed, "
2171 "since the chosen backend does not seem to support "
2172 "signing; this should actually never happen, "
2173 "please report this bug.") );
2177 TQByteArray signature;
2178 const GpgME::SigningResult res =
2179 job->exec( signingKeys, cText, signingMode( format ), signature );
2181 std::stringstream ss;
2183 kdDebug(5006) << ss.str().c_str() << endl;
2185 if ( res.error().isCanceled() ) {
2186 kdDebug() <<
"signing was canceled by user" << endl;
2189 if ( res.error() ) {
2190 kdDebug() <<
"signing failed: " << res.error().asString() << endl;
2191 job->showErrorDialog( mComposeWin );
2195 if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2196 if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2197 Kleo::MessageBox::auditLog( 0, job.get(), i18n(
"GnuPG Audit Log for Signing Operation") );
2199 mSignature = signature;
2200 if ( mSignature.isEmpty() ) {
2201 KMessageBox::sorry( mComposeWin,
2202 i18n(
"The signing operation failed. "
2203 "Please make sure that the gpg-agent program "
2209 Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody,
2210 const TQByteArray& cText,
2211 const std::vector<GpgME::Key> & encryptionKeys,
2212 Kleo::CryptoMessageFormat format )
2215 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2217 const Kleo::CryptoBackend::Protocol * proto
2218 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2221 std::unique_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
2222 textMode( format ) ) );
2224 KMessageBox::sorry( mComposeWin,
2225 i18n(
"This message could not be encrypted, "
2226 "since the chosen backend does not seem to support "
2227 "encryption; this should actually never happen, "
2228 "please report this bug.") );
2229 return Kpgp::Failure;
2232 const GpgME::EncryptionResult res =
2233 job->exec( encryptionKeys, cText,
true , encryptedBody );
2235 std::stringstream ss;
2237 kdDebug(5006) << ss.str().c_str() << endl;
2239 if ( res.error().isCanceled() ) {
2240 kdDebug() <<
"encryption was canceled by user" << endl;
2241 return Kpgp::Canceled;
2243 if ( res.error() ) {
2244 kdDebug() <<
"encryption failed: " << res.error().asString() << endl;
2245 job->showErrorDialog( mComposeWin );
2246 return Kpgp::Failure;
2249 if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2250 if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2251 Kleo::MessageBox::auditLog( 0, job.get(), i18n(
"GnuPG Audit Log for Encryption Operation") );
2256 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedBody,
2257 const TQByteArray& cText,
2258 const std::vector<GpgME::Key> & signingKeys,
2259 const std::vector<GpgME::Key> & encryptionKeys,
2260 Kleo::CryptoMessageFormat format )
2263 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2265 const Kleo::CryptoBackend::Protocol * proto
2266 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2269 std::unique_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
2270 textMode( format ) ) );
2272 KMessageBox::sorry( mComposeWin,
2273 i18n(
"This message could not be signed and encrypted, "
2274 "since the chosen backend does not seem to support "
2275 "combined signing and encryption; this should actually never happen, "
2276 "please report this bug.") );
2277 return Kpgp::Failure;
2280 const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
2281 job->exec( signingKeys, encryptionKeys, cText,
false, encryptedBody );
2283 std::stringstream ss;
2284 ss << res.first <<
'\n' << res.second;
2285 kdDebug(5006) << ss.str().c_str() << endl;
2287 if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
2288 kdDebug() <<
"encrypt/sign was canceled by user" << endl;
2289 return Kpgp::Canceled;
2291 if ( res.first.error() || res.second.error() ) {
2292 if ( res.first.error() )
2293 kdDebug() <<
"signing failed: " << res.first.error().asString() << endl;
2295 kdDebug() <<
"encryption failed: " << res.second.error().asString() << endl;
2296 job->showErrorDialog( mComposeWin );
2297 return Kpgp::Failure;
2300 if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2301 if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2302 Kleo::MessageBox::auditLog( 0, job.get(), i18n(
"GnuPG Audit Log for Encryption Operation") );
2308 #include "messagecomposer.moc"
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
TQString idString() const
Returns a string that can be used to identify this folder.
void setBody(const TQCString &aStr)
Set the message body.
static void bodyPart(DwBodyPart *aDwBodyPart, KMMessagePart *aPart, bool withBody=true)
Fill the KMMessagePart structure for a given DwBodyPart.
void setAutomaticFields(bool isMultipart=false)
Set fields that are either automatically set (Message-id) or that do not change from one message to a...
void setCharset(const TQCString &charset, DwEntity *entity=0)
Sets the charset of the message or a subpart of the message.
void setUnencryptedMsg(KMMessage *unencrypted)
Specifies an unencrypted copy of this message to be stored in a separate member variable to allow sav...
void removeHeaderField(const TQCString &name)
Remove header field with given name.
void addDwBodyPart(DwBodyPart *aDwPart)
Append a DwBodyPart to the message.
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
TQCString asString() const
Return the entire message contents as a string.
void addBodyPart(const KMMessagePart *aPart)
Append a body part to the message.
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
TQString headerAsString() const
Return header as string.
void setHeaderField(const TQCString &name, const TQString &value, HeaderFieldType type=Unstructured, bool prepend=false)
Set the header field with the given name to the given value.
void deleteBodyParts()
Delete all body parts.
DwBodyPart * createDWBodyPart(const KMMessagePart *aPart)
Compose a DwBodyPart (needed for adding a part to the message).
A class to resolve signing/encryption keys w.r.t.
TQByteArray byteArrayFromTQCStringNoDetach(TQCString &cstr)
Creates a TQByteArray from a TQCString without detaching (duplicating the data).
void append(TQByteArray &that, const TQByteArray &str)
Append a bytearray to a bytearray.
TQByteArray ByteArray(const DwString &str)
Construct a TQByteArray from a DwString.
TQCString lf2crlf(const TQCString &src)
Convert "\n" line endings to "\r\n".
TQCString CString(const DwString &str)
Construct a TQCString from a DwString.