kmail

messagecomposer.cpp
1
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
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"
43#include "kmfolder.h"
44#include "kmfoldercombobox.h"
45#include "keyresolver.h"
46#include "kleo_util.h"
47#include "globalsettings.h"
48#include "custommimeheader.h"
49#include "kmedit.h"
50#include "util.h"
51
52#include <libkpimidentities/identity.h>
53#include <libkpimidentities/identitymanager.h>
54#include <libemailfunctions/email.h>
55
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>
65
66#include <kmime_util.h>
67#include <kmime_codecs.h>
68#include <kpgpblock.h>
69
70#include <mimelib/mimepp.h>
71
72#include <tdemessagebox.h>
73#include <tdelocale.h>
74#include <kinputdialog.h>
75#include <kdebug.h>
76#include <tdeaction.h>
77#include <tqfile.h>
78#include <tqtextcodec.h>
79#include <tqtextedit.h>
80#include <tqtimer.h>
81
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>
87
88#include <algorithm>
89#include <sstream>
90#include <memory>
91
92// ## keep default values in sync with configuredialog.cpp, Security::CryptoTab::setup()
93// This should be ported to a .kcfg one day I suppose (dfaure).
94
95static inline bool warnSendUnsigned() {
96 TDEConfigGroup group( KMKernel::config(), "Composer" );
97 return group.readBoolEntry( "crypto-warning-unsigned", false );
98}
99static inline bool warnSendUnencrypted() {
100 TDEConfigGroup group( KMKernel::config(), "Composer" );
101 return group.readBoolEntry( "crypto-warning-unencrypted", false );
102}
103static inline bool saveMessagesEncrypted() {
104 TDEConfigGroup group( KMKernel::config(), "Composer" );
105 return group.readBoolEntry( "crypto-store-encrypted", true );
106}
107static inline bool encryptToSelf() {
108 // return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
109 TDEConfigGroup group( KMKernel::config(), "Composer" );
110 return group.readBoolEntry( "crypto-encrypt-to-self", true );
111}
112static inline bool showKeyApprovalDialog() {
113 TDEConfigGroup group( KMKernel::config(), "Composer" );
114 return group.readBoolEntry( "crypto-show-keys-for-approval", true );
115}
116
117static inline int encryptKeyNearExpiryWarningThresholdInDays() {
118 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
119 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
120 return -1;
121 const int num = composer.readNumEntry( "crypto-warn-encr-key-near-expire-int", 14 );
122 return kMax( 1, num );
123}
124
125static inline int signingKeyNearExpiryWarningThresholdInDays() {
126 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
127 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
128 return -1;
129 const int num = composer.readNumEntry( "crypto-warn-sign-key-near-expire-int", 14 );
130 return kMax( 1, num );
131}
132
133static inline int encryptRootCertNearExpiryWarningThresholdInDays() {
134 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
135 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
136 return -1;
137 const int num = composer.readNumEntry( "crypto-warn-encr-root-near-expire-int", 14 );
138 return kMax( 1, num );
139}
140
141static inline int signingRootCertNearExpiryWarningThresholdInDays() {
142 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
143 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
144 return -1;
145 const int num = composer.readNumEntry( "crypto-warn-sign-root-near-expire-int", 14 );
146 return kMax( 1, num );
147}
148
149static inline int encryptChainCertNearExpiryWarningThresholdInDays() {
150 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
151 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
152 return -1;
153 const int num = composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int", 14 );
154 return kMax( 1, num );
155}
156
157static inline int signingChainCertNearExpiryWarningThresholdInDays() {
158 const TDEConfigGroup composer( KMKernel::config(), "Composer" );
159 if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
160 return -1;
161 const int num = composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int", 14 );
162 return kMax( 1, num );
163}
164
165/*
166 Design of this:
167
168 The idea is that the main run of applyChanges here makes two jobs:
169 the first sets the flags for encryption/signing or not, and the other
170 starts the encryption process.
171
172 When a job is run, it has already been removed from the job queue. This
173 means if one of the current jobs needs to add new jobs, it can add them
174 to the front and that way control when new jobs are added.
175
176 For example, the compose message job will add jobs that will do the
177 actual encryption and signing.
178
179 There are two types of jobs: synchronous and asynchronous:
180
181 A synchronous job simply implments the execute() method and performs
182 it's operation there and sets mComposer->mRc to false if the compose
183 queue should be canceled.
184
185 An asynchronous job only sets up and starts it's operation. Before
186 returning, it connects to the result signals of the operation
187 (e.g. Kleo::Job's result(...) signal) and sets mComposer->mHoldJobs
188 to true. This makes the scheduler return to the event loop. The job
189 is now responsible for giving control back to the scheduler by
190 calling mComposer->doNextJob().
191*/
192
193/*
194 Test plan:
195
196 For each message format (e.g. openPGP/MIME)
197 1. Body signed
198 2. Body encrypted
199 3. Body signed and encrypted
200 4. Body encrypted, attachments encrypted (they must be encrypted together, mEarlyAddAttachments)
201 5. Body encrypted, attachments not encrypted
202 6. Body encrypted, attachment encrypted and signed (separately)
203 7. Body not encrypted, one attachment encrypted+signed, one attachment encrypted only, one attachment signed only
204 (https://intevation.de/roundup/aegypten/issue295)
205 (this is the reason attachments can't be encrypted together)
206 8. Body and attachments encrypted+signed (they must be encrypted+signed together, mEarlyAddAttachments)
207 9. Body encrypted+signed, attachments encrypted
208 10. Body encrypted+signed, one attachment signed, one attachment not encrypted nor signed
209 ...
210
211 I recorded a KDExecutor script sending all of the above (David)
212
213 Further tests (which test opportunistic encryption):
214 1. Send a message to a person with valid key but without encryption preference
215 and answer the question whether the message should be encrypted with Yes.
216 2. Send a message to a person with valid key but without encryption preference
217 and answer the question whether the message should be encrypted with No.
218 3. Send a message to a person with valid key and with encryption preference
219 "Encrypt whenever possible" (aka opportunistic encryption).
220*/
221
222static TQString mErrorProcessingStructuringInfo =
223i18n("<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>");
226static TQString mErrorNoCryptPlugAndNoBuildIn =
227i18n("<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>");
234
235
236class MessageComposerJob {
237public:
238 MessageComposerJob( MessageComposer* composer ) : mComposer( composer ) {}
239 virtual ~MessageComposerJob() {}
240
241 virtual void execute() = 0;
242
243protected:
244 // These are the methods that call the private MessageComposer methods
245 // Workaround for friend not being inherited
246 void adjustCryptFlags() { mComposer->adjustCryptFlags(); }
247 void composeMessage() { mComposer->composeMessage(); }
248 void continueComposeMessage( KMMessage& msg, bool doSign, bool doEncrypt,
249 Kleo::CryptoMessageFormat format )
250 {
251 mComposer->continueComposeMessage( msg, doSign, doEncrypt, format );
252 }
253 void chiasmusEncryptAllAttachments() {
254 mComposer->chiasmusEncryptAllAttachments();
255 }
256
257 MessageComposer* mComposer;
258};
259
260class ChiasmusBodyPartEncryptJob : public MessageComposerJob {
261public:
262 ChiasmusBodyPartEncryptJob( MessageComposer * composer )
263 : MessageComposerJob( composer ) {}
264
265 void execute() {
266 chiasmusEncryptAllAttachments();
267 }
268};
269
270class AdjustCryptFlagsJob : public MessageComposerJob {
271public:
272 AdjustCryptFlagsJob( MessageComposer* composer )
273 : MessageComposerJob( composer ) {}
274
275 void execute() {
276 adjustCryptFlags();
277 }
278};
279
280class ComposeMessageJob : public MessageComposerJob {
281public:
282 ComposeMessageJob( MessageComposer* composer )
283 : MessageComposerJob( composer ) {}
284
285 void execute() {
286 composeMessage();
287 }
288};
289
290MessageComposer::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 ),
304 mHoldJobs( false ),
305 mNewBodyPart( 0 ),
306 mEarlyAddAttachments( false ), mAllAttachmentsAreInBody( false ),
307 mPreviousBoundaryLevel( 0 ),
308 mEncryptWithChiasmus( false ),
309 mPerformingSignOperation( false )
310{
311}
312
313MessageComposer::~MessageComposer()
314{
315 delete mKeyResolver; mKeyResolver = 0;
316 delete mNewBodyPart; mNewBodyPart = 0;
317}
318
319void MessageComposer::applyChanges( bool disableCrypto )
320{
321 // Do the initial setup
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;
326 } else {
327 mDebugComposerCrypto = false;
328 kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl;
329 }
330
331 mHoldJobs = false;
332 mRc = true;
333
334 mDisableCrypto = disableCrypto;
335
336 // 1: Read everything from KMComposeWin and set all
337 // trivial parts of the message
338 readFromComposeWin();
339
340 // From now on, we're not supposed to read from the composer win
341 // TODO: Make it so ;-)
342 // 1.5: Replace all body parts with their chiasmus-encrypted equivalent
343 mJobs.push_back( new ChiasmusBodyPartEncryptJob( this ) );
344
345 // 2: Set encryption/signing options and resolve keys
346 mJobs.push_back( new AdjustCryptFlagsJob( this ) );
347
348 // 3: Build the message (makes the crypto jobs also)
349 mJobs.push_back( new ComposeMessageJob( this ) );
350
351 // Finally: Run the jobs
352 doNextJob();
353}
354
355void MessageComposer::doNextJob()
356{
357 delete mCurrentJob; mCurrentJob = 0;
358
359 if( mJobs.isEmpty() ) {
360 // No more jobs. Signal that we're done
361 emitDone( mRc );
362 return;
363 }
364
365 if( !mRc ) {
366 // Something has gone wrong - stop the process and bail out
367 while( !mJobs.isEmpty() ) {
368 delete mJobs.front();
369 mJobs.pop_front();
370 }
371 emitDone( false );
372 return;
373 }
374
375 // We have more jobs to do, but allow others to come first
376 TQTimer::singleShot( 0, this, TQ_SLOT( slotDoNextJob() ) );
377}
378
379void MessageComposer::emitDone( bool b )
380{
381 // Save memory - before sending the mail
382 mEncodedBody = TQByteArray();
383 delete mNewBodyPart; mNewBodyPart = 0;
384 mOldBodyPart.clear();
385 emit done( b );
386}
387
388void MessageComposer::slotDoNextJob()
389{
390 assert( !mCurrentJob );
391 if( mHoldJobs )
392 // Always make it run from now. If more than one job should be held,
393 // The individual jobs must do this.
394 mHoldJobs = false;
395 else {
396 assert( !mJobs.empty() );
397 // Get the next job
398 mCurrentJob = mJobs.front();
399 assert( mCurrentJob );
400 mJobs.pop_front();
401
402 // Execute it
403 mCurrentJob->execute();
404 }
405
406 // Finally run the next job if necessary
407 if( !mHoldJobs )
408 doNextJob();
409}
410
411void MessageComposer::readFromComposeWin()
412{
413 // Copy necessary attributes over
414 mDisableBreaking = false;
415
416 mSignBody = mComposeWin->mSignAction->isChecked();
417 mSigningRequested = mSignBody; // for now; will be adjusted depending on attachments
418 mEncryptBody = mComposeWin->mEncryptAction->isChecked();
419 mEncryptionRequested = mEncryptBody; // for now; will be adjusted depending on attachments
420
421 mAutoCharset = mComposeWin->mAutoCharset;
422 mCharset = mComposeWin->mCharset;
423 mReferenceMessage = mComposeWin->mMsg;
424 // if the user made any modifications to the message then the Content-Type
425 // of the message is no longer reliable (e. g. if he editted a draft/resent a
426 // message and then removed all attachments or changed from PGP/MIME signed
427 // to clearsigned);
428 // even if the user didn't make any modifications to the message the
429 // Content-Type of the message might be wrong, e.g. when inline-forwarding
430 // an mp/alt message then the Content-Type is set to mp/alt although it should
431 // be text/plain (cf. bug 127526);
432 // OTOH we must not reset the Content-Type of inline invitations;
433 // therefore we reset the Content-Type to text/plain whenever the current
434 // Content-Type is multipart/*.
435 if ( mReferenceMessage->type() == DwMime::kTypeMultipart )
436 mReferenceMessage->setHeaderField( "Content-Type", "text/plain" );
437 mUseOpportunisticEncryption = GlobalSettings::self()->pgpAutoEncrypt();
438 mAllowedCryptoMessageFormats = mComposeWin->cryptoMessageFormat();
439
440 if( mAutoCharset ) {
441 TQCString charset = KMMsgBase::autoDetectCharset( mCharset, KMMessage::preferredCharsets(), mComposeWin->mEditor->text() );
442 if( charset.isEmpty() )
443 {
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." ) );
448 mRc = false;
449 return;
450 }
451 mCharset = charset;
452 // Also apply this to the composer window
453 mComposeWin->mCharset = charset;
454 }
455 mReferenceMessage->setCharset(mCharset);
456
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());
463
464 const KPIM::Identity & id = mComposeWin->identity();
465
466 KMFolder *f = mComposeWin->mFcc->getFolder();
467 assert( f != 0 );
468 if ( f->idString() == id.fcc() )
469 mReferenceMessage->removeHeaderField("X-KMail-Fcc");
470 else
471 mReferenceMessage->setFcc( f->idString() );
472
473 // set the correct drafts folder
474 mReferenceMessage->setDrafts( id.drafts() );
475
476 if (id.isDefault())
477 mReferenceMessage->removeHeaderField("X-KMail-Identity");
478 else mReferenceMessage->setHeaderField("X-KMail-Identity", TQString::number( id.uoid() ));
479
480 TQString replyAddr;
481 if (!mComposeWin->replyTo().isEmpty()) replyAddr = mComposeWin->replyTo();
482 else replyAddr = mComposeWin->from();
483
484 if (mComposeWin->mRequestMDNAction->isChecked())
485 mReferenceMessage->setHeaderField("Disposition-Notification-To", replyAddr);
486 else
487 mReferenceMessage->removeHeaderField("Disposition-Notification-To");
488
489 if (mComposeWin->mUrgentAction->isChecked()) {
490 mReferenceMessage->setHeaderField("X-PRIORITY", "2 (High)");
491 mReferenceMessage->setHeaderField("Priority", "urgent");
492 } else {
493 mReferenceMessage->removeHeaderField("X-PRIORITY");
494 mReferenceMessage->removeHeaderField("Priority");
495 }
496
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() );
504 }
505
506
507 // we have to remember the Bcc because it might have been overwritten
508 // by a custom header (therefore we can't use bcc() later) and because
509 // mimelib removes addresses without domain part (therefore we can't use
510 // mReferenceMessage->bcc() later and also not now. So get the Bcc from
511 // the composer window.)
512 mBcc = mComposeWin->bcc();
513 mTo = KPIM::splitEmailAddrList( mComposeWin->to().stripWhiteSpace() );
514 mCc = KPIM::splitEmailAddrList( mComposeWin->cc().stripWhiteSpace() );
515 mBccList = KPIM::splitEmailAddrList( mBcc.stripWhiteSpace() );
516
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 ) ) );
521
522 mEncryptWithChiasmus = mComposeWin->mEncryptWithChiasmus;
523
524 mIsRichText = mComposeWin->mEditor->textFormat() == TQt::RichText;
525 mIdentityUid = mComposeWin->identityUid();
526 mText = breakLinesAndApplyCodec();
527 assert( mText.isEmpty() || mText[mText.size()-1] == '\n' );
528 // Hopefully we can get rid of this eventually, it's needed to be able
529 // to break the plain/text version of a multipart/alternative (html) mail
530 // according to the line breaks of the richtext version.
531 mLineBreakColumn = mComposeWin->mEditor->lineBreakColumn();
532}
533
534static TQCString escape_quoted_string( const TQCString & str ) {
535 TQCString result;
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] ) {
541 case '\\':
542 case '"':
543 *d++ = '\\';
544 default: // fall through:
545 *d++ = ch;
546 }
547 result.truncate( d - result.begin() );
548 return result;
549}
550
551bool MessageComposer::encryptWithChiasmus( const Kleo::CryptoBackend::Protocol * chiasmus,
552 const TQByteArray& body,
553 TQByteArray& resultData )
554{
555 std::unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-encrypt", TQStringVariantMap() ) );
556 if ( !job ) {
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" ) );
560 return false;
561 }
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" ) );
568 return false;
569 }
570 const GpgME::Error err = job->exec();
571 if ( err.isCanceled() || err ) {
572 if ( err )
573 job->showErrorDialog( mComposeWin, i18n( "Chiasmus Encryption Error" ) );
574 return false;
575 }
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" ) );
582 return false;
583 }
584 resultData = result.toByteArray();
585 return true;
586}
587
588void MessageComposer::chiasmusEncryptAllAttachments() {
589 if ( !mEncryptWithChiasmus )
590 return;
591 assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
592 if ( mAttachments.empty() )
593 return;
594 const Kleo::CryptoBackend::Protocol * chiasmus
595 = Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
596 assert( chiasmus ); // kmcomposewin code should have made sure
597
598
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 ) )
603 continue; // already encrypted
604 const TQByteArray body = part->bodyDecodedBinary();
605 TQByteArray resultData;
606 if ( !encryptWithChiasmus( chiasmus, body, resultData ) ) {
607 mRc = false;
608 return;
609 }
610 // everything ok, so let's fill in the part again:
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"
620 ? "*=" + enc_name
621 : "=\"" + escape_quoted_string( enc_name ) + '\"' );
622 part->setContentDisposition( cDisp );
623 }
624}
625
626void MessageComposer::adjustCryptFlags()
627{
628 if ( !mDisableCrypto &&
629 mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat &&
630 !mAttachments.empty() &&
631 ( mSigningRequested || mEncryptionRequested ) )
632 {
633 int ret;
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 "
638 "of attachments.\n"
639 "Really use deprecated inline OpenPGP?"),
640 i18n("Insecure Message Format"),
641 i18n("Use Inline OpenPGP"),
642 i18n("Use OpenPGP/MIME") );
643 }
644 else {
645 // if other crypto message formats are allowed then simply don't use
646 // inline OpenPGP
647 ret = KMessageBox::No;
648 }
649
650 if ( ret == KMessageBox::Cancel ) {
651 mRc = false;
652 return;
653 } else if ( ret == KMessageBox::No ) {
654 mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat;
655 mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat;
656 if ( mSigningRequested ) {
657 // The composer window disabled signing on the attachments, re-enable it
658 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
659 mAttachments[idx].sign = true;
660 }
661 if ( mEncryptionRequested ) {
662 // The composer window disabled encrypting on the attachments, re-enable it
663 // We assume this is what the user wants - after all he chose OpenPGP/MIME for this.
664 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
665 mAttachments[idx].encrypt = true;
666 }
667 }
668 }
669
670 mKeyResolver =
671 new Kleo::KeyResolver( encryptToSelf(), showKeyApprovalDialog(),
672 mUseOpportunisticEncryption, mAllowedCryptoMessageFormats,
673 encryptKeyNearExpiryWarningThresholdInDays(),
674 signingKeyNearExpiryWarningThresholdInDays(),
675 encryptRootCertNearExpiryWarningThresholdInDays(),
676 signingRootCertNearExpiryWarningThresholdInDays(),
677 encryptChainCertNearExpiryWarningThresholdInDays(),
678 signingChainCertNearExpiryWarningThresholdInDays() );
679
680 if ( !mDisableCrypto ) {
681 const KPIM::Identity & id =
682 kmkernel->identityManager()->identityForUoidOrDefault( mIdentityUid );
683
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 ) {
690 mRc = false;
691 return;
692 }
693
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 ) {
700 mRc = false;
701 return;
702 }
703 }
704
705 mKeyResolver->setPrimaryRecipients( mTo + mCc );
706 mKeyResolver->setSecondaryRecipients( mBccList );
707
708 // check settings of composer buttons *and* attachment check boxes
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;
714 else
715 doEncryptCompletely = false;
716 if ( mAttachments[idx].sign )
717 mSigningRequested = true;
718 else
719 doSignCompletely = false;
720 }
721
722 mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely );
723
724 if ( !mRc )
725 return;
726
727 mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely );
728
729 if ( !mRc )
730 return;
731
732 // resolveAllKeys needs to run even if mDisableCrypto == true, since
733 // we depend on it collecting all recipients into one dummy
734 // SplitInfo to avoid special-casing all over the place:
735 if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok )
736 mRc = false;
737}
738
739bool MessageComposer::determineWhetherToSign( bool doSignCompletely ) {
740 bool sign = false;
741 switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) {
742 case Kleo::DoIt:
743 if ( !mSigningRequested ) {
744 markAllAttachmentsForSigning( true );
745 return true;
746 }
747 sign = true;
748 break;
749 case Kleo::DontDoIt:
750 sign = false;
751 break;
752 case Kleo::AskOpportunistic:
753 assert( 0 );
754 case Kleo::Ask:
755 {
756 // the user wants to be asked or has to be asked
757 const KCursorSaver idle( KBusyPtr::idle() );
758 const TQString msg = i18n("Examination of the recipient's signing preferences "
759 "yielded that you be asked whether or not to sign "
760 "this message.\n"
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:
767 mRc = false;
768 return false;
769 case KMessageBox::Yes:
770 markAllAttachmentsForSigning( true );
771 return true;
772 case KMessageBox::No:
773 markAllAttachmentsForSigning( false );
774 return false;
775 }
776 }
777 break;
778 case Kleo::Conflict:
779 {
780 // warn the user that there are conflicting signing preferences
781 const KCursorSaver idle( KBusyPtr::idle() );
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:
790 mRc = false;
791 return false;
792 case KMessageBox::Yes:
793 markAllAttachmentsForSigning( true );
794 return true;
795 case KMessageBox::No:
796 markAllAttachmentsForSigning( false );
797 return false;
798 }
799 }
800 break;
801 case Kleo::Impossible:
802 {
803 const KCursorSaver idle( KBusyPtr::idle() );
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 ) {
811 mRc = false;
812 return false;
813 } else {
814 markAllAttachmentsForSigning( false );
815 return false;
816 }
817 }
818 }
819
820 if ( !sign || !doSignCompletely ) {
821 if ( warnSendUnsigned() ) {
822 const KCursorSaver idle( KBusyPtr::idle() );
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?") // oh, I hate this...
827 : i18n("This message will not be signed.\n"
828 "Sending unsigned message might violate site policy.\n"
829 "Sign message instead?") ; // oh, I hate this...
830 const TQString buttonText = sign && !doSignCompletely
831 ? i18n("&Sign All Parts") : i18n("&Sign") ;
832 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
833 i18n("Unsigned-Message Warning"),
834 buttonText,
835 i18n("Send &As Is") ) ) {
836 case KMessageBox::Cancel:
837 mRc = false;
838 return false;
839 case KMessageBox::Yes:
840 markAllAttachmentsForSigning( true );
841 return true;
842 case KMessageBox::No:
843 return sign || doSignCompletely;
844 }
845 }
846 }
847
848 return sign || doSignCompletely ;
849}
850
851bool MessageComposer::determineWhetherToEncrypt( bool doEncryptCompletely ) {
852 bool encrypt = false;
853 bool opportunistic = false;
854 switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) {
855 case Kleo::DoIt:
856 if ( !mEncryptionRequested ) {
857 markAllAttachmentsForEncryption( true );
858 return true;
859 }
860 encrypt = true;
861 break;
862 case Kleo::DontDoIt:
863 encrypt = false;
864 break;
865 case Kleo::AskOpportunistic:
866 opportunistic = true;
867 // fall through...
868 case Kleo::Ask:
869 {
870 // the user wants to be asked or has to be asked
871 const KCursorSaver idle( KBusyPtr::idle() );
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 "
877 "this message.\n"
878 "Encrypt this message?");
879 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
880 i18n("Encrypt Message?"),
881 mDoSign
882 ? i18n("Sign && &Encrypt")
883 : i18n("&Encrypt"),
884 mDoSign
885 ? i18n("&Sign Only")
886 : i18n("&Send As-Is") ) ) {
887 case KMessageBox::Cancel:
888 mRc = false;
889 return false;
890 case KMessageBox::Yes:
891 markAllAttachmentsForEncryption( true );
892 return true;
893 case KMessageBox::No:
894 markAllAttachmentsForEncryption( false );
895 return false;
896 }
897 }
898 break;
899 case Kleo::Conflict:
900 {
901 // warn the user that there are conflicting encryption preferences
902 const KCursorSaver idle( KBusyPtr::idle() );
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?"),
908 i18n("&Encrypt"),
909 i18n("Do &Not Encrypt") ) ) {
910 case KMessageBox::Cancel:
911 mRc = false;
912 return false;
913 case KMessageBox::Yes:
914 markAllAttachmentsForEncryption( true );
915 return true;
916 case KMessageBox::No:
917 markAllAttachmentsForEncryption( false );
918 return false;
919 }
920 }
921 break;
922 case Kleo::Impossible:
923 {
924 const KCursorSaver idle( KBusyPtr::idle() );
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 ) {
933 mRc = false;
934 return false;
935 } else {
936 markAllAttachmentsForEncryption( false );
937 return false;
938 }
939 }
940 }
941
942 if ( !encrypt || !doEncryptCompletely ) {
943 if ( warnSendUnencrypted() ) {
944 const KCursorSaver idle( KBusyPtr::idle() );
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?") // oh, I hate this...
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?") ; // oh, I hate this...
954 const TQString buttonText = !doEncryptCompletely
955 ? i18n("&Encrypt All Parts") : i18n("&Encrypt") ;
956 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
957 i18n("Unencrypted Message Warning"),
958 buttonText,
959 mDoSign
960 ? i18n("&Sign Only")
961 : i18n("&Send As-Is") ) ) {
962 case KMessageBox::Cancel:
963 mRc = false;
964 return false;
965 case KMessageBox::Yes:
966 markAllAttachmentsForEncryption( true );
967 return true;
968 case KMessageBox::No:
969 return encrypt || doEncryptCompletely;
970 }
971 }
972 }
973
974 return encrypt || doEncryptCompletely ;
975}
976
977void MessageComposer::markAllAttachmentsForSigning( bool sign ) {
978 mSignBody = sign;
979 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
980 it->sign = sign;
981}
982
983void MessageComposer::markAllAttachmentsForEncryption( bool enc ) {
984 mEncryptBody = enc;
985 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
986 it->encrypt = enc;
987}
988
989
990void MessageComposer::composeMessage()
991{
992 for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
993 if ( mKeyResolver->encryptionItems( concreteCryptoMessageFormats[i] ).empty() )
994 continue;
995 KMMessage * msg = new KMMessage( *mReferenceMessage );
996 composeMessage( *msg, mDoSign, mDoEncrypt, concreteCryptoMessageFormats[i] );
997 if ( !mRc )
998 return;
999 }
1000}
1001
1002//
1003// These are replacements for StructuringInfo(Wrapper):
1004//
1005
1006// check whether to use multipart/{signed,encrypted}
1007static inline bool makeMultiMime( Kleo::CryptoMessageFormat f, bool sign ) {
1008 switch ( f ) {
1009 default:
1010 case Kleo::InlineOpenPGPFormat:
1011 case Kleo::SMIMEOpaqueFormat: return false;
1012 case Kleo::OpenPGPMIMEFormat: return true;
1013 case Kleo::SMIMEFormat: return sign; // only on sign - there's no mp/encrypted for S/MIME
1014 }
1015}
1016static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
1017 return makeMultiMime( f, true );
1018}
1019static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
1020 return makeMultiMime( f, false );
1021}
1022
1023static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool /*signing*/ ) {
1024 return f != Kleo::InlineOpenPGPFormat;
1025}
1026
1027static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f, bool signing ) {
1028 switch ( f ) {
1029 default:
1030 case Kleo::InlineOpenPGPFormat: return 0;
1031 case Kleo::OpenPGPMIMEFormat:
1032 return signing ?
1033 "multipart/signed;\n\t"
1034 "boundary=\"%boundary\";\n\t"
1035 "protocol=\"application/pgp-signature\";\n\t"
1036 "micalg=pgp-sha1" // FIXME: obtain this parameter from gpgme!
1037 :
1038 "multipart/encrypted;\n\t"
1039 "boundary=\"%boundary\";\n\t"
1040 "protocol=\"application/pgp-encrypted\""
1041 ;
1042 case Kleo::SMIMEFormat:
1043 if ( signing )
1044 return
1045 "multipart/signed;\n\t"
1046 "boundary=\"%boundary\";\n\t"
1047 "protocol=\"application/pkcs7-signature\";\n\t"
1048 "micalg=sha1"; // FIXME: obtain this parameter from gpgme!
1049 // fall through (for encryption, there's no difference between
1050 // SMIME and SMIMEOpaque, since there is no mp/encrypted for
1051 // S/MIME):
1052 case Kleo::SMIMEOpaqueFormat:
1053 return signing ?
1054 "application/pkcs7-mime;\n\t"
1055 "smime-type=signed-data;\n\t"
1056 "name=\"smime.p7m\";\n\t"
1057 :
1058 "application/pkcs7-mime;\n\t"
1059 "smime-type=enveloped-data;\n\t"
1060 "name=\"smime.p7m\";\n\t"
1061 ;
1062 }
1063}
1064
1065static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
1066 switch ( f ) {
1067 default:
1068 case Kleo::InlineOpenPGPFormat:
1069 case Kleo::OpenPGPMIMEFormat:
1070 return 0;
1071 case Kleo::SMIMEFormat:
1072 if ( signing )
1073 return 0;
1074 case Kleo::SMIMEOpaqueFormat:
1075 return "attachment; filename=\"smime.p7m\"";
1076 }
1077}
1078
1079static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
1080 return makeMultiPartSigned( f );
1081}
1082
1083static inline const char * nestedContentType( Kleo::CryptoMessageFormat f, bool signing ) {
1084 switch ( f ) {
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:
1088 if ( signing )
1089 return "application/pkcs7-signature; name=\"smime.p7s\"";
1090 // fall through:
1091 default:
1092 case Kleo::InlineOpenPGPFormat:
1093 case Kleo::SMIMEOpaqueFormat:
1094 return 0;
1095 }
1096}
1097
1098static 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\"";
1103 return 0;
1104}
1105
1106static inline bool binaryHint( Kleo::CryptoMessageFormat f ) {
1107 switch ( f ) {
1108 case Kleo::SMIMEFormat:
1109 case Kleo::SMIMEOpaqueFormat:
1110 return true;
1111 default:
1112 case Kleo::OpenPGPMIMEFormat:
1113 case Kleo::InlineOpenPGPFormat:
1114 return false;
1115 }
1116}
1117
1118static inline bool armor( Kleo::CryptoMessageFormat f ) {
1119 return !binaryHint( f );
1120}
1121
1122static inline bool textMode( Kleo::CryptoMessageFormat f ) {
1123 return f == Kleo::InlineOpenPGPFormat;
1124}
1125
1126static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) {
1127 switch ( f ) {
1128 case Kleo::SMIMEOpaqueFormat:
1129 return GpgME::Context::Normal;
1130 case Kleo::InlineOpenPGPFormat:
1131 return GpgME::Context::Clearsigned;
1132 default:
1133 case Kleo::SMIMEFormat:
1134 case Kleo::OpenPGPMIMEFormat:
1135 return GpgME::Context::Detached;
1136 }
1137}
1138
1139//
1140// END replacements for StructuringInfo(Wrapper)
1141//
1142
1143class EncryptMessageJob : public MessageComposerJob {
1144public:
1145 EncryptMessageJob( KMMessage* msg, const Kleo::KeyResolver::SplitInfo & si,
1146 bool doSign, bool doEncrypt, const TQByteArray& encodedBody,
1147 int boundaryLevel, /*const KMMessagePart& oldBodyPart,*/
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 ), /*mOldBodyPart( oldBodyPart ),*/
1153 mNewBodyPart( newBodyPart ), mFormat( format ) {}
1154
1155 void execute() {
1156 KMMessagePart tmpNewBodyPart;
1157 tmpNewBodyPart.duplicate( *mNewBodyPart ); // slow - we duplicate everything again
1158
1159 // TODO: Async call
1160
1161 mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt,
1162 tmpNewBodyPart, mFormat );
1163 if ( !mComposer->mRc ) {
1164 delete mMsg; mMsg = 0;
1165 return;
1166 }
1167 mComposer->mMessageList.push_back( mMsg );
1168 }
1169
1170private:
1171 KMMessage* mMsg;
1172 Kleo::KeyResolver::SplitInfo mSplitInfo;
1173 bool mDoSign, mDoEncrypt;
1174 TQByteArray mEncodedBody;
1175 int mBoundaryLevel;
1176 //KMMessagePart mOldBodyPart;
1177 KMMessagePart* mNewBodyPart;
1178 Kleo::CryptoMessageFormat mFormat;
1179};
1180
1181class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
1182public:
1183 SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer )
1184 : MessageComposerJob( composer ) {}
1185
1186 void execute() {
1187 KMMessage * last = mComposer->mMessageList.back();
1188 mComposer->mMessageList.pop_back();
1189 mComposer->mMessageList.back()->setUnencryptedMsg( last );
1190 }
1191};
1192
1193void MessageComposer::composeInlineOpenPGPMessage( KMMessage& theMessage,
1194 bool doSign, bool doEncrypt )
1195{
1196 // preprocess the body text
1197 const TQByteArray bodyData = mText;
1198 if (bodyData.isNull()) {
1199 mRc = false;
1200 return;
1201 }
1202
1203 mNewBodyPart = 0; // unused
1204 mEarlyAddAttachments = false;
1205 mAllAttachmentsAreInBody = false;
1206
1207 // set the main headers
1208 theMessage.deleteBodyParts();
1209 TQString oldContentType = theMessage.headerField( "Content-Type" );
1210 theMessage.removeHeaderField("Content-Type");
1211 theMessage.removeHeaderField("Content-Transfer-Encoding");
1212
1213 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1214 = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat );
1215 kdWarning( splitInfos.empty() )
1216 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat"
1217 << endl;
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;
1221 KMMessage* msg = new KMMessage( theMessage );
1222 if ( doEncrypt ) {
1223 Kpgp::Result result;
1224 TQByteArray encryptedBody;
1225 if ( doSign ) { // Sign and encrypt
1226 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat );
1227 result = pgpSignedAndEncryptedMsg( encryptedBody, bodyData, signingKeys,
1228 splitInfo.keys, Kleo::InlineOpenPGPFormat );
1229 } else { // Encrypt but don't sign
1230 result = pgpEncryptedMsg( encryptedBody, bodyData,
1231 splitInfo.keys, Kleo::InlineOpenPGPFormat );
1232 }
1233 if ( result != Kpgp::Ok ) {
1234 mRc = false;
1235 return;
1236 }
1237 assert( !encryptedBody.isNull() ); // if you hit this, check gpg-agent is running, then blame gpgme.
1238 mOldBodyPart.setBodyEncodedBinary( encryptedBody );
1239 } else {
1240 if ( doSign ) { // Sign but don't encrypt
1241 pgpSignedMsg( bodyData, Kleo::InlineOpenPGPFormat );
1242 if ( mSignature.isNull() ) {
1243 mRc = false;
1244 return;
1245 }
1246 mOldBodyPart.setBodyEncodedBinary( mSignature );
1247 } else { // don't sign nor encrypt -> nothing to do
1248 assert( !bodyData.isNull() );
1249 mOldBodyPart.setBodyEncodedBinary( bodyData );
1250 }
1251 }
1252 mOldBodyPart.setContentDisposition( "inline" );
1253 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1254 if (mOldBodyPart.type() == DwMime::kTypeText) {
1255 mOldBodyPart.setCharset(mCharset);
1256 }
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 );
1262 KMMessage* msgUnenc = new KMMessage( theMessage );
1263 addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1264 msg->setUnencryptedMsg( msgUnenc );
1265 }
1266 }
1267 } // end for
1268}
1269
1270// very much inspired by composeInlineOpenPGPMessage
1271void MessageComposer::composeChiasmusMessage( KMMessage& theMessage, Kleo::CryptoMessageFormat format )
1272{
1273 assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
1274 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
1275 assert( cpf );
1276 const Kleo::CryptoBackend::Protocol * chiasmus
1277 = cpf->protocol( "Chiasmus" );
1278 assert( chiasmus ); // kmcomposewin code should have made sure
1279
1280 // preprocess the body text
1281 const TQByteArray bodyData = mText;
1282 if (bodyData.isNull()) {
1283 mRc = false;
1284 return;
1285 }
1286
1287 mNewBodyPart = 0; // unused
1288 mEarlyAddAttachments = false;
1289 mAllAttachmentsAreInBody = false;
1290
1291 // set the main headers
1292 theMessage.deleteBodyParts();
1293 TQString oldContentType = theMessage.headerField( "Content-Type" );
1294 theMessage.removeHeaderField("Content-Type");
1295 theMessage.removeHeaderField("Content-Transfer-Encoding");
1296
1297 // This reads strange, but we know that AdjustCryptFlagsJob created a single splitinfo,
1298 // under the given "format" (usually openpgp/mime; doesn't matter)
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 )
1303 {
1304 const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
1305 KMMessage* msg = new KMMessage( theMessage );
1306 TQByteArray encryptedBody;
1307
1308 if ( !encryptWithChiasmus( chiasmus, bodyData, encryptedBody ) ) {
1309 mRc = false;
1310 return;
1311 }
1312 assert( !encryptedBody.isNull() );
1313 // This leaves CTE==7-bit, no good
1314 //mOldBodyPart.setBodyEncodedBinary( encryptedBody );
1315
1316 bool doSign = false;
1317 TQValueList<int> allowedCTEs;
1318 mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
1319 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1320 doSign );
1321
1322
1323 mOldBodyPart.setContentDisposition( "inline" );
1324 // Used in case of no attachments
1325 mOldBodyPart.setOriginalContentTypeStr( "application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
1326 // Used in case of attachments
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 );
1332
1333 if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
1334 mOldBodyPart.setBodyEncodedBinary( bodyData );
1335 KMMessage* msgUnenc = new KMMessage( theMessage );
1336 addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1337 msg->setUnencryptedMsg( msgUnenc );
1338 }
1339 }
1340}
1341
1342void MessageComposer::composeMessage( KMMessage& theMessage,
1343 bool doSign, bool doEncrypt,
1344 Kleo::CryptoMessageFormat format )
1345{
1346#ifdef DEBUG
1347 kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl;
1348#endif
1349 if ( format == Kleo::InlineOpenPGPFormat ) {
1350 composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
1351 return;
1352 }
1353
1354 if ( mEncryptWithChiasmus )
1355 {
1356 composeChiasmusMessage( theMessage, format );
1357 return;
1358 }
1359
1360 // create informative header for those that have no mime-capable
1361 // email client
1362 theMessage.setBody( "This message is in MIME format." );
1363
1364 // preprocess the body text
1365 TQByteArray bodyData = mText;
1366 if (bodyData.isNull()) {
1367 mRc = false;
1368 return;
1369 }
1370
1371 // set the main headers
1372 TQString oldContentType = theMessage.headerField( "Content-Type" );
1373 theMessage.deleteBodyParts();
1374 theMessage.removeHeaderField("Content-Type");
1375 theMessage.removeHeaderField("Content-Transfer-Encoding");
1376 theMessage.setAutomaticFields(true); // == multipart/mixed
1377
1378 // this is our *final* body part
1379 mNewBodyPart = new KMMessagePart;
1380
1381 // this is the boundary depth of the surrounding MIME part
1382 mPreviousBoundaryLevel = 0;
1383
1384 // whether the body must be signed/encrypted
1385 const bool doEncryptBody = doEncrypt && mEncryptBody;
1386 const bool doSignBody = doSign && mSignBody;
1387
1388 // create temporary bodyPart for editor text
1389 // (and for all attachments, if mail is to be signed and/or encrypted)
1390 mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
1391
1392 mAllAttachmentsAreInBody = mEarlyAddAttachments;
1393
1394 // test whether there ARE attachments that can be included into the body
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 )
1399 someOk = true;
1400 else
1401 mAllAttachmentsAreInBody = false;
1402 }
1403 if( !mAllAttachmentsAreInBody && !someOk )
1404 mEarlyAddAttachments = false;
1405 }
1406
1407 kdDebug(5006) << "mEarlyAddAttachments=" << mEarlyAddAttachments << " mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
1408
1409 // if an html message is to be generated, make a text/plain and text/html part
1410 mMultipartMixedBoundary = "";
1411 if ( mEarlyAddAttachments ) {
1412 mOldBodyPart.setTypeStr( "multipart" );
1413 mOldBodyPart.setSubtypeStr( "mixed" );
1414 // calculate a boundary string
1415 DwMediaType tmpCT;
1416 tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1417 mMultipartMixedBoundary = tmpCT.Boundary().c_str();
1418 }
1419 else if ( mIsRichText ) {
1420 mOldBodyPart.setTypeStr( "multipart" );
1421 mOldBodyPart.setSubtypeStr( "alternative" );
1422 }
1423 else
1424 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1425
1426 mOldBodyPart.setContentDisposition( "inline" );
1427
1428 if ( mIsRichText ) { // create a multipart body
1429 // calculate a boundary string
1430 TQCString boundaryCStr; // storing boundary string data
1431 TQCString newbody;
1432 DwMediaType tmpCT;
1433 tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1434 boundaryCStr = KMail::Util::CString( tmpCT.Boundary() );
1435 TQValueList<int> allowedCTEs;
1436
1437 KMMessagePart textBodyPart;
1438 textBodyPart.setTypeStr("text");
1439 textBodyPart.setSubtypeStr("plain");
1440
1441 TQCString textbody = plainTextFromMarkup( mText /* converted to TQString */ );
1442
1443 // the signed body must not be 8bit encoded
1444 textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
1445 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1446 doSign );
1447 textBodyPart.setCharset( mCharset );
1448 textBodyPart.setBodyEncoded( textbody );
1449 DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart );
1450 textDwPart->Assemble();
1451 newbody += "--";
1452 newbody += boundaryCStr;
1453 newbody += "\n";
1454 newbody += textDwPart->AsString().c_str();
1455 delete textDwPart;
1456 textDwPart = 0;
1457
1458 KMMessagePart htmlBodyPart;
1459 htmlBodyPart.setTypeStr("text");
1460 htmlBodyPart.setSubtypeStr("html");
1461 TQByteArray htmlbody = mText;
1462 // the signed body must not be 8bit encoded
1463 htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
1464 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1465 doSign );
1466 htmlBodyPart.setCharset( mCharset );
1467 htmlBodyPart.setBodyEncodedBinary( htmlbody );
1468 DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart );
1469 htmlDwPart->Assemble();
1470 newbody += "\n--";
1471 newbody += boundaryCStr;
1472 newbody += "\n";
1473 newbody += htmlDwPart->AsString().c_str();
1474 delete htmlDwPart;
1475 htmlDwPart = 0;
1476
1477 newbody += "--";
1478 newbody += boundaryCStr;
1479 newbody += "--\n";
1480 bodyData = KMail::Util::byteArrayFromTQCStringNoDetach( newbody );
1481 mOldBodyPart.setBodyEncodedBinary( bodyData );
1482
1483 mSaveBoundary = tmpCT.Boundary();
1484 }
1485
1486 // Prepare attachments that will be signed/encrypted
1487 for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1488 // signed/encrypted body parts must be either QP or base64 encoded
1489 // Why not 7 bit? Because the LF->CRLF canonicalization would render
1490 // e.g. 7 bit encoded shell scripts unusable because of the CRs.
1491 //
1492 // (marc) this is a workaround for the KMail bug that doesn't
1493 // respect the CRLF->LF de-canonicalisation. We should
1494 // eventually get rid of this:
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;
1505 }
1506 }
1507 }
1508
1509 if( mEarlyAddAttachments ) {
1510 // add the normal body text
1511 KMMessagePart innerBodyPart;
1512 if ( mIsRichText ) {
1513 innerBodyPart.setTypeStr( "multipart");//text" );
1514 innerBodyPart.setSubtypeStr("alternative");//html");
1515 }
1516 else {
1517 innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1518 }
1519 innerBodyPart.setContentDisposition( "inline" );
1520 TQValueList<int> allowedCTEs;
1521 // the signed body must not be 8bit encoded
1522 innerBodyPart.setBodyAndGuessCte( bodyData, allowedCTEs,
1523 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1524 doSign );
1525 if ( !mIsRichText )
1526 innerBodyPart.setCharset( mCharset );
1527 innerBodyPart.setBodyEncodedBinary( bodyData ); // do we need this, since setBodyAndGuessCte does this already?
1528 DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
1529 innerDwPart->Assemble();
1530 TQByteArray tmpbody = KMail::Util::ByteArray( innerDwPart->AsString() );
1531 if ( mIsRichText ) { // and add our mp/a boundary
1532 int boundPos = tmpbody.find( '\n' );
1533 if( -1 < boundPos ) {
1534 TQCString bStr( ";\n boundary=\"" );
1535 bStr += mSaveBoundary.c_str();
1536 bStr += "\"";
1537 bodyData = tmpbody;
1538 KMail::Util::insert( bodyData, boundPos, bStr );
1539 KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
1540 }
1541 }
1542 else {
1543 bodyData = tmpbody;
1544 KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
1545 }
1546 delete innerDwPart;
1547 innerDwPart = 0;
1548 // add all matching Attachments
1549 // NOTE: This code will be changed when KMime is complete.
1550 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1551 if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
1552 innerDwPart = theMessage.createDWBodyPart( it->part );
1553 innerDwPart->Assemble();
1554 KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "\n" ) );
1555 KMail::Util::append( bodyData, innerDwPart->AsString().c_str() );
1556 delete innerDwPart;
1557 innerDwPart = 0;
1558 }
1559 }
1560 KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "--\n" ) );
1561 } else { // !earlyAddAttachments
1562 TQValueList<int> allowedCTEs;
1563 // the signed body must not be 8bit encoded
1564 mOldBodyPart.setBodyAndGuessCte(bodyData, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1565 doSign);
1566 if ( !mIsRichText )
1567 mOldBodyPart.setCharset(mCharset);
1568 }
1569 // create S/MIME body part for signing and/or encrypting
1570 mOldBodyPart.setBodyEncodedBinary( bodyData );
1571
1572 if( doSignBody || doEncryptBody ) {
1573 // get string representation of body part (including the attachments)
1574
1575 DwBodyPart* dwPart;
1576 if ( mIsRichText && !mEarlyAddAttachments ) {
1577 // if we are using richtext and not already have a mp/a body
1578 // make the body a mp/a body
1579 dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
1580 DwHeaders& headers = dwPart->Headers();
1581 DwMediaType& ct = headers.ContentType();
1582 ct.SetBoundary(mSaveBoundary);
1583 dwPart->Assemble();
1584 }
1585 else {
1586 dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
1587 dwPart->Assemble();
1588 }
1589 mEncodedBody = KMail::Util::ByteArray( dwPart->AsString() );
1590 delete dwPart;
1591 dwPart = 0;
1592
1593 // manually add a boundary definition to the Content-Type header
1594 if( !mMultipartMixedBoundary.isEmpty() ) {
1595 int boundPos = mEncodedBody.find( '\n' );
1596 if( -1 < boundPos ) {
1597 // insert new "boundary" parameter
1598 TQCString bStr( ";\n boundary=\"" );
1599 bStr += mMultipartMixedBoundary;
1600 bStr += "\"";
1601 KMail::Util::insert( mEncodedBody, boundPos, bStr.data() );
1602 }
1603 }
1604
1605 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1606 // according to RfC 2633, 3.1.1 Canonicalization
1607 //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1608 mEncodedBody = KMail::Util::lf2crlf( mEncodedBody );
1609 }
1610
1611 if ( doSignBody ) {
1612 mPerformingSignOperation = true; // this lets the KMComposeWin know if it is safe to close the window.
1613 pgpSignedMsg( mEncodedBody, format );
1614 mPerformingSignOperation = false;
1615
1616 if ( mSignature.isEmpty() ) {
1617 kdDebug() << "signature was empty" << endl;
1618 mRc = false;
1619 return;
1620 }
1621 mRc = processStructuringInfo( TQString(),
1622 mOldBodyPart.contentDescription(),
1623 mOldBodyPart.typeStr(),
1624 mOldBodyPart.subtypeStr(),
1625 mOldBodyPart.contentDisposition(),
1626 mOldBodyPart.contentTransferEncodingStr(),
1627 mEncodedBody, "signature",
1628 mSignature,
1629 *mNewBodyPart, true, format );
1630 if ( mRc ) {
1631 if ( !makeMultiPartSigned( format ) ) {
1632 mNewBodyPart->setCharset( mCharset );
1633 }
1634 } else
1635 KMessageBox::sorry( mComposeWin,
1636 mErrorProcessingStructuringInfo );
1637 }
1638
1639 if ( !mRc )
1640 return;
1641
1642 continueComposeMessage( theMessage, doSign, doEncrypt, format );
1643}
1644
1645// Do the encryption stuff
1646void MessageComposer::continueComposeMessage( KMMessage& theMessage,
1647 bool doSign, bool doEncrypt,
1648 Kleo::CryptoMessageFormat format )
1649{
1650
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;
1656
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,
1663 /*mOldBodyPart,*/ mNewBodyPart,
1664 format, this ) );
1665 }
1666
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,
1671 /*mOldBodyPart,*/ mNewBodyPart,
1672 format, this ) );
1673}
1674
1675void MessageComposer::encryptMessage( KMMessage* msg,
1676 const Kleo::KeyResolver::SplitInfo & splitInfo,
1677 bool doSign, bool doEncrypt,
1678 KMMessagePart newBodyPart,
1679 Kleo::CryptoMessageFormat format )
1680{
1681 if ( doEncrypt && splitInfo.keys.empty() ) {
1682 // the user wants to send the message unencrypted
1683 //mComposeWin->setEncryption( false, false );
1684 //FIXME why is this talkback needed? Till
1685 doEncrypt = false;
1686 }
1687
1688 const bool doEncryptBody = doEncrypt && mEncryptBody;
1689 const bool doSignBody = doSign && mSignBody;
1690
1691 if ( doEncryptBody ) {
1692 TQByteArray innerContent;
1693 if ( doSignBody ) {
1694 // extract signed body from newBodyPart
1695 DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
1696 dwPart->Assemble();
1697 innerContent = KMail::Util::ByteArray( dwPart->AsString() );
1698 delete dwPart;
1699 dwPart = 0;
1700 } else {
1701 innerContent = mEncodedBody;
1702 }
1703
1704 // now do the encrypting:
1705 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1706 // according to RfC 2633, 3.1.1 Canonicalization
1707 //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1708 innerContent = KMail::Util::lf2crlf( innerContent );
1709 //kdDebug(5006) << " done." << endl;
1710
1711 TQByteArray encryptedBody;
1712 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
1713 splitInfo.keys, format );
1714 if ( result != Kpgp::Ok ) {
1715 mRc = false;
1716 return;
1717 }
1718 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
1719 newBodyPart.contentDescription(),
1720 newBodyPart.typeStr(),
1721 newBodyPart.subtypeStr(),
1722 newBodyPart.contentDisposition(),
1723 newBodyPart.contentTransferEncodingStr(),
1724 innerContent,
1725 "encrypted data",
1726 encryptedBody,
1727 newBodyPart, false, format );
1728 if ( !mRc )
1729 KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
1730 }
1731
1732 // process the attachments that are not included into the body
1733 if( mRc ) {
1734 const bool useNewBodyPart = doSignBody || doEncryptBody;
1735 addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
1736 useNewBodyPart ? newBodyPart : mOldBodyPart, format );
1737 }
1738}
1739
1740void MessageComposer::addBodyAndAttachments( KMMessage* msg,
1741 const Kleo::KeyResolver::SplitInfo & splitInfo,
1742 bool doSign, bool doEncrypt,
1743 const KMMessagePart& ourFineBodyPart,
1744 Kleo::CryptoMessageFormat format )
1745{
1746 const bool doEncryptBody = doEncrypt && mEncryptBody;
1747 const bool doSignBody = doSign && mSignBody;
1748
1749 if( !mAttachments.empty()
1750 && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
1751 // set the content type header
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;
1756
1757 // add our Body Part
1758 DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
1759 DwHeaders& headers = tmpDwPart->Headers();
1760 DwMediaType& ct = headers.ContentType();
1761 if ( !mSaveBoundary.empty() )
1762 ct.SetBoundary(mSaveBoundary);
1763 tmpDwPart->Assemble();
1764
1765 //KMMessagePart newPart;
1766 //newPart.setBody(tmpDwPart->AsString().c_str());
1767 msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain
1768
1769 // add Attachments
1770 // create additional bodyparts for the attachments (if any)
1771 KMMessagePart newAttachPart;
1772 for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1773
1774 const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
1775
1776 if ( !cryptFlagsDifferent && mEarlyAddAttachments )
1777 continue;
1778
1779 const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
1780 const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
1781
1782 if ( !encryptThisNow && !signThisNow ) {
1783 msg->addBodyPart( it->part );
1784 // Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
1785 (void)msg->asDwMessage();
1786 continue;
1787 }
1788
1789 KMMessagePart& rEncryptMessagePart( *it->part );
1790
1791 DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
1792 innerDwPart->Assemble();
1793 TQByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
1794 delete innerDwPart;
1795 innerDwPart = 0;
1796
1797 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1798 // according to RfC 2633, 3.1.1 Canonicalization
1799 //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1800 encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
1801
1802 // sign this attachment
1803 if( signThisNow ) {
1804 pgpSignedMsg( encodedAttachment, format );
1805 mRc = !mSignature.isEmpty();
1806 if( mRc ) {
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(),
1813 encodedAttachment,
1814 "signature",
1815 mSignature,
1816 newAttachPart, true, format );
1817 if( mRc ) {
1818 if( encryptThisNow ) {
1819 rEncryptMessagePart = newAttachPart;
1820 DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
1821 dwPart->Assemble();
1822 encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
1823 delete dwPart;
1824 dwPart = 0;
1825 }
1826 } else
1827 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1828 } else {
1829 // quit the attachments' loop
1830 break;
1831 }
1832 }
1833 if( encryptThisNow ) {
1834 TQByteArray encryptedBody;
1835 Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
1836 encodedAttachment,
1837 splitInfo.keys,
1838 format );
1839
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(),
1847 encodedAttachment,
1848 "encrypted data",
1849 encryptedBody,
1850 newAttachPart, false, format );
1851 if ( !mRc )
1852 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1853 } else
1854 mRc = false;
1855 }
1856 msg->addBodyPart( &newAttachPart );
1857 (void)msg->asDwMessage(); // Assemble the message. One gets a completely empty message otherwise :/
1858 }
1859 } else { // no attachments in the final message
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;
1864 } else {
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;
1873 }
1874 if ( !ourFineBodyPart.charset().isEmpty() )
1875 msg->setCharset( ourFineBodyPart.charset() );
1876 msg->setHeaderField( "Content-Transfer-Encoding",
1877 ourFineBodyPart.contentTransferEncodingStr() );
1878 msg->setHeaderField( "Content-Description",
1879 ourFineBodyPart.contentDescription() );
1880 msg->setHeaderField( "Content-Disposition",
1881 ourFineBodyPart.contentDisposition() );
1882
1883 if ( mDebugComposerCrypto )
1884 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
1885
1886 // set body content
1887 msg->setBody( ourFineBodyPart.dwBody() );
1888
1889 }
1890
1891 msg->setHeaderField( "X-KMail-Recipients",
1892 splitInfo.recipients.join(", "), KMMessage::Address );
1893
1894 if ( mDebugComposerCrypto ) {
1895 kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
1896 msg->headers().Assemble();
1897 kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
1898 }
1899}
1900
1901//-----------------------------------------------------------------------------
1902// This method does not call any crypto ops, so it does not need to be async
1903bool 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,
1910 const TQString /*contentDescCiph*/,
1911 const TQByteArray& ciphertext,
1912 KMMessagePart& resultingPart,
1913 bool signing, Kleo::CryptoMessageFormat format )
1914{
1915 assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' ); // I was called with a TQCString !?
1916 bool bOk = true;
1917
1918 if ( makeMimeObject( format, signing ) ) {
1919 TQCString mainHeader = "Content-Type: ";
1920 const char * toplevelCT = toplevelContentType( format, signing );
1921 if ( toplevelCT )
1922 mainHeader += toplevelCT;
1923 else {
1924 if( makeMultiMime( format, signing ) )
1925 mainHeader += "text/plain";
1926 else
1927 mainHeader += contentTypeClear + '/' + contentSubtypeClear;
1928 }
1929
1930 const TQCString boundaryCStr = KMime::multiPartBoundary();
1931 // add "boundary" parameter
1932 if ( makeMultiMime( format, signing ) )
1933 mainHeader.replace( "%boundary", boundaryCStr );
1934
1935 if ( toplevelCT ) {
1936 if ( const char * str = toplevelContentDisposition( format, signing ) ) {
1937 mainHeader += "\nContent-Disposition: ";
1938 mainHeader += str;
1939 }
1940 if ( !makeMultiMime( format, signing ) &&
1941 binaryHint( format ) )
1942 mainHeader += "\nContent-Transfer-Encoding: base64";
1943 } else {
1944 if( 0 < contentDispClear.length() ) {
1945 mainHeader += "\nContent-Disposition: ";
1946 mainHeader += contentDispClear;
1947 }
1948 if( 0 < contentTEncClear.length() ) {
1949 mainHeader += "\nContent-Transfer-Encoding: ";
1950 mainHeader += contentTEncClear;
1951 }
1952 }
1953
1954 //kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
1955
1956 DwString mainDwStr;
1957 mainDwStr = TQCString(mainHeader + "\n\n").data();
1958 DwBodyPart mainDwPa( mainDwStr, 0 );
1959 mainDwPa.Parse();
1960 KMMessage::bodyPart( &mainDwPa, &resultingPart );
1961 if( !makeMultiMime( format, signing ) ) {
1962 if ( signing && includeCleartextWhenSigning( format ) ) {
1963 TQByteArray bodyText( clearCStr );
1964 KMail::Util::append( bodyText, "\n" );
1965 KMail::Util::append( bodyText, ciphertext );
1966 resultingPart.setBodyEncodedBinary( bodyText );
1967 } else {
1968 resultingPart.setBodyEncodedBinary( ciphertext );
1969 }
1970 } else {
1971 // Build the encapsulated MIME parts.
1972 // Build a MIME part holding the version information
1973 // taking the body contents returned in
1974 // structuring.data.bodyTextVersion.
1975 TQCString versCStr, codeCStr;
1976 if ( !signing && format == Kleo::OpenPGPMIMEFormat )
1977 versCStr =
1978 "Content-Type: application/pgp-encrypted\n"
1979 "Content-Disposition: attachment\n"
1980 "\n"
1981 "Version: 1";
1982
1983 // Build a MIME part holding the code information
1984 // taking the body contents returned in ciphertext.
1985 const char * nestedCT = nestedContentType( format, signing );
1986 assert( nestedCT );
1987 codeCStr = "Content-Type: ";
1988 codeCStr += nestedCT;
1989 codeCStr += '\n';
1990 if ( const char * str = nestedContentDisposition( format, signing ) ) {
1991 codeCStr += "Content-Disposition: ";
1992 codeCStr += str;
1993 codeCStr += '\n';
1994 }
1995 if ( binaryHint( format ) ) {
1996 codeCStr += "Content-Transfer-Encoding: base64\n\n";
1997 codeCStr += KMime::Codec::codecForName( "base64" )->encodeToTQCString( ciphertext );
1998 } else
1999 codeCStr += '\n' + TQCString( ciphertext.data(), ciphertext.size() + 1 );
2000
2001
2002 TQByteArray mainStr;
2003 KMail::Util::append( mainStr, "--" );
2004 KMail::Util::append( mainStr, boundaryCStr );
2005 if ( signing && includeCleartextWhenSigning( format ) &&
2006 !clearCStr.isEmpty() ) {
2007 KMail::Util::append( mainStr, "\n" );
2008 // clearCStr is the one that can be very big for large attachments, don't merge with the other lines
2009 KMail::Util::append( mainStr, clearCStr );
2010 KMail::Util::append( mainStr, "\n--" + boundaryCStr );
2011 }
2012 if ( !versCStr.isEmpty() )
2013 KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
2014 if( !codeCStr.isEmpty() )
2015 KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
2016 KMail::Util::append( mainStr, "--\n" );
2017
2018 //kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
2019 resultingPart.setBodyEncodedBinary( mainStr );
2020 }
2021
2022 } else { // not making a mime object, build a plain message body.
2023
2024 resultingPart.setContentDescription( contentDescClear );
2025 resultingPart.setTypeStr( contentTypeClear );
2026 resultingPart.setSubtypeStr( contentSubtypeClear );
2027 resultingPart.setContentDisposition( contentDispClear );
2028 resultingPart.setContentTransferEncodingStr( contentTEncClear );
2029 TQByteArray resultingBody;
2030
2031 if ( signing && includeCleartextWhenSigning( format ) ) {
2032 if( !clearCStr.isEmpty() )
2033 KMail::Util::append( resultingBody, clearCStr );
2034 }
2035 if ( !ciphertext.isEmpty() )
2036 KMail::Util::append( resultingBody, ciphertext );
2037 else {
2038 // Plugin error!
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>" )
2043 .arg( bugURL ) );
2044 bOk = false;
2045 }
2046 resultingPart.setBodyEncodedBinary( resultingBody );
2047 }
2048
2049 return bOk;
2050}
2051
2052//-----------------------------------------------------------------------------
2053TQCString MessageComposer::plainTextFromMarkup( const TQString& markupText )
2054{
2055 TQTextEdit *hackConspiratorTextEdit = new TQTextEdit( markupText );
2056 hackConspiratorTextEdit->setTextFormat(TQt::PlainText);
2057 if ( !mDisableBreaking ) {
2058 hackConspiratorTextEdit->setWordWrap( TQTextEdit::FixedColumnWidth );
2059 hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
2060 }
2061 TQString text = hackConspiratorTextEdit->text();
2062 TQCString textbody;
2063
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();
2070 } else {
2071 text = codec->toUnicode( text.latin1(), text.length() );
2072 textbody = codec->fromUnicode( text );
2073 }
2074 if (textbody.isNull()) textbody = "";
2075
2076 delete hackConspiratorTextEdit;
2077 return textbody;
2078}
2079
2080//-----------------------------------------------------------------------------
2081TQByteArray MessageComposer::breakLinesAndApplyCodec()
2082{
2083 TQString text;
2084 TQCString cText;
2085
2086 if( mDisableBreaking || mIsRichText || !GlobalSettings::self()->wordWrap() )
2087 text = mComposeWin->mEditor->text();
2088 else
2089 text = mComposeWin->mEditor->brokenText();
2090 text.truncate( text.length() ); // to ensure text.size()==text.length()+1
2091
2092 TQString newText;
2093 const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2094
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 );
2102 } else {
2103 cText = codec->fromUnicode( text );
2104 newText = codec->toUnicode( cText );
2105 }
2106 if (cText.isNull()) cText = "";
2107
2108 if( !text.isEmpty() && (newText != text) ) {
2109 TQString oldText = mComposeWin->mEditor->text();
2110 mComposeWin->mEditor->setText( newText );
2111 KCursorSaver idle( KBusyPtr::idle() );
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 );
2117 if( !anyway ) {
2118 mComposeWin->mEditor->setText(oldText);
2119 return TQByteArray();
2120 }
2121 }
2122
2123 // From RFC 3156:
2124 // Note: The accepted OpenPGP convention is for signed data to end
2125 // with a <CR><LF> sequence. Note that the <CR><LF> sequence
2126 // immediately preceding a MIME boundary delimiter line is considered
2127 // to be part of the delimiter in [3], 5.1. Thus, it is not part of
2128 // the signed data preceding the delimiter line. An implementation
2129 // which elects to adhere to the OpenPGP convention has to make sure
2130 // it inserts a <CR><LF> pair on the last line of the data to be
2131 // signed and transmitted (signed message and transmitted message
2132 // MUST be identical).
2133 // So make sure that the body ends with a <LF>.
2134 if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
2135 kdDebug(5006) << "Added an <LF> on the last line" << endl;
2136 cText += "\n";
2137 }
2139}
2140
2141
2142//-----------------------------------------------------------------------------
2143void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessageFormat format ) {
2144
2145 assert( cText.isEmpty() || cText[cText.size()-1] != '\0' ); // I was called with a TQCString !?
2146 mSignature = TQByteArray();
2147
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.") );
2155 return;
2156 }
2157
2158 // TODO: ASync call? Likely, yes :-)
2159 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2160 assert( cpf );
2161 const Kleo::CryptoBackend::Protocol * proto
2162 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2163 assert( proto );
2164
2165 std::unique_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
2166 textMode( format ) ) );
2167
2168 if ( !job ) {
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.") );
2174 return;
2175 }
2176
2177 TQByteArray signature;
2178 const GpgME::SigningResult res =
2179 job->exec( signingKeys, cText, signingMode( format ), signature );
2180 {
2181 std::stringstream ss;
2182 ss << res;
2183 kdDebug(5006) << ss.str().c_str() << endl;
2184 }
2185 if ( res.error().isCanceled() ) {
2186 kdDebug() << "signing was canceled by user" << endl;
2187 return;
2188 }
2189 if ( res.error() ) {
2190 kdDebug() << "signing failed: " << res.error().asString() << endl;
2191 job->showErrorDialog( mComposeWin );
2192 return;
2193 }
2194
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") );
2198
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 "
2204 "is running." ) );
2205 }
2206}
2207
2208//-----------------------------------------------------------------------------
2209Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody,
2210 const TQByteArray& cText,
2211 const std::vector<GpgME::Key> & encryptionKeys,
2212 Kleo::CryptoMessageFormat format )
2213{
2214 // TODO: ASync call? Likely, yes :-)
2215 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2216 assert( cpf );
2217 const Kleo::CryptoBackend::Protocol * proto
2218 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2219 assert( proto ); // hmmmm....?
2220
2221 std::unique_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
2222 textMode( format ) ) );
2223 if ( !job ) {
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;
2230 }
2231
2232 const GpgME::EncryptionResult res =
2233 job->exec( encryptionKeys, cText, true /* we do ownertrust ourselves */, encryptedBody );
2234 {
2235 std::stringstream ss;
2236 ss << res;
2237 kdDebug(5006) << ss.str().c_str() << endl;
2238 }
2239 if ( res.error().isCanceled() ) {
2240 kdDebug() << "encryption was canceled by user" << endl;
2241 return Kpgp::Canceled;
2242 }
2243 if ( res.error() ) {
2244 kdDebug() << "encryption failed: " << res.error().asString() << endl;
2245 job->showErrorDialog( mComposeWin );
2246 return Kpgp::Failure;
2247 }
2248
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") );
2252
2253 return Kpgp::Ok;
2254}
2255
2256Kpgp::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 )
2261{
2262 // TODO: ASync call? Likely, yes :-)
2263 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2264 assert( cpf );
2265 const Kleo::CryptoBackend::Protocol * proto
2266 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2267 assert( proto ); // hmmmm....?
2268
2269 std::unique_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
2270 textMode( format ) ) );
2271 if ( !job ) {
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;
2278 }
2279
2280 const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
2281 job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
2282 {
2283 std::stringstream ss;
2284 ss << res.first << '\n' << res.second;
2285 kdDebug(5006) << ss.str().c_str() << endl;
2286 }
2287 if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
2288 kdDebug() << "encrypt/sign was canceled by user" << endl;
2289 return Kpgp::Canceled;
2290 }
2291 if ( res.first.error() || res.second.error() ) {
2292 if ( res.first.error() )
2293 kdDebug() << "signing failed: " << res.first.error().asString() << endl;
2294 else
2295 kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
2296 job->showErrorDialog( mComposeWin );
2297 return Kpgp::Failure;
2298 }
2299
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") );
2303
2304 return Kpgp::Ok;
2305}
2306
2307
2308#include "messagecomposer.moc"
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
Definition: kcursorsaver.h:14
Mail folder.
Definition: kmfolder.h:69
TQString idString() const
Returns a string that can be used to identify this folder.
Definition: kmfolder.cpp:705
This is a Mime Message.
Definition: kmmessage.h:68
void setBody(const TQCString &aStr)
Set the message body.
Definition: kmmessage.cpp:2774
static void bodyPart(DwBodyPart *aDwBodyPart, KMMessagePart *aPart, bool withBody=true)
Fill the KMMessagePart structure for a given DwBodyPart.
Definition: kmmessage.cpp:3108
void setAutomaticFields(bool isMultipart=false)
Set fields that are either automatically set (Message-id) or that do not change from one message to a...
Definition: kmmessage.cpp:1777
void setCharset(const TQCString &charset, DwEntity *entity=0)
Sets the charset of the message or a subpart of the message.
Definition: kmmessage.cpp:4114
void setUnencryptedMsg(KMMessage *unencrypted)
Specifies an unencrypted copy of this message to be stored in a separate member variable to allow sav...
Definition: kmmessage.cpp:265
void removeHeaderField(const TQCString &name)
Remove header field with given name.
Definition: kmmessage.cpp:2317
void addDwBodyPart(DwBodyPart *aDwPart)
Append a DwBodyPart to the message.
Definition: kmmessage.cpp:3348
static const TQStringList & preferredCharsets()
Get a list of preferred message charsets.
Definition: kmmessage.cpp:4092
TQCString asString() const
Return the entire message contents as a string.
Definition: kmmessage.cpp:314
void addBodyPart(const KMMessagePart *aPart)
Append a body part to the message.
Definition: kmmessage.cpp:3356
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2289
DwHeaders & headers() const
get the DwHeaders (make sure to call setNeedsAssembly() function after directly modyfying internal da...
Definition: kmmessage.cpp:2546
TQString headerAsString() const
Return header as string.
Definition: kmmessage.cpp:378
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.
Definition: kmmessage.cpp:2339
void deleteBodyParts()
Delete all body parts.
Definition: kmmessage.cpp:3174
DwBodyPart * createDWBodyPart(const KMMessagePart *aPart)
Compose a DwBodyPart (needed for adding a part to the message).
Definition: kmmessage.cpp:3218
A class to resolve signing/encryption keys w.r.t.
Definition: keyresolver.h:127
TQByteArray byteArrayFromTQCStringNoDetach(TQCString &cstr)
Creates a TQByteArray from a TQCString without detaching (duplicating the data).
Definition: util.h:124
void append(TQByteArray &that, const TQByteArray &str)
Append a bytearray to a bytearray.
Definition: util.cpp:144
TQByteArray ByteArray(const DwString &str)
Construct a TQByteArray from a DwString.
Definition: util.cpp:122
TQCString lf2crlf(const TQCString &src)
Convert "\n" line endings to "\r\n".
Definition: util.cpp:74
TQCString CString(const DwString &str)
Construct a TQCString from a DwString.
Definition: util.cpp:113