/**
* messagecomposer . cpp
*
* Copyright ( c ) 2004 Bo Thorsen < bo @ sonofthor . dk >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* In addition , as a special exception , the copyright holders give
* permission to link the code of this program with any edition of
* the Qt library by Trolltech AS , Norway ( or with modified versions
* of Qt that use the same license as Qt ) , and distribute linked
* combinations including the two . You must obey the GNU General
* Public License in all respects for all of the code used other than
* Qt . If you modify this file , you may extend this exception to
* your version of the file , but you are not obligated to do so . If
* you do not wish to do so , delete this exception statement from
* your version .
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include "messagecomposer.h"
# include "kmmsgpart.h"
# define REALLY_WANT_KMCOMPOSEWIN_H
# include "kmcomposewin.h"
# undef REALLY_WANT_KMCOMPOSEWIN_H
# include "klistboxdialog.h"
# include "kcursorsaver.h"
# include "messagesender.h"
# include "kmfolder.h"
# include "kmfoldercombobox.h"
# include "keyresolver.h"
# include "kleo_util.h"
# include "globalsettings.h"
# include "custommimeheader.h"
# include "kmedit.h"
# include "util.h"
# include <libkpimidentities/identity.h>
# include <libkpimidentities/identitymanager.h>
# include <libemailfunctions/email.h>
# include <ui/keyselectiondialog.h>
# include <ui/keyapprovaldialog.h>
# include <ui/messagebox.h>
# include <kleo/cryptobackendfactory.h>
# include <kleo/keylistjob.h>
# include <kleo/encryptjob.h>
# include <kleo/signencryptjob.h>
# include <kleo/signjob.h>
# include <kleo/specialjob.h>
# include <kmime_util.h>
# include <kmime_codecs.h>
# include <kpgpblock.h>
# include <mimelib/mimepp.h>
# include <kmessagebox.h>
# include <klocale.h>
# include <kinputdialog.h>
# include <kdebug.h>
# include <kaction.h>
# include <tqfile.h>
# include <tqtextcodec.h>
# include <tqtextedit.h>
# include <tqtimer.h>
# include <gpgmepp/key.h>
# include <gpgmepp/keylistresult.h>
# include <gpgmepp/encryptionresult.h>
# include <gpgmepp/signingresult.h>
# include <gpgmepp/context.h>
# include <algorithm>
# include <sstream>
# include <memory>
// ## keep default values in sync with configuredialog.cpp, Security::CryptoTab::setup()
// This should be ported to a .kcfg one day I suppose (dfaure).
static inline bool warnSendUnsigned ( ) {
KConfigGroup group ( KMKernel : : config ( ) , " Composer " ) ;
return group . readBoolEntry ( " crypto-warning-unsigned " , false ) ;
}
static inline bool warnSendUnencrypted ( ) {
KConfigGroup group ( KMKernel : : config ( ) , " Composer " ) ;
return group . readBoolEntry ( " crypto-warning-unencrypted " , false ) ;
}
static inline bool saveMessagesEncrypted ( ) {
KConfigGroup group ( KMKernel : : config ( ) , " Composer " ) ;
return group . readBoolEntry ( " crypto-store-encrypted " , true ) ;
}
static inline bool encryptToSelf ( ) {
// return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
KConfigGroup group ( KMKernel : : config ( ) , " Composer " ) ;
return group . readBoolEntry ( " crypto-encrypt-to-self " , true ) ;
}
static inline bool showKeyApprovalDialog ( ) {
KConfigGroup group ( KMKernel : : config ( ) , " Composer " ) ;
return group . readBoolEntry ( " crypto-show-keys-for-approval " , true ) ;
}
static inline int encryptKeyNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-encr-key-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
static inline int signingKeyNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-sign-key-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
static inline int encryptRootCertNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-encr-root-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
static inline int signingRootCertNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-sign-root-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
static inline int encryptChainCertNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-encr-chaincert-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
static inline int signingChainCertNearExpiryWarningThresholdInDays ( ) {
const KConfigGroup composer ( KMKernel : : config ( ) , " Composer " ) ;
if ( ! composer . readBoolEntry ( " crypto-warn-when-near-expire " , true ) )
return - 1 ;
const int num = composer . readNumEntry ( " crypto-warn-sign-chaincert-near-expire-int " , 14 ) ;
return kMax ( 1 , num ) ;
}
/*
Design of this :
The idea is that the main run of applyChanges here makes two jobs :
the first sets the flags for encryption / signing or not , and the other
starts the encryption process .
When a job is run , it has already been removed from the job queue . This
means if one of the current jobs needs to add new jobs , it can add them
to the front and that way control when new jobs are added .
For example , the compose message job will add jobs that will do the
actual encryption and signing .
There are two types of jobs : synchronous and asynchronous :
A synchronous job simply implments the execute ( ) method and performs
it ' s operation there and sets mComposer - > mRc to false if the compose
queue should be canceled .
An asynchronous job only sets up and starts it ' s operation . Before
returning , it connects to the result signals of the operation
( e . g . Kleo : : Job ' s result ( . . . ) signal ) and sets mComposer - > mHoldJobs
to true . This makes the scheduler return to the event loop . The job
is now responsible for giving control back to the scheduler by
calling mComposer - > doNextJob ( ) .
*/
/*
Test plan :
For each message format ( e . g . openPGP / MIME )
1. Body signed
2. Body encrypted
3. Body signed and encrypted
4. Body encrypted , attachments encrypted ( they must be encrypted together , mEarlyAddAttachments )
5. Body encrypted , attachments not encrypted
6. Body encrypted , attachment encrypted and signed ( separately )
7. Body not encrypted , one attachment encrypted + signed , one attachment encrypted only , one attachment signed only
( https : //intevation.de/roundup/aegypten/issue295)
( this is the reason attachments can ' t be encrypted together )
8. Body and attachments encrypted + signed ( they must be encrypted + signed together , mEarlyAddAttachments )
9. Body encrypted + signed , attachments encrypted
10. Body encrypted + signed , one attachment signed , one attachment not encrypted nor signed
. . .
I recorded a KDExecutor script sending all of the above ( David )
Further tests ( which test opportunistic encryption ) :
1. Send a message to a person with valid key but without encryption preference
and answer the question whether the message should be encrypted with Yes .
2. Send a message to a person with valid key but without encryption preference
and answer the question whether the message should be encrypted with No .
3. Send a message to a person with valid key and with encryption preference
" Encrypt whenever possible " ( aka opportunistic encryption ) .
*/
static TQString mErrorProcessingStructuringInfo =
i18n ( " <qt><p>Structuring information returned by the Crypto plug-in "
" could not be processed correctly; the plug-in might be damaged.</p> "
" <p>Please contact your system administrator.</p></qt> " ) ;
static TQString mErrorNoCryptPlugAndNoBuildIn =
i18n ( " <p>No active Crypto Plug-In was found and the built-in OpenPGP code "
" did not run successfully.</p> "
" <p>You can do two things to change this:</p> "
" <ul><li><em>either</em> activate a Plug-In using the "
" Settings->Configure KMail->Plug-In dialog.</li> "
" <li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
" Identity->Advanced tab.</li></ul> " ) ;
class MessageComposerJob {
public :
MessageComposerJob ( MessageComposer * composer ) : mComposer ( composer ) { }
virtual ~ MessageComposerJob ( ) { }
virtual void execute ( ) = 0 ;
protected :
// These are the methods that call the private MessageComposer methods
// Workaround for friend not being inherited
void adjustCryptFlags ( ) { mComposer - > adjustCryptFlags ( ) ; }
void composeMessage ( ) { mComposer - > composeMessage ( ) ; }
void continueComposeMessage ( KMMessage & msg , bool doSign , bool doEncrypt ,
Kleo : : CryptoMessageFormat format )
{
mComposer - > continueComposeMessage ( msg , doSign , doEncrypt , format ) ;
}
void chiasmusEncryptAllAttachments ( ) {
mComposer - > chiasmusEncryptAllAttachments ( ) ;
}
MessageComposer * mComposer ;
} ;
class ChiasmusBodyPartEncryptJob : public MessageComposerJob {
public :
ChiasmusBodyPartEncryptJob ( MessageComposer * composer )
: MessageComposerJob ( composer ) { }
void execute ( ) {
chiasmusEncryptAllAttachments ( ) ;
}
} ;
class AdjustCryptFlagsJob : public MessageComposerJob {
public :
AdjustCryptFlagsJob ( MessageComposer * composer )
: MessageComposerJob ( composer ) { }
void execute ( ) {
adjustCryptFlags ( ) ;
}
} ;
class ComposeMessageJob : public MessageComposerJob {
public :
ComposeMessageJob ( MessageComposer * composer )
: MessageComposerJob ( composer ) { }
void execute ( ) {
composeMessage ( ) ;
}
} ;
MessageComposer : : MessageComposer ( KMComposeWin * win , const char * name )
: TQObject ( win , name ) , mComposeWin ( win ) , mCurrentJob ( 0 ) ,
mReferenceMessage ( 0 ) , mKeyResolver ( 0 ) ,
mUseOpportunisticEncryption ( false ) ,
mSignBody ( false ) , mEncryptBody ( false ) ,
mSigningRequested ( false ) , mEncryptionRequested ( false ) ,
mDoSign ( false ) , mDoEncrypt ( false ) ,
mAllowedCryptoMessageFormats ( 0 ) ,
mDisableCrypto ( false ) ,
mDisableBreaking ( false ) ,
mDebugComposerCrypto ( false ) ,
mAutoCharset ( true ) ,
mIsRichText ( false ) ,
mIdentityUid ( 0 ) , mRc ( true ) ,
mHoldJobs ( false ) ,
mNewBodyPart ( 0 ) ,
mEarlyAddAttachments ( false ) , mAllAttachmentsAreInBody ( false ) ,
mPreviousBoundaryLevel ( 0 ) ,
mEncryptWithChiasmus ( false ) ,
mPerformingSignOperation ( false )
{
}
MessageComposer : : ~ MessageComposer ( )
{
delete mKeyResolver ; mKeyResolver = 0 ;
delete mNewBodyPart ; mNewBodyPart = 0 ;
}
void MessageComposer : : applyChanges ( bool disableCrypto )
{
// Do the initial setup
if ( getenv ( " KMAIL_DEBUG_COMPOSER_CRYPTO " ) ! = 0 ) {
TQCString cE = getenv ( " KMAIL_DEBUG_COMPOSER_CRYPTO " ) ;
mDebugComposerCrypto = cE = = " 1 " | | cE . upper ( ) = = " ON " | | cE . upper ( ) = = " TRUE " ;
kdDebug ( 5006 ) < < " KMAIL_DEBUG_COMPOSER_CRYPTO = TRUE " < < endl ;
} else {
mDebugComposerCrypto = false ;
kdDebug ( 5006 ) < < " KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE " < < endl ;
}
mHoldJobs = false ;
mRc = true ;
mDisableCrypto = disableCrypto ;
// 1: Read everything from KMComposeWin and set all
// trivial parts of the message
readFromComposeWin ( ) ;
// From now on, we're not supposed to read from the composer win
// TODO: Make it so ;-)
// 1.5: Replace all body parts with their chiasmus-encrypted equivalent
mJobs . push_back ( new ChiasmusBodyPartEncryptJob ( this ) ) ;
// 2: Set encryption/signing options and resolve keys
mJobs . push_back ( new AdjustCryptFlagsJob ( this ) ) ;
// 3: Build the message (makes the crypto jobs also)
mJobs . push_back ( new ComposeMessageJob ( this ) ) ;
// Finally: Run the jobs
doNextJob ( ) ;
}
void MessageComposer : : doNextJob ( )
{
delete mCurrentJob ; mCurrentJob = 0 ;
if ( mJobs . isEmpty ( ) ) {
// No more jobs. Signal that we're done
emitDone ( mRc ) ;
return ;
}
if ( ! mRc ) {
// Something has gone wrong - stop the process and bail out
while ( ! mJobs . isEmpty ( ) ) {
delete mJobs . front ( ) ;
mJobs . pop_front ( ) ;
}
emitDone ( false ) ;
return ;
}
// We have more jobs to do, but allow others to come first
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( slotDoNextJob ( ) ) ) ;
}
void MessageComposer : : emitDone ( bool b )
{
// Save memory - before sending the mail
mEncodedBody = TQByteArray ( ) ;
delete mNewBodyPart ; mNewBodyPart = 0 ;
mOldBodyPart . clear ( ) ;
emit done ( b ) ;
}
void MessageComposer : : slotDoNextJob ( )
{
assert ( ! mCurrentJob ) ;
if ( mHoldJobs )
// Always make it run from now. If more than one job should be held,
// The individual jobs must do this.
mHoldJobs = false ;
else {
assert ( ! mJobs . empty ( ) ) ;
// Get the next job
mCurrentJob = mJobs . front ( ) ;
assert ( mCurrentJob ) ;
mJobs . pop_front ( ) ;
// Execute it
mCurrentJob - > execute ( ) ;
}
// Finally run the next job if necessary
if ( ! mHoldJobs )
doNextJob ( ) ;
}
void MessageComposer : : readFromComposeWin ( )
{
// Copy necessary attributes over
mDisableBreaking = false ;
mSignBody = mComposeWin - > mSignAction - > isChecked ( ) ;
mSigningRequested = mSignBody ; // for now; will be adjusted depending on attachments
mEncryptBody = mComposeWin - > mEncryptAction - > isChecked ( ) ;
mEncryptionRequested = mEncryptBody ; // for now; will be adjusted depending on attachments
mAutoCharset = mComposeWin - > mAutoCharset ;
mCharset = mComposeWin - > mCharset ;
mReferenceMessage = mComposeWin - > mMsg ;
// if the user made any modifications to the message then the Content-Type
// of the message is no longer reliable (e. g. if he editted a draft/resent a
// message and then removed all attachments or changed from PGP/MIME signed
// to clearsigned);
// even if the user didn't make any modifications to the message the
// Content-Type of the message might be wrong, e.g. when inline-forwarding
// an mp/alt message then the Content-Type is set to mp/alt although it should
// be text/plain (cf. bug 127526);
// OTOH we must not reset the Content-Type of inline invitations;
// therefore we reset the Content-Type to text/plain whenever the current
// Content-Type is multipart/*.
if ( mReferenceMessage - > type ( ) = = DwMime : : kTypeMultipart )
mReferenceMessage - > setHeaderField ( " Content-Type " , " text/plain " ) ;
mUseOpportunisticEncryption = GlobalSettings : : self ( ) - > pgpAutoEncrypt ( ) ;
mAllowedCryptoMessageFormats = mComposeWin - > cryptoMessageFormat ( ) ;
if ( mAutoCharset ) {
TQCString charset = KMMsgBase : : autoDetectCharset ( mCharset , KMMessage : : preferredCharsets ( ) , mComposeWin - > mEditor - > text ( ) ) ;
if ( charset . isEmpty ( ) )
{
KMessageBox : : sorry ( mComposeWin ,
i18n ( " No suitable encoding could be found for "
" your message. \n Please set an encoding "
" using the 'Options' menu. " ) ) ;
mRc = false ;
return ;
}
mCharset = charset ;
// Also apply this to the composer window
mComposeWin - > mCharset = charset ;
}
mReferenceMessage - > setCharset ( mCharset ) ;
mReferenceMessage - > setTo ( mComposeWin - > to ( ) ) ;
mReferenceMessage - > setFrom ( mComposeWin - > from ( ) ) ;
mReferenceMessage - > setCc ( mComposeWin - > cc ( ) ) ;
mReferenceMessage - > setSubject ( mComposeWin - > subject ( ) ) ;
mReferenceMessage - > setReplyTo ( mComposeWin - > replyTo ( ) ) ;
mReferenceMessage - > setBcc ( mComposeWin - > bcc ( ) ) ;
const KPIM : : Identity & id = mComposeWin - > identity ( ) ;
KMFolder * f = mComposeWin - > mFcc - > getFolder ( ) ;
assert ( f ! = 0 ) ;
if ( f - > idString ( ) = = id . fcc ( ) )
mReferenceMessage - > removeHeaderField ( " X-KMail-Fcc " ) ;
else
mReferenceMessage - > setFcc ( f - > idString ( ) ) ;
// set the correct drafts folder
mReferenceMessage - > setDrafts ( id . drafts ( ) ) ;
if ( id . isDefault ( ) )
mReferenceMessage - > removeHeaderField ( " X-KMail-Identity " ) ;
else mReferenceMessage - > setHeaderField ( " X-KMail-Identity " , TQString : : number ( id . uoid ( ) ) ) ;
TQString replyAddr ;
if ( ! mComposeWin - > replyTo ( ) . isEmpty ( ) ) replyAddr = mComposeWin - > replyTo ( ) ;
else replyAddr = mComposeWin - > from ( ) ;
if ( mComposeWin - > mRequestMDNAction - > isChecked ( ) )
mReferenceMessage - > setHeaderField ( " Disposition-Notification-To " , replyAddr ) ;
else
mReferenceMessage - > removeHeaderField ( " Disposition-Notification-To " ) ;
if ( mComposeWin - > mUrgentAction - > isChecked ( ) ) {
mReferenceMessage - > setHeaderField ( " X-PRIORITY " , " 2 (High) " ) ;
mReferenceMessage - > setHeaderField ( " Priority " , " urgent " ) ;
} else {
mReferenceMessage - > removeHeaderField ( " X-PRIORITY " ) ;
mReferenceMessage - > removeHeaderField ( " Priority " ) ;
}
int num = GlobalSettings : : self ( ) - > custHeaderCount ( ) ;
for ( int ix = 0 ; ix < num ; ix + + ) {
CustomMimeHeader customMimeHeader ( TQString : : number ( ix ) ) ;
customMimeHeader . readConfig ( ) ;
mReferenceMessage - > setHeaderField (
KMMsgBase : : toUsAscii ( customMimeHeader . custHeaderName ( ) ) ,
customMimeHeader . custHeaderValue ( ) ) ;
}
// we have to remember the Bcc because it might have been overwritten
// by a custom header (therefore we can't use bcc() later) and because
// mimelib removes addresses without domain part (therefore we can't use
// mReferenceMessage->bcc() later and also not now. So get the Bcc from
// the composer window.)
mBcc = mComposeWin - > bcc ( ) ;
mTo = KPIM : : splitEmailAddrList ( mComposeWin - > to ( ) . stripWhiteSpace ( ) ) ;
mCc = KPIM : : splitEmailAddrList ( mComposeWin - > cc ( ) . stripWhiteSpace ( ) ) ;
mBccList = KPIM : : splitEmailAddrList ( mBcc . stripWhiteSpace ( ) ) ;
for ( unsigned int i = 0 ; i < mComposeWin - > mAtmList . count ( ) ; + + i )
mAttachments . push_back ( Attachment ( mComposeWin - > mAtmList . at ( i ) ,
mComposeWin - > signFlagOfAttachment ( i ) ,
mComposeWin - > encryptFlagOfAttachment ( i ) ) ) ;
mEncryptWithChiasmus = mComposeWin - > mEncryptWithChiasmus ;
mIsRichText = mComposeWin - > mEditor - > textFormat ( ) = = Qt : : RichText ;
mIdentityUid = mComposeWin - > identityUid ( ) ;
mText = breakLinesAndApplyCodec ( ) ;
assert ( mText . isEmpty ( ) | | mText [ mText . size ( ) - 1 ] = = ' \n ' ) ;
// Hopefully we can get rid of this eventually, it's needed to be able
// to break the plain/text version of a multipart/alternative (html) mail
// according to the line breaks of the richtext version.
mLineBreakColumn = mComposeWin - > mEditor - > lineBreakColumn ( ) ;
}
static TQCString escape_quoted_string ( const TQCString & str ) {
TQCString result ;
const unsigned int str_len = str . length ( ) ;
result . resize ( 2 * str_len + 1 ) ;
char * d = result . data ( ) ;
for ( unsigned int i = 0 ; i < str_len ; + + i )
switch ( const char ch = str [ i ] ) {
case ' \\ ' :
case ' " ' :
* d + + = ' \\ ' ;
default : // fall through:
* d + + = ch ;
}
result . truncate ( d - result . begin ( ) ) ;
return result ;
}
bool MessageComposer : : encryptWithChiasmus ( const Kleo : : CryptoBackend : : Protocol * chiasmus ,
const TQByteArray & body ,
TQByteArray & resultData )
{
std : : auto_ptr < Kleo : : SpecialJob > job ( chiasmus - > specialJob ( " x-encrypt " , TQMap < TQString , TQVariant > ( ) ) ) ;
if ( ! job . get ( ) ) {
const TQString msg = i18n ( " Chiasmus backend does not offer the "
" \" x-encrypt \" function. Please report this bug. " ) ;
KMessageBox : : error ( mComposeWin , msg , i18n ( " Chiasmus Backend Error " ) ) ;
return false ;
}
if ( ! job - > setProperty ( " key " , GlobalSettings : : chiasmusKey ( ) ) | |
! job - > setProperty ( " options " , GlobalSettings : : chiasmusOptions ( ) ) | |
! job - > setProperty ( " input " , body ) ) {
const TQString msg = i18n ( " The \" x-encrypt \" function does not accept "
" the expected parameters. Please report this bug. " ) ;
KMessageBox : : error ( mComposeWin , msg , i18n ( " Chiasmus Backend Error " ) ) ;
return false ;
}
const GpgME : : Error err = job - > exec ( ) ;
if ( err . isCanceled ( ) | | err ) {
if ( err )
job - > showErrorDialog ( mComposeWin , i18n ( " Chiasmus Encryption Error " ) ) ;
return false ;
}
const TQVariant result = job - > property ( " result " ) ;
if ( result . type ( ) ! = TQVariant : : ByteArray ) {
const TQString msg = i18n ( " Unexpected return value from Chiasmus backend: "
" The \" x-encrypt \" function did not return a "
" byte array. Please report this bug. " ) ;
KMessageBox : : error ( mComposeWin , msg , i18n ( " Chiasmus Backend Error " ) ) ;
return false ;
}
resultData = result . toByteArray ( ) ;
return true ;
}
void MessageComposer : : chiasmusEncryptAllAttachments ( ) {
if ( ! mEncryptWithChiasmus )
return ;
assert ( ! GlobalSettings : : chiasmusKey ( ) . isEmpty ( ) ) ; // kmcomposewin code should have made sure
if ( mAttachments . empty ( ) )
return ;
const Kleo : : CryptoBackend : : Protocol * chiasmus
= Kleo : : CryptoBackendFactory : : instance ( ) - > protocol ( " Chiasmus " ) ;
assert ( chiasmus ) ; // kmcomposewin code should have made sure
for ( TQValueVector < Attachment > : : iterator it = mAttachments . begin ( ) , end = mAttachments . end ( ) ; it ! = end ; + + it ) {
KMMessagePart * part = it - > part ;
const TQString filename = part - > fileName ( ) ;
if ( filename . endsWith ( " .xia " , false ) )
continue ; // already encrypted
const TQByteArray body = part - > bodyDecodedBinary ( ) ;
TQByteArray resultData ;
if ( ! encryptWithChiasmus ( chiasmus , body , resultData ) ) {
mRc = false ;
return ;
}
// everything ok, so let's fill in the part again:
TQValueList < int > dummy ;
part - > setBodyAndGuessCte ( resultData , dummy ) ;
part - > setTypeStr ( " application " ) ;
part - > setSubtypeStr ( " vnd.de.bund.bsi.chiasmus " ) ;
part - > setName ( filename + " .xia " ) ;
const TQCString enc_name = KMMsgBase : : encodeRFC2231StringAutoDetectCharset (
filename + " .xia " , part - > charset ( ) ) ;
const TQCString cDisp = " attachment; \n \t filename "
+ ( TQString ( enc_name ) ! = filename + " .xia "
? " *= " + enc_name
: " = \" " + escape_quoted_string ( enc_name ) + ' \" ' ) ;
part - > setContentDisposition ( cDisp ) ;
}
}
void MessageComposer : : adjustCryptFlags ( )
{
if ( ! mDisableCrypto & &
mAllowedCryptoMessageFormats & Kleo : : InlineOpenPGPFormat & &
! mAttachments . empty ( ) & &
( mSigningRequested | | mEncryptionRequested ) )
{
int ret ;
if ( mAllowedCryptoMessageFormats = = Kleo : : InlineOpenPGPFormat ) {
ret = KMessageBox : : warningYesNoCancel ( mComposeWin ,
i18n ( " The inline OpenPGP crypto message format "
" does not support encryption or signing "
" of attachments. \n "
" Really use deprecated inline OpenPGP? " ) ,
i18n ( " Insecure Message Format " ) ,
i18n ( " Use Inline OpenPGP " ) ,
i18n ( " Use OpenPGP/MIME " ) ) ;
}
else {
// if other crypto message formats are allowed then simply don't use
// inline OpenPGP
ret = KMessageBox : : No ;
}
if ( ret = = KMessageBox : : Cancel ) {
mRc = false ;
return ;
} else if ( ret = = KMessageBox : : No ) {
mAllowedCryptoMessageFormats & = ~ Kleo : : InlineOpenPGPFormat ;
mAllowedCryptoMessageFormats | = Kleo : : OpenPGPMIMEFormat ;
if ( mSigningRequested ) {
// The composer window disabled signing on the attachments, re-enable it
for ( unsigned int idx = 0 ; idx < mAttachments . size ( ) ; + + idx )
mAttachments [ idx ] . sign = true ;
}
if ( mEncryptionRequested ) {
// The composer window disabled encrypting on the attachments, re-enable it
// We assume this is what the user wants - after all he chose OpenPGP/MIME for this.
for ( unsigned int idx = 0 ; idx < mAttachments . size ( ) ; + + idx )
mAttachments [ idx ] . encrypt = true ;
}
}
}
mKeyResolver =
new Kleo : : KeyResolver ( encryptToSelf ( ) , showKeyApprovalDialog ( ) ,
mUseOpportunisticEncryption , mAllowedCryptoMessageFormats ,
encryptKeyNearExpiryWarningThresholdInDays ( ) ,
signingKeyNearExpiryWarningThresholdInDays ( ) ,
encryptRootCertNearExpiryWarningThresholdInDays ( ) ,
signingRootCertNearExpiryWarningThresholdInDays ( ) ,
encryptChainCertNearExpiryWarningThresholdInDays ( ) ,
signingChainCertNearExpiryWarningThresholdInDays ( ) ) ;
if ( ! mDisableCrypto ) {
const KPIM : : Identity & id =
kmkernel - > identityManager ( ) - > identityForUoidOrDefault ( mIdentityUid ) ;
TQStringList encryptToSelfKeys ;
if ( ! id . pgpEncryptionKey ( ) . isEmpty ( ) )
encryptToSelfKeys . push_back ( id . pgpEncryptionKey ( ) ) ;
if ( ! id . smimeEncryptionKey ( ) . isEmpty ( ) )
encryptToSelfKeys . push_back ( id . smimeEncryptionKey ( ) ) ;
if ( mKeyResolver - > setEncryptToSelfKeys ( encryptToSelfKeys ) ! = Kpgp : : Ok ) {
mRc = false ;
return ;
}
TQStringList signKeys ;
if ( ! id . pgpSigningKey ( ) . isEmpty ( ) )
signKeys . push_back ( mPGPSigningKey = id . pgpSigningKey ( ) ) ;
if ( ! id . smimeSigningKey ( ) . isEmpty ( ) )
signKeys . push_back ( mSMIMESigningKey = id . smimeSigningKey ( ) ) ;
if ( mKeyResolver - > setSigningKeys ( signKeys ) ! = Kpgp : : Ok ) {
mRc = false ;
return ;
}
}
mKeyResolver - > setPrimaryRecipients ( mTo + mCc ) ;
mKeyResolver - > setSecondaryRecipients ( mBccList ) ;
// check settings of composer buttons *and* attachment check boxes
bool doSignCompletely = mSigningRequested ;
bool doEncryptCompletely = mEncryptionRequested ;
for ( unsigned int idx = 0 ; idx < mAttachments . size ( ) ; + + idx ) {
if ( mAttachments [ idx ] . encrypt )
mEncryptionRequested = true ;
else
doEncryptCompletely = false ;
if ( mAttachments [ idx ] . sign )
mSigningRequested = true ;
else
doSignCompletely = false ;
}
mDoSign = ! mDisableCrypto & & determineWhetherToSign ( doSignCompletely ) ;
if ( ! mRc )
return ;
mDoEncrypt = ! mDisableCrypto & & determineWhetherToEncrypt ( doEncryptCompletely ) ;
if ( ! mRc )
return ;
// resolveAllKeys needs to run even if mDisableCrypto == true, since
// we depend on it collecting all recipients into one dummy
// SplitInfo to avoid special-casing all over the place:
if ( mKeyResolver - > resolveAllKeys ( mDoSign , mDoEncrypt ) ! = Kpgp : : Ok )
mRc = false ;
}
bool MessageComposer : : determineWhetherToSign ( bool doSignCompletely ) {
bool sign = false ;
switch ( mKeyResolver - > checkSigningPreferences ( mSigningRequested ) ) {
case Kleo : : DoIt :
if ( ! mSigningRequested ) {
markAllAttachmentsForSigning ( true ) ;
return true ;
}
sign = true ;
break ;
case Kleo : : DontDoIt :
sign = false ;
break ;
case Kleo : : AskOpportunistic :
assert ( 0 ) ;
case Kleo : : Ask :
{
// the user wants to be asked or has to be asked
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = i18n ( " Examination of the recipient's signing preferences "
" yielded that you be asked whether or not to sign "
" this message. \n "
" Sign this message? " ) ;
switch ( KMessageBox : : questionYesNoCancel ( mComposeWin , msg ,
i18n ( " Sign Message? " ) ,
i18n ( " to sign " , " &Sign " ) ,
i18n ( " Do &Not Sign " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForSigning ( true ) ;
return true ;
case KMessageBox : : No :
markAllAttachmentsForSigning ( false ) ;
return false ;
}
}
break ;
case Kleo : : Conflict :
{
// warn the user that there are conflicting signing preferences
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = i18n ( " There are conflicting signing preferences "
" for these recipients. \n "
" Sign this message? " ) ;
switch ( KMessageBox : : warningYesNoCancel ( mComposeWin , msg ,
i18n ( " Sign Message? " ) ,
i18n ( " to sign " , " &Sign " ) ,
i18n ( " Do &Not Sign " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForSigning ( true ) ;
return true ;
case KMessageBox : : No :
markAllAttachmentsForSigning ( false ) ;
return false ;
}
}
break ;
case Kleo : : Impossible :
{
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = i18n ( " You have requested to sign this message, "
" but no valid signing keys have been configured "
" for this identity. " ) ;
if ( KMessageBox : : warningContinueCancel ( mComposeWin , msg ,
i18n ( " Send Unsigned? " ) ,
i18n ( " Send &Unsigned " ) )
= = KMessageBox : : Cancel ) {
mRc = false ;
return false ;
} else {
markAllAttachmentsForSigning ( false ) ;
return false ;
}
}
}
if ( ! sign | | ! doSignCompletely ) {
if ( warnSendUnsigned ( ) ) {
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = sign & & ! doSignCompletely
? i18n ( " Some parts of this message will not be signed. \n "
" Sending only partially signed messages might violate site policy. \n "
" Sign all parts instead? " ) // oh, I hate this...
: i18n ( " This message will not be signed. \n "
" Sending unsigned message might violate site policy. \n "
" Sign message instead? " ) ; // oh, I hate this...
const TQString buttonText = sign & & ! doSignCompletely
? i18n ( " &Sign All Parts " ) : i18n ( " &Sign " ) ;
switch ( KMessageBox : : warningYesNoCancel ( mComposeWin , msg ,
i18n ( " Unsigned-Message Warning " ) ,
buttonText ,
i18n ( " Send &As Is " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForSigning ( true ) ;
return true ;
case KMessageBox : : No :
return sign | | doSignCompletely ;
}
}
}
return sign | | doSignCompletely ;
}
bool MessageComposer : : determineWhetherToEncrypt ( bool doEncryptCompletely ) {
bool encrypt = false ;
bool opportunistic = false ;
switch ( mKeyResolver - > checkEncryptionPreferences ( mEncryptionRequested ) ) {
case Kleo : : DoIt :
if ( ! mEncryptionRequested ) {
markAllAttachmentsForEncryption ( true ) ;
return true ;
}
encrypt = true ;
break ;
case Kleo : : DontDoIt :
encrypt = false ;
break ;
case Kleo : : AskOpportunistic :
opportunistic = true ;
// fall through...
case Kleo : : Ask :
{
// the user wants to be asked or has to be asked
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = opportunistic
? i18n ( " Valid trusted encryption keys were found for all recipients. \n "
" Encrypt this message? " )
: i18n ( " Examination of the recipient's encryption preferences "
" yielded that you be asked whether or not to encrypt "
" this message. \n "
" Encrypt this message? " ) ;
switch ( KMessageBox : : questionYesNoCancel ( mComposeWin , msg ,
i18n ( " Encrypt Message? " ) ,
mDoSign
? i18n ( " Sign && &Encrypt " )
: i18n ( " &Encrypt " ) ,
mDoSign
? i18n ( " &Sign Only " )
: i18n ( " &Send As-Is " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForEncryption ( true ) ;
return true ;
case KMessageBox : : No :
markAllAttachmentsForEncryption ( false ) ;
return false ;
}
}
break ;
case Kleo : : Conflict :
{
// warn the user that there are conflicting encryption preferences
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = i18n ( " There are conflicting encryption preferences "
" for these recipients. \n "
" Encrypt this message? " ) ;
switch ( KMessageBox : : warningYesNoCancel ( mComposeWin , msg ,
i18n ( " Encrypt Message? " ) ,
i18n ( " &Encrypt " ) ,
i18n ( " Do &Not Encrypt " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForEncryption ( true ) ;
return true ;
case KMessageBox : : No :
markAllAttachmentsForEncryption ( false ) ;
return false ;
}
}
break ;
case Kleo : : Impossible :
{
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = i18n ( " You have requested to encrypt this message, "
" and to encrypt a copy to yourself, "
" but no valid trusted encryption keys have been "
" configured for this identity. " ) ;
if ( KMessageBox : : warningContinueCancel ( mComposeWin , msg ,
i18n ( " Send Unencrypted? " ) ,
i18n ( " Send &Unencrypted " ) )
= = KMessageBox : : Cancel ) {
mRc = false ;
return false ;
} else {
markAllAttachmentsForEncryption ( false ) ;
return false ;
}
}
}
if ( ! encrypt | | ! doEncryptCompletely ) {
if ( warnSendUnencrypted ( ) ) {
const KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
const TQString msg = ! doEncryptCompletely
? i18n ( " Some parts of this message will not be encrypted. \n "
" Sending only partially encrypted messages might violate site policy "
" and/or leak sensitive information. \n "
" Encrypt all parts instead? " ) // oh, I hate this...
: i18n ( " This message will not be encrypted. \n "
" Sending unencrypted messages might violate site policy and/or "
" leak sensitive information. \n "
" Encrypt messages instead? " ) ; // oh, I hate this...
const TQString buttonText = ! doEncryptCompletely
? i18n ( " &Encrypt All Parts " ) : i18n ( " &Encrypt " ) ;
switch ( KMessageBox : : warningYesNoCancel ( mComposeWin , msg ,
i18n ( " Unencrypted Message Warning " ) ,
buttonText ,
mDoSign
? i18n ( " &Sign Only " )
: i18n ( " &Send As-Is " ) ) ) {
case KMessageBox : : Cancel :
mRc = false ;
return false ;
case KMessageBox : : Yes :
markAllAttachmentsForEncryption ( true ) ;
return true ;
case KMessageBox : : No :
return encrypt | | doEncryptCompletely ;
}
}
}
return encrypt | | doEncryptCompletely ;
}
void MessageComposer : : markAllAttachmentsForSigning ( bool sign ) {
mSignBody = sign ;
for ( TQValueVector < Attachment > : : iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it )
it - > sign = sign ;
}
void MessageComposer : : markAllAttachmentsForEncryption ( bool enc ) {
mEncryptBody = enc ;
for ( TQValueVector < Attachment > : : iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it )
it - > encrypt = enc ;
}
void MessageComposer : : composeMessage ( )
{
for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; + + i ) {
if ( mKeyResolver - > encryptionItems ( concreteCryptoMessageFormats [ i ] ) . empty ( ) )
continue ;
KMMessage * msg = new KMMessage ( * mReferenceMessage ) ;
composeMessage ( * msg , mDoSign , mDoEncrypt , concreteCryptoMessageFormats [ i ] ) ;
if ( ! mRc )
return ;
}
}
//
// These are replacements for StructuringInfo(Wrapper):
//
// check whether to use multipart/{signed,encrypted}
static inline bool makeMultiMime ( Kleo : : CryptoMessageFormat f , bool sign ) {
switch ( f ) {
default :
case Kleo : : InlineOpenPGPFormat :
case Kleo : : SMIMEOpaqueFormat : return false ;
case Kleo : : OpenPGPMIMEFormat : return true ;
case Kleo : : SMIMEFormat : return sign ; // only on sign - there's no mp/encrypted for S/MIME
}
}
static inline bool makeMultiPartSigned ( Kleo : : CryptoMessageFormat f ) {
return makeMultiMime ( f , true ) ;
}
static inline bool makeMultiPartEncrypted ( Kleo : : CryptoMessageFormat f ) {
return makeMultiMime ( f , false ) ;
}
static inline bool makeMimeObject ( Kleo : : CryptoMessageFormat f , bool /*signing*/ ) {
return f ! = Kleo : : InlineOpenPGPFormat ;
}
static inline const char * toplevelContentType ( Kleo : : CryptoMessageFormat f , bool signing ) {
switch ( f ) {
default :
case Kleo : : InlineOpenPGPFormat : return 0 ;
case Kleo : : OpenPGPMIMEFormat :
return signing ?
" multipart/signed; \n \t "
" boundary= \" %boundary \" ; \n \t "
" protocol= \" application/pgp-signature \" ; \n \t "
" micalg=pgp-sha1 " // FIXME: obtain this parameter from gpgme!
:
" multipart/encrypted; \n \t "
" boundary= \" %boundary \" ; \n \t "
" protocol= \" application/pgp-encrypted \" "
;
case Kleo : : SMIMEFormat :
if ( signing )
return
" multipart/signed; \n \t "
" boundary= \" %boundary \" ; \n \t "
" protocol= \" application/pkcs7-signature \" ; \n \t "
" micalg=sha1 " ; // FIXME: obtain this parameter from gpgme!
// fall through (for encryption, there's no difference between
// SMIME and SMIMEOpaque, since there is no mp/encrypted for
// S/MIME):
case Kleo : : SMIMEOpaqueFormat :
return signing ?
" application/pkcs7-mime; \n \t "
" smime-type=signed-data; \n \t "
" name= \" smime.p7m \" ; \n \t "
:
" application/pkcs7-mime; \n \t "
" smime-type=enveloped-data; \n \t "
" name= \" smime.p7m \" ; \n \t "
;
}
}
static inline const char * toplevelContentDisposition ( Kleo : : CryptoMessageFormat f , bool signing ) {
switch ( f ) {
default :
case Kleo : : InlineOpenPGPFormat :
case Kleo : : OpenPGPMIMEFormat :
return 0 ;
case Kleo : : SMIMEFormat :
if ( signing )
return 0 ;
case Kleo : : SMIMEOpaqueFormat :
return " attachment; filename= \" smime.p7m \" " ;
}
}
static inline bool includeCleartextWhenSigning ( Kleo : : CryptoMessageFormat f ) {
return makeMultiPartSigned ( f ) ;
}
static inline const char * nestedContentType ( Kleo : : CryptoMessageFormat f , bool signing ) {
switch ( f ) {
case Kleo : : OpenPGPMIMEFormat :
return signing ? " application/pgp-signature; name=signature.asc \n Content-Description: This is a digitally signed message part. " : " application/octet-stream " ;
case Kleo : : SMIMEFormat :
if ( signing )
return " application/pkcs7-signature; name= \" smime.p7s \" " ;
// fall through:
default :
case Kleo : : InlineOpenPGPFormat :
case Kleo : : SMIMEOpaqueFormat :
return 0 ;
}
}
static inline const char * nestedContentDisposition ( Kleo : : CryptoMessageFormat f , bool signing ) {
if ( ! signing & & f = = Kleo : : OpenPGPMIMEFormat )
return " inline; filename= \" msg.asc \" " ;
if ( signing & & f = = Kleo : : SMIMEFormat )
return " attachment; filename= \" smime.p7s \" " ;
return 0 ;
}
static inline bool binaryHint ( Kleo : : CryptoMessageFormat f ) {
switch ( f ) {
case Kleo : : SMIMEFormat :
case Kleo : : SMIMEOpaqueFormat :
return true ;
default :
case Kleo : : OpenPGPMIMEFormat :
case Kleo : : InlineOpenPGPFormat :
return false ;
}
}
static inline bool armor ( Kleo : : CryptoMessageFormat f ) {
return ! binaryHint ( f ) ;
}
static inline bool textMode ( Kleo : : CryptoMessageFormat f ) {
return f = = Kleo : : InlineOpenPGPFormat ;
}
static inline GpgME : : Context : : SignatureMode signingMode ( Kleo : : CryptoMessageFormat f ) {
switch ( f ) {
case Kleo : : SMIMEOpaqueFormat :
return GpgME : : Context : : Normal ;
case Kleo : : InlineOpenPGPFormat :
return GpgME : : Context : : Clearsigned ;
default :
case Kleo : : SMIMEFormat :
case Kleo : : OpenPGPMIMEFormat :
return GpgME : : Context : : Detached ;
}
}
//
// END replacements for StructuringInfo(Wrapper)
//
class EncryptMessageJob : public MessageComposerJob {
public :
EncryptMessageJob ( KMMessage * msg , const Kleo : : KeyResolver : : SplitInfo & si ,
bool doSign , bool doEncrypt , const TQByteArray & encodedBody ,
int boundaryLevel , /*const KMMessagePart& oldBodyPart,*/
KMMessagePart * newBodyPart , Kleo : : CryptoMessageFormat format ,
MessageComposer * composer )
: MessageComposerJob ( composer ) , mMsg ( msg ) , mSplitInfo ( si ) ,
mDoSign ( doSign ) , mDoEncrypt ( doEncrypt ) , mEncodedBody ( encodedBody ) ,
mBoundaryLevel ( boundaryLevel ) , /*mOldBodyPart( oldBodyPart ),*/
mNewBodyPart ( newBodyPart ) , mFormat ( format ) { }
void execute ( ) {
KMMessagePart tmpNewBodyPart ;
tmpNewBodyPart . duplicate ( * mNewBodyPart ) ; // slow - we duplicate everything again
// TODO: Async call
mComposer - > encryptMessage ( mMsg , mSplitInfo , mDoSign , mDoEncrypt ,
tmpNewBodyPart , mFormat ) ;
if ( ! mComposer - > mRc ) {
delete mMsg ; mMsg = 0 ;
return ;
}
mComposer - > mMessageList . push_back ( mMsg ) ;
}
private :
KMMessage * mMsg ;
Kleo : : KeyResolver : : SplitInfo mSplitInfo ;
bool mDoSign , mDoEncrypt ;
TQByteArray mEncodedBody ;
int mBoundaryLevel ;
//KMMessagePart mOldBodyPart;
KMMessagePart * mNewBodyPart ;
Kleo : : CryptoMessageFormat mFormat ;
} ;
class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
public :
SetLastMessageAsUnencryptedVersionOfLastButOne ( MessageComposer * composer )
: MessageComposerJob ( composer ) { }
void execute ( ) {
KMMessage * last = mComposer - > mMessageList . back ( ) ;
mComposer - > mMessageList . pop_back ( ) ;
mComposer - > mMessageList . back ( ) - > setUnencryptedMsg ( last ) ;
}
} ;
void MessageComposer : : composeInlineOpenPGPMessage ( KMMessage & theMessage ,
bool doSign , bool doEncrypt )
{
// preprocess the body text
const TQByteArray bodyData = mText ;
if ( bodyData . isNull ( ) ) {
mRc = false ;
return ;
}
mNewBodyPart = 0 ; // unused
mEarlyAddAttachments = false ;
mAllAttachmentsAreInBody = false ;
// set the main headers
theMessage . deleteBodyParts ( ) ;
TQString oldContentType = theMessage . headerField ( " Content-Type " ) ;
theMessage . removeHeaderField ( " Content-Type " ) ;
theMessage . removeHeaderField ( " Content-Transfer-Encoding " ) ;
const std : : vector < Kleo : : KeyResolver : : SplitInfo > splitInfos
= mKeyResolver - > encryptionItems ( Kleo : : InlineOpenPGPFormat ) ;
kdWarning ( splitInfos . empty ( ) )
< < " MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat "
< < endl ;
std : : vector < Kleo : : KeyResolver : : SplitInfo > : : const_iterator it ;
for ( it = splitInfos . begin ( ) ; it ! = splitInfos . end ( ) ; + + it ) {
const Kleo : : KeyResolver : : SplitInfo & splitInfo = * it ;
KMMessage * msg = new KMMessage ( theMessage ) ;
if ( doEncrypt ) {
Kpgp : : Result result ;
TQByteArray encryptedBody ;
if ( doSign ) { // Sign and encrypt
const std : : vector < GpgME : : Key > signingKeys = mKeyResolver - > signingKeys ( Kleo : : InlineOpenPGPFormat ) ;
result = pgpSignedAndEncryptedMsg ( encryptedBody , bodyData , signingKeys ,
splitInfo . keys , Kleo : : InlineOpenPGPFormat ) ;
} else { // Encrypt but don't sign
result = pgpEncryptedMsg ( encryptedBody , bodyData ,
splitInfo . keys , Kleo : : InlineOpenPGPFormat ) ;
}
if ( result ! = Kpgp : : Ok ) {
mRc = false ;
return ;
}
assert ( ! encryptedBody . isNull ( ) ) ; // if you hit this, check gpg-agent is running, then blame gpgme.
mOldBodyPart . setBodyEncodedBinary ( encryptedBody ) ;
} else {
if ( doSign ) { // Sign but don't encrypt
pgpSignedMsg ( bodyData , Kleo : : InlineOpenPGPFormat ) ;
if ( mSignature . isNull ( ) ) {
mRc = false ;
return ;
}
mOldBodyPart . setBodyEncodedBinary ( mSignature ) ;
} else { // don't sign nor encrypt -> nothing to do
assert ( ! bodyData . isNull ( ) ) ;
mOldBodyPart . setBodyEncodedBinary ( bodyData ) ;
}
}
mOldBodyPart . setContentDisposition ( " inline " ) ;
mOldBodyPart . setOriginalContentTypeStr ( oldContentType . utf8 ( ) ) ;
mOldBodyPart . setCharset ( mCharset ) ;
addBodyAndAttachments ( msg , splitInfo , false , false , mOldBodyPart , Kleo : : InlineOpenPGPFormat ) ;
mMessageList . push_back ( msg ) ;
if ( it = = splitInfos . begin ( ) ) {
if ( doEncrypt & & ! saveMessagesEncrypted ( ) ) {
mOldBodyPart . setBodyEncodedBinary ( bodyData ) ;
KMMessage * msgUnenc = new KMMessage ( theMessage ) ;
addBodyAndAttachments ( msgUnenc , splitInfo , false , false , mOldBodyPart , Kleo : : InlineOpenPGPFormat ) ;
msg - > setUnencryptedMsg ( msgUnenc ) ;
}
}
} // end for
}
// very much inspired by composeInlineOpenPGPMessage
void MessageComposer : : composeChiasmusMessage ( KMMessage & theMessage , Kleo : : CryptoMessageFormat format )
{
assert ( ! GlobalSettings : : chiasmusKey ( ) . isEmpty ( ) ) ; // kmcomposewin code should have made sure
const Kleo : : CryptoBackendFactory * cpf = Kleo : : CryptoBackendFactory : : instance ( ) ;
assert ( cpf ) ;
const Kleo : : CryptoBackend : : Protocol * chiasmus
= cpf - > protocol ( " Chiasmus " ) ;
assert ( chiasmus ) ; // kmcomposewin code should have made sure
// preprocess the body text
const TQByteArray bodyData = mText ;
if ( bodyData . isNull ( ) ) {
mRc = false ;
return ;
}
mNewBodyPart = 0 ; // unused
mEarlyAddAttachments = false ;
mAllAttachmentsAreInBody = false ;
// set the main headers
theMessage . deleteBodyParts ( ) ;
TQString oldContentType = theMessage . headerField ( " Content-Type " ) ;
theMessage . removeHeaderField ( " Content-Type " ) ;
theMessage . removeHeaderField ( " Content-Transfer-Encoding " ) ;
// This reads strange, but we know that AdjustCryptFlagsJob created a single splitinfo,
// under the given "format" (usually openpgp/mime; doesn't matter)
const std : : vector < Kleo : : KeyResolver : : SplitInfo > splitInfos
= mKeyResolver - > encryptionItems ( format ) ;
assert ( splitInfos . size ( ) = = 1 ) ;
for ( std : : vector < Kleo : : KeyResolver : : SplitInfo > : : const_iterator it = splitInfos . begin ( ) ; it ! = splitInfos . end ( ) ; + + it )
{
const Kleo : : KeyResolver : : SplitInfo & splitInfo = * it ;
KMMessage * msg = new KMMessage ( theMessage ) ;
TQByteArray encryptedBody ;
if ( ! encryptWithChiasmus ( chiasmus , bodyData , encryptedBody ) ) {
mRc = false ;
return ;
}
assert ( ! encryptedBody . isNull ( ) ) ;
// This leaves CTE==7-bit, no good
//mOldBodyPart.setBodyEncodedBinary( encryptedBody );
bool doSign = false ;
TQValueList < int > allowedCTEs ;
mOldBodyPart . setBodyAndGuessCte ( encryptedBody , allowedCTEs ,
! kmkernel - > msgSender ( ) - > sendQuotedPrintable ( ) & & ! doSign ,
doSign ) ;
mOldBodyPart . setContentDisposition ( " inline " ) ;
// Used in case of no attachments
mOldBodyPart . setOriginalContentTypeStr ( " application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset= " + mCharset ) ;
// Used in case of attachments
mOldBodyPart . setTypeStr ( " application " ) ;
mOldBodyPart . setSubtypeStr ( " vnd.de.bund.bsi.chiasmus-text " ) ;
mOldBodyPart . setAdditionalCTypeParamStr ( TQCString ( " chiasmus-charset= " + mCharset ) ) ;
addBodyAndAttachments ( msg , splitInfo , false , false , mOldBodyPart , Kleo : : InlineOpenPGPFormat ) ;
mMessageList . push_back ( msg ) ;
if ( it = = splitInfos . begin ( ) & & ! saveMessagesEncrypted ( ) ) {
mOldBodyPart . setBodyEncodedBinary ( bodyData ) ;
KMMessage * msgUnenc = new KMMessage ( theMessage ) ;
addBodyAndAttachments ( msgUnenc , splitInfo , false , false , mOldBodyPart , Kleo : : InlineOpenPGPFormat ) ;
msg - > setUnencryptedMsg ( msgUnenc ) ;
}
}
}
void MessageComposer : : composeMessage ( KMMessage & theMessage ,
bool doSign , bool doEncrypt ,
Kleo : : CryptoMessageFormat format )
{
# ifdef DEBUG
kdDebug ( 5006 ) < < " entering KMComposeWin::composeMessage " < < endl ;
# endif
if ( format = = Kleo : : InlineOpenPGPFormat ) {
composeInlineOpenPGPMessage ( theMessage , doSign , doEncrypt ) ;
return ;
}
if ( mEncryptWithChiasmus )
{
composeChiasmusMessage ( theMessage , format ) ;
return ;
}
// create informative header for those that have no mime-capable
// email client
theMessage . setBody ( " This message is in MIME format. " ) ;
// preprocess the body text
TQByteArray bodyData = mText ;
if ( bodyData . isNull ( ) ) {
mRc = false ;
return ;
}
// set the main headers
TQString oldContentType = theMessage . headerField ( " Content-Type " ) ;
theMessage . deleteBodyParts ( ) ;
theMessage . removeHeaderField ( " Content-Type " ) ;
theMessage . removeHeaderField ( " Content-Transfer-Encoding " ) ;
theMessage . setAutomaticFields ( true ) ; // == multipart/mixed
// this is our *final* body part
mNewBodyPart = new KMMessagePart ;
// this is the boundary depth of the surrounding MIME part
mPreviousBoundaryLevel = 0 ;
// whether the body must be signed/encrypted
const bool doEncryptBody = doEncrypt & & mEncryptBody ;
const bool doSignBody = doSign & & mSignBody ;
// create temporary bodyPart for editor text
// (and for all attachments, if mail is to be signed and/or encrypted)
mEarlyAddAttachments = ! mAttachments . empty ( ) & & ( doSignBody | | doEncryptBody ) ;
mAllAttachmentsAreInBody = mEarlyAddAttachments ;
// test whether there ARE attachments that can be included into the body
if ( mEarlyAddAttachments ) {
bool someOk = false ;
for ( TQValueVector < Attachment > : : const_iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it ) {
if ( it - > encrypt = = doEncryptBody & & it - > sign = = doSignBody )
someOk = true ;
else
mAllAttachmentsAreInBody = false ;
}
if ( ! mAllAttachmentsAreInBody & & ! someOk )
mEarlyAddAttachments = false ;
}
kdDebug ( 5006 ) < < " mEarlyAddAttachments= " < < mEarlyAddAttachments < < " mAllAttachmentsAreInBody= " < < mAllAttachmentsAreInBody < < endl ;
// if an html message is to be generated, make a text/plain and text/html part
mMultipartMixedBoundary = " " ;
if ( mEarlyAddAttachments ) {
mOldBodyPart . setTypeStr ( " multipart " ) ;
mOldBodyPart . setSubtypeStr ( " mixed " ) ;
// calculate a boundary string
DwMediaType tmpCT ;
tmpCT . CreateBoundary ( + + mPreviousBoundaryLevel ) ;
mMultipartMixedBoundary = tmpCT . Boundary ( ) . c_str ( ) ;
}
else if ( mIsRichText ) {
mOldBodyPart . setTypeStr ( " multipart " ) ;
mOldBodyPart . setSubtypeStr ( " alternative " ) ;
}
else
mOldBodyPart . setOriginalContentTypeStr ( oldContentType . utf8 ( ) ) ;
mOldBodyPart . setContentDisposition ( " inline " ) ;
if ( mIsRichText ) { // create a multipart body
// calculate a boundary string
TQCString boundaryCStr ; // storing boundary string data
TQCString newbody ;
DwMediaType tmpCT ;
tmpCT . CreateBoundary ( + + mPreviousBoundaryLevel ) ;
boundaryCStr = KMail : : Util : : CString ( tmpCT . Boundary ( ) ) ;
TQValueList < int > allowedCTEs ;
KMMessagePart textBodyPart ;
textBodyPart . setTypeStr ( " text " ) ;
textBodyPart . setSubtypeStr ( " plain " ) ;
TQCString textbody = plainTextFromMarkup ( mText /* converted to TQString */ ) ;
// the signed body must not be 8bit encoded
textBodyPart . setBodyAndGuessCte ( textbody , allowedCTEs ,
! kmkernel - > msgSender ( ) - > sendQuotedPrintable ( ) & & ! doSign ,
doSign ) ;
textBodyPart . setCharset ( mCharset ) ;
textBodyPart . setBodyEncoded ( textbody ) ;
DwBodyPart * textDwPart = theMessage . createDWBodyPart ( & textBodyPart ) ;
textDwPart - > Assemble ( ) ;
newbody + = " -- " ;
newbody + = boundaryCStr ;
newbody + = " \n " ;
newbody + = textDwPart - > AsString ( ) . c_str ( ) ;
delete textDwPart ;
textDwPart = 0 ;
KMMessagePart htmlBodyPart ;
htmlBodyPart . setTypeStr ( " text " ) ;
htmlBodyPart . setSubtypeStr ( " html " ) ;
TQByteArray htmlbody = mText ;
// the signed body must not be 8bit encoded
htmlBodyPart . setBodyAndGuessCte ( htmlbody , allowedCTEs ,
! kmkernel - > msgSender ( ) - > sendQuotedPrintable ( ) & & ! doSign ,
doSign ) ;
htmlBodyPart . setCharset ( mCharset ) ;
htmlBodyPart . setBodyEncodedBinary ( htmlbody ) ;
DwBodyPart * htmlDwPart = theMessage . createDWBodyPart ( & htmlBodyPart ) ;
htmlDwPart - > Assemble ( ) ;
newbody + = " \n -- " ;
newbody + = boundaryCStr ;
newbody + = " \n " ;
newbody + = htmlDwPart - > AsString ( ) . c_str ( ) ;
delete htmlDwPart ;
htmlDwPart = 0 ;
newbody + = " -- " ;
newbody + = boundaryCStr ;
newbody + = " -- \n " ;
bodyData = KMail : : Util : : byteArrayFromQCStringNoDetach ( newbody ) ;
mOldBodyPart . setBodyEncodedBinary ( bodyData ) ;
mSaveBoundary = tmpCT . Boundary ( ) ;
}
// Prepare attachments that will be signed/encrypted
for ( TQValueVector < Attachment > : : const_iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it ) {
// signed/encrypted body parts must be either QP or base64 encoded
// Why not 7 bit? Because the LF->CRLF canonicalization would render
// e.g. 7 bit encoded shell scripts unusable because of the CRs.
//
// (marc) this is a workaround for the KMail bug that doesn't
// respect the CRLF->LF de-canonicalisation. We should
// eventually get rid of this:
if ( it - > sign | | it - > encrypt ) {
TQCString cte = it - > part - > cteStr ( ) . lower ( ) ;
if ( ( " 8bit " = = cte & & it - > part - > type ( ) ! = DwMime : : kTypeMessage )
| | ( ( it - > part - > type ( ) = = DwMime : : kTypeText )
& & ( " 7bit " = = cte ) ) ) {
const TQByteArray body = it - > part - > bodyDecodedBinary ( ) ;
TQValueList < int > dummy ;
it - > part - > setBodyAndGuessCte ( body , dummy , false , it - > sign ) ;
kdDebug ( 5006 ) < < " Changed encoding of message part from "
< < cte < < " to " < < it - > part - > cteStr ( ) < < endl ;
}
}
}
if ( mEarlyAddAttachments ) {
// add the normal body text
KMMessagePart innerBodyPart ;
if ( mIsRichText ) {
innerBodyPart . setTypeStr ( " multipart " ) ; //text" );
innerBodyPart . setSubtypeStr ( " alternative " ) ; //html");
}
else {
innerBodyPart . setOriginalContentTypeStr ( oldContentType . utf8 ( ) ) ;
}
innerBodyPart . setContentDisposition ( " inline " ) ;
TQValueList < int > allowedCTEs ;
// the signed body must not be 8bit encoded
innerBodyPart . setBodyAndGuessCte ( bodyData , allowedCTEs ,
! kmkernel - > msgSender ( ) - > sendQuotedPrintable ( ) & & ! doSign ,
doSign ) ;
if ( ! mIsRichText )
innerBodyPart . setCharset ( mCharset ) ;
innerBodyPart . setBodyEncodedBinary ( bodyData ) ; // do we need this, since setBodyAndGuessCte does this already?
DwBodyPart * innerDwPart = theMessage . createDWBodyPart ( & innerBodyPart ) ;
innerDwPart - > Assemble ( ) ;
TQByteArray tmpbody = KMail : : Util : : ByteArray ( innerDwPart - > AsString ( ) ) ;
if ( mIsRichText ) { // and add our mp/a boundary
int boundPos = tmpbody . find ( ' \n ' ) ;
if ( - 1 < boundPos ) {
TQCString bStr ( " ; \n boundary= \" " ) ;
bStr + = mSaveBoundary . c_str ( ) ;
bStr + = " \" " ;
bodyData = tmpbody ;
KMail : : Util : : insert ( bodyData , boundPos , bStr ) ;
KMail : : Util : : insert ( bodyData , 0 , " -- " + mMultipartMixedBoundary + " \n " ) ; // prepend
}
}
else {
bodyData = tmpbody ;
KMail : : Util : : insert ( bodyData , 0 , " -- " + mMultipartMixedBoundary + " \n " ) ; // prepend
}
delete innerDwPart ;
innerDwPart = 0 ;
// add all matching Attachments
// NOTE: This code will be changed when KMime is complete.
for ( TQValueVector < Attachment > : : iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it ) {
if ( it - > encrypt = = doEncryptBody & & it - > sign = = doSignBody ) {
innerDwPart = theMessage . createDWBodyPart ( it - > part ) ;
innerDwPart - > Assemble ( ) ;
KMail : : Util : : append ( bodyData , TQCString ( " \n -- " + mMultipartMixedBoundary + " \n " ) ) ;
KMail : : Util : : append ( bodyData , innerDwPart - > AsString ( ) . c_str ( ) ) ;
delete innerDwPart ;
innerDwPart = 0 ;
}
}
KMail : : Util : : append ( bodyData , TQCString ( " \n -- " + mMultipartMixedBoundary + " -- \n " ) ) ;
} else { // !earlyAddAttachments
TQValueList < int > allowedCTEs ;
// the signed body must not be 8bit encoded
mOldBodyPart . setBodyAndGuessCte ( bodyData , allowedCTEs , ! kmkernel - > msgSender ( ) - > sendQuotedPrintable ( ) & & ! doSign ,
doSign ) ;
if ( ! mIsRichText )
mOldBodyPart . setCharset ( mCharset ) ;
}
// create S/MIME body part for signing and/or encrypting
mOldBodyPart . setBodyEncodedBinary ( bodyData ) ;
if ( doSignBody | | doEncryptBody ) {
// get string representation of body part (including the attachments)
DwBodyPart * dwPart ;
if ( mIsRichText & & ! mEarlyAddAttachments ) {
// if we are using richtext and not already have a mp/a body
// make the body a mp/a body
dwPart = theMessage . createDWBodyPart ( & mOldBodyPart ) ;
DwHeaders & headers = dwPart - > Headers ( ) ;
DwMediaType & ct = headers . ContentType ( ) ;
ct . SetBoundary ( mSaveBoundary ) ;
dwPart - > Assemble ( ) ;
}
else {
dwPart = theMessage . createDWBodyPart ( & mOldBodyPart ) ;
dwPart - > Assemble ( ) ;
}
mEncodedBody = KMail : : Util : : ByteArray ( dwPart - > AsString ( ) ) ;
delete dwPart ;
dwPart = 0 ;
// manually add a boundary definition to the Content-Type header
if ( ! mMultipartMixedBoundary . isEmpty ( ) ) {
int boundPos = mEncodedBody . find ( ' \n ' ) ;
if ( - 1 < boundPos ) {
// insert new "boundary" parameter
TQCString bStr ( " ; \n boundary= \" " ) ;
bStr + = mMultipartMixedBoundary ;
bStr + = " \" " ;
KMail : : Util : : insert ( mEncodedBody , boundPos , bStr . data ( ) ) ;
}
}
// replace simple LFs by CRLFs for all MIME supporting CryptPlugs
// according to RfC 2633, 3.1.1 Canonicalization
//kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
mEncodedBody = KMail : : Util : : lf2crlf ( mEncodedBody ) ;
}
if ( doSignBody ) {
mPerformingSignOperation = true ; // this lets the KMComposeWin know if it is safe to close the window.
pgpSignedMsg ( mEncodedBody , format ) ;
mPerformingSignOperation = false ;
if ( mSignature . isEmpty ( ) ) {
kdDebug ( ) < < " signature was empty " < < endl ;
mRc = false ;
return ;
}
mRc = processStructuringInfo ( TQString : : null ,
mOldBodyPart . contentDescription ( ) ,
mOldBodyPart . typeStr ( ) ,
mOldBodyPart . subtypeStr ( ) ,
mOldBodyPart . contentDisposition ( ) ,
mOldBodyPart . contentTransferEncodingStr ( ) ,
mEncodedBody , " signature " ,
mSignature ,
* mNewBodyPart , true , format ) ;
if ( mRc ) {
if ( ! makeMultiPartSigned ( format ) ) {
mNewBodyPart - > setCharset ( mCharset ) ;
}
} else
KMessageBox : : sorry ( mComposeWin ,
mErrorProcessingStructuringInfo ) ;
}
if ( ! mRc )
return ;
continueComposeMessage ( theMessage , doSign , doEncrypt , format ) ;
}
// Do the encryption stuff
void MessageComposer : : continueComposeMessage ( KMMessage & theMessage ,
bool doSign , bool doEncrypt ,
Kleo : : CryptoMessageFormat format )
{
const std : : vector < Kleo : : KeyResolver : : SplitInfo > splitInfos
= mKeyResolver - > encryptionItems ( format ) ;
kdWarning ( splitInfos . empty ( ) )
< < " MessageComposer::continueComposeMessage(): splitInfos.empty() for "
< < Kleo : : cryptoMessageFormatToString ( format ) < < endl ;
if ( ! splitInfos . empty ( ) & & doEncrypt & & ! saveMessagesEncrypted ( ) ) {
mJobs . push_front ( new SetLastMessageAsUnencryptedVersionOfLastButOne ( this ) ) ;
mJobs . push_front ( new EncryptMessageJob ( new KMMessage ( theMessage ) ,
Kleo : : KeyResolver : : SplitInfo ( splitInfos . front ( ) . recipients ) , doSign ,
false , mEncodedBody ,
mPreviousBoundaryLevel ,
/*mOldBodyPart,*/ mNewBodyPart ,
format , this ) ) ;
}
for ( std : : vector < Kleo : : KeyResolver : : SplitInfo > : : const_iterator it = splitInfos . begin ( ) ; it ! = splitInfos . end ( ) ; + + it )
mJobs . push_front ( new EncryptMessageJob ( new KMMessage ( theMessage ) , * it , doSign ,
doEncrypt , mEncodedBody ,
mPreviousBoundaryLevel ,
/*mOldBodyPart,*/ mNewBodyPart ,
format , this ) ) ;
}
void MessageComposer : : encryptMessage ( KMMessage * msg ,
const Kleo : : KeyResolver : : SplitInfo & splitInfo ,
bool doSign , bool doEncrypt ,
KMMessagePart newBodyPart ,
Kleo : : CryptoMessageFormat format )
{
if ( doEncrypt & & splitInfo . keys . empty ( ) ) {
// the user wants to send the message unencrypted
//mComposeWin->setEncryption( false, false );
//FIXME why is this talkback needed? Till
doEncrypt = false ;
}
const bool doEncryptBody = doEncrypt & & mEncryptBody ;
const bool doSignBody = doSign & & mSignBody ;
if ( doEncryptBody ) {
TQByteArray innerContent ;
if ( doSignBody ) {
// extract signed body from newBodyPart
DwBodyPart * dwPart = msg - > createDWBodyPart ( & newBodyPart ) ;
dwPart - > Assemble ( ) ;
innerContent = KMail : : Util : : ByteArray ( dwPart - > AsString ( ) ) ;
delete dwPart ;
dwPart = 0 ;
} else {
innerContent = mEncodedBody ;
}
// now do the encrypting:
// replace simple LFs by CRLFs for all MIME supporting CryptPlugs
// according to RfC 2633, 3.1.1 Canonicalization
//kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
innerContent = KMail : : Util : : lf2crlf ( innerContent ) ;
//kdDebug(5006) << " done." << endl;
TQByteArray encryptedBody ;
Kpgp : : Result result = pgpEncryptedMsg ( encryptedBody , innerContent ,
splitInfo . keys , format ) ;
if ( result ! = Kpgp : : Ok ) {
mRc = false ;
return ;
}
mRc = processStructuringInfo ( " http://www.gnupg.org/aegypten/ " ,
newBodyPart . contentDescription ( ) ,
newBodyPart . typeStr ( ) ,
newBodyPart . subtypeStr ( ) ,
newBodyPart . contentDisposition ( ) ,
newBodyPart . contentTransferEncodingStr ( ) ,
innerContent ,
" encrypted data " ,
encryptedBody ,
newBodyPart , false , format ) ;
if ( ! mRc )
KMessageBox : : sorry ( mComposeWin , mErrorProcessingStructuringInfo ) ;
}
// process the attachments that are not included into the body
if ( mRc ) {
const bool useNewBodyPart = doSignBody | | doEncryptBody ;
addBodyAndAttachments ( msg , splitInfo , doSign , doEncrypt ,
useNewBodyPart ? newBodyPart : mOldBodyPart , format ) ;
}
}
void MessageComposer : : addBodyAndAttachments ( KMMessage * msg ,
const Kleo : : KeyResolver : : SplitInfo & splitInfo ,
bool doSign , bool doEncrypt ,
const KMMessagePart & ourFineBodyPart ,
Kleo : : CryptoMessageFormat format )
{
const bool doEncryptBody = doEncrypt & & mEncryptBody ;
const bool doSignBody = doSign & & mSignBody ;
if ( ! mAttachments . empty ( )
& & ( ! mEarlyAddAttachments | | ! mAllAttachmentsAreInBody ) ) {
// set the content type header
msg - > headers ( ) . ContentType ( ) . SetType ( DwMime : : kTypeMultipart ) ;
msg - > headers ( ) . ContentType ( ) . SetSubtype ( DwMime : : kSubtypeMixed ) ;
msg - > headers ( ) . ContentType ( ) . CreateBoundary ( 0 ) ;
kdDebug ( 5006 ) < < " MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed " < < endl ;
// add our Body Part
DwBodyPart * tmpDwPart = msg - > createDWBodyPart ( & ourFineBodyPart ) ;
DwHeaders & headers = tmpDwPart - > Headers ( ) ;
DwMediaType & ct = headers . ContentType ( ) ;
if ( ! mSaveBoundary . empty ( ) )
ct . SetBoundary ( mSaveBoundary ) ;
tmpDwPart - > Assemble ( ) ;
//KMMessagePart newPart;
//newPart.setBody(tmpDwPart->AsString().c_str());
msg - > addDwBodyPart ( tmpDwPart ) ; // only this method doesn't add it as text/plain
// add Attachments
// create additional bodyparts for the attachments (if any)
KMMessagePart newAttachPart ;
for ( TQValueVector < Attachment > : : iterator it = mAttachments . begin ( ) ; it ! = mAttachments . end ( ) ; + + it ) {
const bool cryptFlagsDifferent = ( it - > encrypt ! = doEncryptBody | | it - > sign ! = doSignBody ) ;
if ( ! cryptFlagsDifferent & & mEarlyAddAttachments )
continue ;
const bool encryptThisNow = doEncrypt & & cryptFlagsDifferent & & it - > encrypt ;
const bool signThisNow = doSign & & cryptFlagsDifferent & & it - > sign ;
if ( ! encryptThisNow & & ! signThisNow ) {
msg - > addBodyPart ( it - > part ) ;
// Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
( void ) msg - > asDwMessage ( ) ;
continue ;
}
KMMessagePart & rEncryptMessagePart ( * it - > part ) ;
DwBodyPart * innerDwPart = msg - > createDWBodyPart ( it - > part ) ;
innerDwPart - > Assemble ( ) ;
TQByteArray encodedAttachment = KMail : : Util : : ByteArray ( innerDwPart - > AsString ( ) ) ;
delete innerDwPart ;
innerDwPart = 0 ;
// replace simple LFs by CRLFs for all MIME supporting CryptPlugs
// according to RfC 2633, 3.1.1 Canonicalization
//kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
encodedAttachment = KMail : : Util : : lf2crlf ( encodedAttachment ) ;
// sign this attachment
if ( signThisNow ) {
pgpSignedMsg ( encodedAttachment , format ) ;
mRc = ! mSignature . isEmpty ( ) ;
if ( mRc ) {
mRc = processStructuringInfo ( " http://www.gnupg.org/aegypten/ " ,
it - > part - > contentDescription ( ) ,
it - > part - > typeStr ( ) ,
it - > part - > subtypeStr ( ) ,
it - > part - > contentDisposition ( ) ,
it - > part - > contentTransferEncodingStr ( ) ,
encodedAttachment ,
" signature " ,
mSignature ,
newAttachPart , true , format ) ;
if ( mRc ) {
if ( encryptThisNow ) {
rEncryptMessagePart = newAttachPart ;
DwBodyPart * dwPart = msg - > createDWBodyPart ( & newAttachPart ) ;
dwPart - > Assemble ( ) ;
encodedAttachment = KMail : : Util : : ByteArray ( dwPart - > AsString ( ) ) ;
delete dwPart ;
dwPart = 0 ;
}
} else
KMessageBox : : sorry ( mComposeWin , mErrorProcessingStructuringInfo ) ;
} else {
// quit the attachments' loop
break ;
}
}
if ( encryptThisNow ) {
TQByteArray encryptedBody ;
Kpgp : : Result result = pgpEncryptedMsg ( encryptedBody ,
encodedAttachment ,
splitInfo . keys ,
format ) ;
if ( Kpgp : : Ok = = result ) {
mRc = processStructuringInfo ( " http://www.gnupg.org/aegypten/ " ,
rEncryptMessagePart . contentDescription ( ) ,
rEncryptMessagePart . typeStr ( ) ,
rEncryptMessagePart . subtypeStr ( ) ,
rEncryptMessagePart . contentDisposition ( ) ,
rEncryptMessagePart . contentTransferEncodingStr ( ) ,
encodedAttachment ,
" encrypted data " ,
encryptedBody ,
newAttachPart , false , format ) ;
if ( ! mRc )
KMessageBox : : sorry ( mComposeWin , mErrorProcessingStructuringInfo ) ;
} else
mRc = false ;
}
msg - > addBodyPart ( & newAttachPart ) ;
( void ) msg - > asDwMessage ( ) ; // Assemble the message. One gets a completely empty message otherwise :/
}
} else { // no attachments in the final message
if ( ourFineBodyPart . originalContentTypeStr ( ) ) {
msg - > headers ( ) . ContentType ( ) . FromString ( ourFineBodyPart . originalContentTypeStr ( ) ) ;
msg - > headers ( ) . ContentType ( ) . Parse ( ) ;
kdDebug ( 5006 ) < < " MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()= " < < ourFineBodyPart . originalContentTypeStr ( ) < < endl ;
} else {
TQCString ct = ourFineBodyPart . typeStr ( ) + " / " + ourFineBodyPart . subtypeStr ( ) ;
if ( ct = = " multipart/mixed " )
ct + = " ; \n \t boundary= \" " + mMultipartMixedBoundary + ' " ' ;
else if ( ct = = " multipart/alternative " )
ct + = " ; \n \t boundary= \" " + TQCString ( mSaveBoundary . c_str ( ) ) + ' " ' ;
msg - > headers ( ) . ContentType ( ) . FromString ( ct ) ;
msg - > headers ( ) . ContentType ( ) . Parse ( ) ;
kdDebug ( 5006 ) < < " MessageComposer::addBodyAndAttachments() : set top level Content-Type to " < < ct < < endl ;
}
if ( ! ourFineBodyPart . charset ( ) . isEmpty ( ) )
msg - > setCharset ( ourFineBodyPart . charset ( ) ) ;
msg - > setHeaderField ( " Content-Transfer-Encoding " ,
ourFineBodyPart . contentTransferEncodingStr ( ) ) ;
msg - > setHeaderField ( " Content-Description " ,
ourFineBodyPart . contentDescription ( ) ) ;
msg - > setHeaderField ( " Content-Disposition " ,
ourFineBodyPart . contentDisposition ( ) ) ;
if ( mDebugComposerCrypto )
kdDebug ( 5006 ) < < " MessageComposer::addBodyAndAttachments() : top level headers and body adjusted " < < endl ;
// set body content
msg - > setBody ( ourFineBodyPart . dwBody ( ) ) ;
}
msg - > setHeaderField ( " X-KMail-Recipients " ,
splitInfo . recipients . join ( " , " ) , KMMessage : : Address ) ;
if ( mDebugComposerCrypto ) {
kdDebug ( 5006 ) < < " MessageComposer::addBodyAndAttachments(): \n Final message: \n ||| " < < msg - > asString ( ) < < " ||| \n \n " < < endl ;
msg - > headers ( ) . Assemble ( ) ;
kdDebug ( 5006 ) < < " \n \n \n MessageComposer::addBodyAndAttachments(): \n Final headers: \n \n " < < msg - > headerAsString ( ) < < " ||| \n \n \n \n \n " < < endl ;
}
}
//-----------------------------------------------------------------------------
// This method does not call any crypto ops, so it does not need to be async
bool MessageComposer : : processStructuringInfo ( const TQString bugURL ,
const TQString contentDescClear ,
const TQCString contentTypeClear ,
const TQCString contentSubtypeClear ,
const TQCString contentDispClear ,
const TQCString contentTEncClear ,
const TQByteArray & clearCStr ,
const TQString /*contentDescCiph*/ ,
const TQByteArray & ciphertext ,
KMMessagePart & resultingPart ,
bool signing , Kleo : : CryptoMessageFormat format )
{
assert ( clearCStr . isEmpty ( ) | | clearCStr [ clearCStr . size ( ) - 1 ] ! = ' \0 ' ) ; // I was called with a TQCString !?
bool bOk = true ;
if ( makeMimeObject ( format , signing ) ) {
TQCString mainHeader = " Content-Type: " ;
const char * toplevelCT = toplevelContentType ( format , signing ) ;
if ( toplevelCT )
mainHeader + = toplevelCT ;
else {
if ( makeMultiMime ( format , signing ) )
mainHeader + = " text/plain " ;
else
mainHeader + = contentTypeClear + ' / ' + contentSubtypeClear ;
}
const TQCString boundaryCStr = KMime : : multiPartBoundary ( ) ;
// add "boundary" parameter
if ( makeMultiMime ( format , signing ) )
mainHeader . replace ( " %boundary " , boundaryCStr ) ;
if ( toplevelCT ) {
if ( const char * str = toplevelContentDisposition ( format , signing ) ) {
mainHeader + = " \n Content-Disposition: " ;
mainHeader + = str ;
}
if ( ! makeMultiMime ( format , signing ) & &
binaryHint ( format ) )
mainHeader + = " \n Content-Transfer-Encoding: base64 " ;
} else {
if ( 0 < contentDispClear . length ( ) ) {
mainHeader + = " \n Content-Disposition: " ;
mainHeader + = contentDispClear ;
}
if ( 0 < contentTEncClear . length ( ) ) {
mainHeader + = " \n Content-Transfer-Encoding: " ;
mainHeader + = contentTEncClear ;
}
}
//kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
DwString mainDwStr ;
mainDwStr = mainHeader + " \n \n " ;
DwBodyPart mainDwPa ( mainDwStr , 0 ) ;
mainDwPa . Parse ( ) ;
KMMessage : : bodyPart ( & mainDwPa , & resultingPart ) ;
if ( ! makeMultiMime ( format , signing ) ) {
if ( signing & & includeCleartextWhenSigning ( format ) ) {
TQByteArray bodyText ( clearCStr ) ;
KMail : : Util : : append ( bodyText , " \n " ) ;
KMail : : Util : : append ( bodyText , ciphertext ) ;
resultingPart . setBodyEncodedBinary ( bodyText ) ;
} else {
resultingPart . setBodyEncodedBinary ( ciphertext ) ;
}
} else {
// Build the encapsulated MIME parts.
// Build a MIME part holding the version information
// taking the body contents returned in
// structuring.data.bodyTextVersion.
TQCString versCStr , codeCStr ;
if ( ! signing & & format = = Kleo : : OpenPGPMIMEFormat )
versCStr =
" Content-Type: application/pgp-encrypted \n "
" Content-Disposition: attachment \n "
" \n "
" Version: 1 " ;
// Build a MIME part holding the code information
// taking the body contents returned in ciphertext.
const char * nestedCT = nestedContentType ( format , signing ) ;
assert ( nestedCT ) ;
codeCStr = " Content-Type: " ;
codeCStr + = nestedCT ;
codeCStr + = ' \n ' ;
if ( const char * str = nestedContentDisposition ( format , signing ) ) {
codeCStr + = " Content-Disposition: " ;
codeCStr + = str ;
codeCStr + = ' \n ' ;
}
if ( binaryHint ( format ) ) {
codeCStr + = " Content-Transfer-Encoding: base64 \n \n " ;
codeCStr + = KMime : : Codec : : codecForName ( " base64 " ) - > encodeToQCString ( ciphertext ) ;
} else
codeCStr + = ' \n ' + TQCString ( ciphertext . data ( ) , ciphertext . size ( ) + 1 ) ;
TQByteArray mainStr ;
KMail : : Util : : append ( mainStr , " -- " ) ;
KMail : : Util : : append ( mainStr , boundaryCStr ) ;
if ( signing & & includeCleartextWhenSigning ( format ) & &
! clearCStr . isEmpty ( ) ) {
KMail : : Util : : append ( mainStr , " \n " ) ;
// clearCStr is the one that can be very big for large attachments, don't merge with the other lines
KMail : : Util : : append ( mainStr , clearCStr ) ;
KMail : : Util : : append ( mainStr , " \n -- " + boundaryCStr ) ;
}
if ( ! versCStr . isEmpty ( ) )
KMail : : Util : : append ( mainStr , " \n " + versCStr + " \n -- " + boundaryCStr ) ;
if ( ! codeCStr . isEmpty ( ) )
KMail : : Util : : append ( mainStr , " \n " + codeCStr + " \n -- " + boundaryCStr ) ;
KMail : : Util : : append ( mainStr , " -- \n " ) ;
//kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
resultingPart . setBodyEncodedBinary ( mainStr ) ;
}
} else { // not making a mime object, build a plain message body.
resultingPart . setContentDescription ( contentDescClear ) ;
resultingPart . setTypeStr ( contentTypeClear ) ;
resultingPart . setSubtypeStr ( contentSubtypeClear ) ;
resultingPart . setContentDisposition ( contentDispClear ) ;
resultingPart . setContentTransferEncodingStr ( contentTEncClear ) ;
TQByteArray resultingBody ;
if ( signing & & includeCleartextWhenSigning ( format ) ) {
if ( ! clearCStr . isEmpty ( ) )
KMail : : Util : : append ( resultingBody , clearCStr ) ;
}
if ( ! ciphertext . isEmpty ( ) )
KMail : : Util : : append ( resultingBody , ciphertext ) ;
else {
// Plugin error!
KMessageBox : : sorry ( mComposeWin ,
i18n ( " <qt><p>Error: The backend did not return "
" any encoded data.</p> "
" <p>Please report this bug:<br>%2</p></qt> " )
. arg ( bugURL ) ) ;
bOk = false ;
}
resultingPart . setBodyEncodedBinary ( resultingBody ) ;
}
return bOk ;
}
//-----------------------------------------------------------------------------
TQCString MessageComposer : : plainTextFromMarkup ( const TQString & markupText )
{
TQTextEdit * hackConspiratorTextEdit = new TQTextEdit ( markupText ) ;
hackConspiratorTextEdit - > setTextFormat ( Qt : : PlainText ) ;
if ( ! mDisableBreaking ) {
hackConspiratorTextEdit - > setWordWrap ( TQTextEdit : : FixedColumnWidth ) ;
hackConspiratorTextEdit - > setWrapColumnOrWidth ( mLineBreakColumn ) ;
}
TQString text = hackConspiratorTextEdit - > text ( ) ;
TQCString textbody ;
const TQTextCodec * codec = KMMsgBase : : codecForName ( mCharset ) ;
if ( mCharset = = " us-ascii " ) {
textbody = KMMsgBase : : toUsAscii ( text ) ;
} else if ( codec = = 0 ) {
kdDebug ( 5006 ) < < " Something is wrong and I can not get a codec. " < < endl ;
textbody = text . local8Bit ( ) ;
} else {
text = codec - > toUnicode ( text . latin1 ( ) , text . length ( ) ) ;
textbody = codec - > fromUnicode ( text ) ;
}
if ( textbody . isNull ( ) ) textbody = " " ;
delete hackConspiratorTextEdit ;
return textbody ;
}
//-----------------------------------------------------------------------------
TQByteArray MessageComposer : : breakLinesAndApplyCodec ( )
{
TQString text ;
TQCString cText ;
if ( mDisableBreaking | | mIsRichText | | ! GlobalSettings : : self ( ) - > wordWrap ( ) )
text = mComposeWin - > mEditor - > text ( ) ;
else
text = mComposeWin - > mEditor - > brokenText ( ) ;
text . truncate ( text . length ( ) ) ; // to ensure text.size()==text.length()+1
TQString newText ;
const TQTextCodec * codec = KMMsgBase : : codecForName ( mCharset ) ;
if ( mCharset = = " us-ascii " ) {
cText = KMMsgBase : : toUsAscii ( text ) ;
newText = TQString : : fromLatin1 ( cText ) ;
} else if ( codec = = 0 ) {
kdDebug ( 5006 ) < < " Something is wrong and I can not get a codec. " < < endl ;
cText = text . local8Bit ( ) ;
newText = TQString : : fromLocal8Bit ( cText ) ;
} else {
cText = codec - > fromUnicode ( text ) ;
newText = codec - > toUnicode ( cText ) ;
}
if ( cText . isNull ( ) ) cText = " " ;
if ( ! text . isEmpty ( ) & & ( newText ! = text ) ) {
TQString oldText = mComposeWin - > mEditor - > text ( ) ;
mComposeWin - > mEditor - > setText ( newText ) ;
KCursorSaver idle ( KBusyPtr : : idle ( ) ) ;
bool anyway = ( KMessageBox : : warningYesNo ( mComposeWin ,
i18n ( " <qt>Not all characters fit into the chosen "
" encoding.<br><br>Send the message anyway?</qt> " ) ,
i18n ( " Some Characters Will Be Lost " ) ,
i18n ( " Lose Characters " ) , i18n ( " Change Encoding " ) ) = = KMessageBox : : Yes ) ;
if ( ! anyway ) {
mComposeWin - > mEditor - > setText ( oldText ) ;
return TQByteArray ( ) ;
}
}
// From RFC 3156:
// Note: The accepted OpenPGP convention is for signed data to end
// with a <CR><LF> sequence. Note that the <CR><LF> sequence
// immediately preceding a MIME boundary delimiter line is considered
// to be part of the delimiter in [3], 5.1. Thus, it is not part of
// the signed data preceding the delimiter line. An implementation
// which elects to adhere to the OpenPGP convention has to make sure
// it inserts a <CR><LF> pair on the last line of the data to be
// signed and transmitted (signed message and transmitted message
// MUST be identical).
// So make sure that the body ends with a <LF>.
if ( cText . isEmpty ( ) | | cText [ cText . length ( ) - 1 ] ! = ' \n ' ) {
kdDebug ( 5006 ) < < " Added an <LF> on the last line " < < endl ;
cText + = " \n " ;
}
return KMail : : Util : : byteArrayFromQCStringNoDetach ( cText ) ;
}
//-----------------------------------------------------------------------------
void MessageComposer : : pgpSignedMsg ( const TQByteArray & cText , Kleo : : CryptoMessageFormat format ) {
assert ( cText . isEmpty ( ) | | cText [ cText . size ( ) - 1 ] ! = ' \0 ' ) ; // I was called with a TQCString !?
mSignature = TQByteArray ( ) ;
const std : : vector < GpgME : : Key > signingKeys = mKeyResolver - > signingKeys ( format ) ;
if ( signingKeys . empty ( ) ) {
KMessageBox : : sorry ( mComposeWin ,
i18n ( " This message could not be signed, "
" since no valid signing keys have been found; "
" this should actually never happen, "
" please report this bug. " ) ) ;
return ;
}
// TODO: ASync call? Likely, yes :-)
const Kleo : : CryptoBackendFactory * cpf = Kleo : : CryptoBackendFactory : : instance ( ) ;
assert ( cpf ) ;
const Kleo : : CryptoBackend : : Protocol * proto
= isSMIME ( format ) ? cpf - > smime ( ) : cpf - > openpgp ( ) ;
assert ( proto ) ; /// hmmm.....?
std : : auto_ptr < Kleo : : SignJob > job ( proto - > signJob ( armor ( format ) ,
textMode ( format ) ) ) ;
if ( ! job . get ( ) ) {
KMessageBox : : sorry ( mComposeWin ,
i18n ( " This message could not be signed, "
" since the chosen backend does not seem to support "
" signing; this should actually never happen, "
" please report this bug. " ) ) ;
return ;
}
TQByteArray signature ;
const GpgME : : SigningResult res =
job - > exec ( signingKeys , cText , signingMode ( format ) , signature ) ;
{
std : : stringstream ss ;
ss < < res ;
kdDebug ( 5006 ) < < ss . str ( ) . c_str ( ) < < endl ;
}
if ( res . error ( ) . isCanceled ( ) ) {
kdDebug ( ) < < " signing was canceled by user " < < endl ;
return ;
}
if ( res . error ( ) ) {
kdDebug ( ) < < " signing failed: " < < res . error ( ) . asString ( ) < < endl ;
job - > showErrorDialog ( mComposeWin ) ;
return ;
}
if ( GlobalSettings : : showGnuPGAuditLogAfterSuccessfulSignEncrypt ( ) )
if ( Kleo : : MessageBox : : showAuditLogButton ( job . get ( ) ) )
Kleo : : MessageBox : : auditLog ( 0 , job . get ( ) , i18n ( " GnuPG Audit Log for Signing Operation " ) ) ;
mSignature = signature ;
if ( mSignature . isEmpty ( ) ) {
KMessageBox : : sorry ( mComposeWin ,
i18n ( " The signing operation failed. "
" Please make sure that the gpg-agent program "
" is running. " ) ) ;
}
}
//-----------------------------------------------------------------------------
Kpgp : : Result MessageComposer : : pgpEncryptedMsg ( TQByteArray & encryptedBody ,
const TQByteArray & cText ,
const std : : vector < GpgME : : Key > & encryptionKeys ,
Kleo : : CryptoMessageFormat format )
{
// TODO: ASync call? Likely, yes :-)
const Kleo : : CryptoBackendFactory * cpf = Kleo : : CryptoBackendFactory : : instance ( ) ;
assert ( cpf ) ;
const Kleo : : CryptoBackend : : Protocol * proto
= isSMIME ( format ) ? cpf - > smime ( ) : cpf - > openpgp ( ) ;
assert ( proto ) ; // hmmmm....?
std : : auto_ptr < Kleo : : EncryptJob > job ( proto - > encryptJob ( armor ( format ) ,
textMode ( format ) ) ) ;
if ( ! job . get ( ) ) {
KMessageBox : : sorry ( mComposeWin ,
i18n ( " This message could not be encrypted, "
" since the chosen backend does not seem to support "
" encryption; this should actually never happen, "
" please report this bug. " ) ) ;
return Kpgp : : Failure ;
}
const GpgME : : EncryptionResult res =
job - > exec ( encryptionKeys , cText , true /* we do ownertrust ourselves */ , encryptedBody ) ;
{
std : : stringstream ss ;
ss < < res ;
kdDebug ( 5006 ) < < ss . str ( ) . c_str ( ) < < endl ;
}
if ( res . error ( ) . isCanceled ( ) ) {
kdDebug ( ) < < " encryption was canceled by user " < < endl ;
return Kpgp : : Canceled ;
}
if ( res . error ( ) ) {
kdDebug ( ) < < " encryption failed: " < < res . error ( ) . asString ( ) < < endl ;
job - > showErrorDialog ( mComposeWin ) ;
return Kpgp : : Failure ;
}
if ( GlobalSettings : : showGnuPGAuditLogAfterSuccessfulSignEncrypt ( ) )
if ( Kleo : : MessageBox : : showAuditLogButton ( job . get ( ) ) )
Kleo : : MessageBox : : auditLog ( 0 , job . get ( ) , i18n ( " GnuPG Audit Log for Encryption Operation " ) ) ;
return Kpgp : : Ok ;
}
Kpgp : : Result MessageComposer : : pgpSignedAndEncryptedMsg ( TQByteArray & encryptedBody ,
const TQByteArray & cText ,
const std : : vector < GpgME : : Key > & signingKeys ,
const std : : vector < GpgME : : Key > & encryptionKeys ,
Kleo : : CryptoMessageFormat format )
{
// TODO: ASync call? Likely, yes :-)
const Kleo : : CryptoBackendFactory * cpf = Kleo : : CryptoBackendFactory : : instance ( ) ;
assert ( cpf ) ;
const Kleo : : CryptoBackend : : Protocol * proto
= isSMIME ( format ) ? cpf - > smime ( ) : cpf - > openpgp ( ) ;
assert ( proto ) ; // hmmmm....?
std : : auto_ptr < Kleo : : SignEncryptJob > job ( proto - > signEncryptJob ( armor ( format ) ,
textMode ( format ) ) ) ;
if ( ! job . get ( ) ) {
KMessageBox : : sorry ( mComposeWin ,
i18n ( " This message could not be signed and encrypted, "
" since the chosen backend does not seem to support "
" combined signing and encryption; this should actually never happen, "
" please report this bug. " ) ) ;
return Kpgp : : Failure ;
}
const std : : pair < GpgME : : SigningResult , GpgME : : EncryptionResult > res =
job - > exec ( signingKeys , encryptionKeys , cText , false , encryptedBody ) ;
{
std : : stringstream ss ;
ss < < res . first < < ' \n ' < < res . second ;
kdDebug ( 5006 ) < < ss . str ( ) . c_str ( ) < < endl ;
}
if ( res . first . error ( ) . isCanceled ( ) | | res . second . error ( ) . isCanceled ( ) ) {
kdDebug ( ) < < " encrypt/sign was canceled by user " < < endl ;
return Kpgp : : Canceled ;
}
if ( res . first . error ( ) | | res . second . error ( ) ) {
if ( res . first . error ( ) )
kdDebug ( ) < < " signing failed: " < < res . first . error ( ) . asString ( ) < < endl ;
else
kdDebug ( ) < < " encryption failed: " < < res . second . error ( ) . asString ( ) < < endl ;
job - > showErrorDialog ( mComposeWin ) ;
return Kpgp : : Failure ;
}
if ( GlobalSettings : : showGnuPGAuditLogAfterSuccessfulSignEncrypt ( ) )
if ( Kleo : : MessageBox : : showAuditLogButton ( job . get ( ) ) )
Kleo : : MessageBox : : auditLog ( 0 , job . get ( ) , i18n ( " GnuPG Audit Log for Encryption Operation " ) ) ;
return Kpgp : : Ok ;
}
# include "messagecomposer.moc"