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 "klistboxdialog.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 <kmessagebox.h>
73 #include <klocale.h>
74 #include <kinputdialog.h>
75 #include <kdebug.h>
76 #include <kaction.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 
95 static inline bool warnSendUnsigned() {
96  KConfigGroup group( KMKernel::config(), "Composer" );
97  return group.readBoolEntry( "crypto-warning-unsigned", false );
98 }
99 static inline bool warnSendUnencrypted() {
100  KConfigGroup group( KMKernel::config(), "Composer" );
101  return group.readBoolEntry( "crypto-warning-unencrypted", false );
102 }
103 static inline bool saveMessagesEncrypted() {
104  KConfigGroup group( KMKernel::config(), "Composer" );
105  return group.readBoolEntry( "crypto-store-encrypted", true );
106 }
107 static inline bool encryptToSelf() {
108  // return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
109  KConfigGroup group( KMKernel::config(), "Composer" );
110  return group.readBoolEntry( "crypto-encrypt-to-self", true );
111 }
112 static inline bool showKeyApprovalDialog() {
113  KConfigGroup group( KMKernel::config(), "Composer" );
114  return group.readBoolEntry( "crypto-show-keys-for-approval", true );
115 }
116 
117 static inline int encryptKeyNearExpiryWarningThresholdInDays() {
118  const KConfigGroup 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 
125 static inline int signingKeyNearExpiryWarningThresholdInDays() {
126  const KConfigGroup 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 
133 static inline int encryptRootCertNearExpiryWarningThresholdInDays() {
134  const KConfigGroup 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 
141 static inline int signingRootCertNearExpiryWarningThresholdInDays() {
142  const KConfigGroup 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 
149 static inline int encryptChainCertNearExpiryWarningThresholdInDays() {
150  const KConfigGroup 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 
157 static inline int signingChainCertNearExpiryWarningThresholdInDays() {
158  const KConfigGroup 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 
222 static TQString mErrorProcessingStructuringInfo =
223 i18n("<qt><p>Structuring information returned by the Crypto plug-in "
224  "could not be processed correctly; the plug-in might be damaged.</p>"
225  "<p>Please contact your system administrator.</p></qt>");
226 static TQString mErrorNoCryptPlugAndNoBuildIn =
227 i18n("<p>No active Crypto Plug-In was found and the built-in OpenPGP code "
228  "did not run successfully.</p>"
229  "<p>You can do two things to change this:</p>"
230  "<ul><li><em>either</em> activate a Plug-In using the "
231  "Settings->Configure KMail->Plug-In dialog.</li>"
232  "<li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
233  "Identity->Advanced tab.</li></ul>");
234 
235 
236 class MessageComposerJob {
237 public:
238  MessageComposerJob( MessageComposer* composer ) : mComposer( composer ) {}
239  virtual ~MessageComposerJob() {}
240 
241  virtual void execute() = 0;
242 
243 protected:
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 
260 class ChiasmusBodyPartEncryptJob : public MessageComposerJob {
261 public:
262  ChiasmusBodyPartEncryptJob( MessageComposer * composer )
263  : MessageComposerJob( composer ) {}
264 
265  void execute() {
266  chiasmusEncryptAllAttachments();
267  }
268 };
269 
270 class AdjustCryptFlagsJob : public MessageComposerJob {
271 public:
272  AdjustCryptFlagsJob( MessageComposer* composer )
273  : MessageComposerJob( composer ) {}
274 
275  void execute() {
276  adjustCryptFlags();
277  }
278 };
279 
280 class ComposeMessageJob : public MessageComposerJob {
281 public:
282  ComposeMessageJob( MessageComposer* composer )
283  : MessageComposerJob( composer ) {}
284 
285  void execute() {
286  composeMessage();
287  }
288 };
289 
290 MessageComposer::MessageComposer( KMComposeWin* win, const char* name )
291  : TQObject( win, name ), mComposeWin( win ), mCurrentJob( 0 ),
292  mReferenceMessage( 0 ), mKeyResolver( 0 ),
293  mUseOpportunisticEncryption( false ),
294  mSignBody( false ), mEncryptBody( false ),
295  mSigningRequested( false ), mEncryptionRequested( false ),
296  mDoSign( false ), mDoEncrypt( false ),
297  mAllowedCryptoMessageFormats( 0 ),
298  mDisableCrypto( false ),
299  mDisableBreaking( false ),
300  mDebugComposerCrypto( false ),
301  mAutoCharset( true ),
302  mIsRichText( false ),
303  mIdentityUid( 0 ), mRc( true ),
304  mHoldJobs( false ),
305  mNewBodyPart( 0 ),
306  mEarlyAddAttachments( false ), mAllAttachmentsAreInBody( false ),
307  mPreviousBoundaryLevel( 0 ),
308  mEncryptWithChiasmus( false ),
309  mPerformingSignOperation( false )
310 {
311 }
312 
313 MessageComposer::~MessageComposer()
314 {
315  delete mKeyResolver; mKeyResolver = 0;
316  delete mNewBodyPart; mNewBodyPart = 0;
317 }
318 
319 void 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 
355 void 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, TQT_SLOT( slotDoNextJob() ) );
377 }
378 
379 void 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 
388 void 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 
411 void 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 
534 static 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 
551 bool MessageComposer::encryptWithChiasmus( const Kleo::CryptoBackend::Protocol * chiasmus,
552  const TQByteArray& body,
553  TQByteArray& resultData )
554 {
555  std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-encrypt", TQMap<TQString,TQVariant>() ) );
556  if ( !job.get() ) {
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 
588 void 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 
626 void 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 
739 bool 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 
851 bool 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 
977 void 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 
983 void 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 
990 void 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}
1007 static 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 }
1016 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
1017  return makeMultiMime( f, true );
1018 }
1019 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
1020  return makeMultiMime( f, false );
1021 }
1022 
1023 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool /*signing*/ ) {
1024  return f != Kleo::InlineOpenPGPFormat;
1025 }
1026 
1027 static 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 
1065 static 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 
1079 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
1080  return makeMultiPartSigned( f );
1081 }
1082 
1083 static 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 
1098 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
1099  if ( !signing && f == Kleo::OpenPGPMIMEFormat )
1100  return "inline; filename=\"msg.asc\"";
1101  if ( signing && f == Kleo::SMIMEFormat )
1102  return "attachment; filename=\"smime.p7s\"";
1103  return 0;
1104 }
1105 
1106 static 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 
1118 static inline bool armor( Kleo::CryptoMessageFormat f ) {
1119  return !binaryHint( f );
1120 }
1121 
1122 static inline bool textMode( Kleo::CryptoMessageFormat f ) {
1123  return f == Kleo::InlineOpenPGPFormat;
1124 }
1125 
1126 static 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 
1143 class EncryptMessageJob : public MessageComposerJob {
1144 public:
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 
1170 private:
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 
1181 class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
1182 public:
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 
1193 void 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  mOldBodyPart.setCharset(mCharset);
1255  addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1256  mMessageList.push_back( msg );
1257  if ( it == splitInfos.begin() ) {
1258  if ( doEncrypt && !saveMessagesEncrypted() ) {
1259  mOldBodyPart.setBodyEncodedBinary( bodyData );
1260  KMMessage* msgUnenc = new KMMessage( theMessage );
1261  addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1262  msg->setUnencryptedMsg( msgUnenc );
1263  }
1264  }
1265  } // end for
1266 }
1267 
1268 // very much inspired by composeInlineOpenPGPMessage
1269 void MessageComposer::composeChiasmusMessage( KMMessage& theMessage, Kleo::CryptoMessageFormat format )
1270 {
1271  assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
1272  const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
1273  assert( cpf );
1274  const Kleo::CryptoBackend::Protocol * chiasmus
1275  = cpf->protocol( "Chiasmus" );
1276  assert( chiasmus ); // kmcomposewin code should have made sure
1277 
1278  // preprocess the body text
1279  const TQByteArray bodyData = mText;
1280  if (bodyData.isNull()) {
1281  mRc = false;
1282  return;
1283  }
1284 
1285  mNewBodyPart = 0; // unused
1286  mEarlyAddAttachments = false;
1287  mAllAttachmentsAreInBody = false;
1288 
1289  // set the main headers
1290  theMessage.deleteBodyParts();
1291  TQString oldContentType = theMessage.headerField( "Content-Type" );
1292  theMessage.removeHeaderField("Content-Type");
1293  theMessage.removeHeaderField("Content-Transfer-Encoding");
1294 
1295  // This reads strange, but we know that AdjustCryptFlagsJob created a single splitinfo,
1296  // under the given "format" (usually openpgp/mime; doesn't matter)
1297  const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1298  = mKeyResolver->encryptionItems( format );
1299  assert( splitInfos.size() == 1 );
1300  for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
1301  {
1302  const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
1303  KMMessage* msg = new KMMessage( theMessage );
1304  TQByteArray encryptedBody;
1305 
1306  if ( !encryptWithChiasmus( chiasmus, bodyData, encryptedBody ) ) {
1307  mRc = false;
1308  return;
1309  }
1310  assert( !encryptedBody.isNull() );
1311  // This leaves CTE==7-bit, no good
1312  //mOldBodyPart.setBodyEncodedBinary( encryptedBody );
1313 
1314  bool doSign = false;
1315  TQValueList<int> allowedCTEs;
1316  mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
1317  !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1318  doSign );
1319 
1320 
1321  mOldBodyPart.setContentDisposition( "inline" );
1322  // Used in case of no attachments
1323  mOldBodyPart.setOriginalContentTypeStr( "application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
1324  // Used in case of attachments
1325  mOldBodyPart.setTypeStr( "application" );
1326  mOldBodyPart.setSubtypeStr( "vnd.de.bund.bsi.chiasmus-text" );
1327  mOldBodyPart.setAdditionalCTypeParamStr( TQCString( "chiasmus-charset=" + mCharset ) );
1328  addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1329  mMessageList.push_back( msg );
1330 
1331  if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
1332  mOldBodyPart.setBodyEncodedBinary( bodyData );
1333  KMMessage* msgUnenc = new KMMessage( theMessage );
1334  addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
1335  msg->setUnencryptedMsg( msgUnenc );
1336  }
1337  }
1338 }
1339 
1340 void MessageComposer::composeMessage( KMMessage& theMessage,
1341  bool doSign, bool doEncrypt,
1342  Kleo::CryptoMessageFormat format )
1343 {
1344 #ifdef DEBUG
1345  kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl;
1346 #endif
1347  if ( format == Kleo::InlineOpenPGPFormat ) {
1348  composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
1349  return;
1350  }
1351 
1352  if ( mEncryptWithChiasmus )
1353  {
1354  composeChiasmusMessage( theMessage, format );
1355  return;
1356  }
1357 
1358  // create informative header for those that have no mime-capable
1359  // email client
1360  theMessage.setBody( "This message is in MIME format." );
1361 
1362  // preprocess the body text
1363  TQByteArray bodyData = mText;
1364  if (bodyData.isNull()) {
1365  mRc = false;
1366  return;
1367  }
1368 
1369  // set the main headers
1370  TQString oldContentType = theMessage.headerField( "Content-Type" );
1371  theMessage.deleteBodyParts();
1372  theMessage.removeHeaderField("Content-Type");
1373  theMessage.removeHeaderField("Content-Transfer-Encoding");
1374  theMessage.setAutomaticFields(true); // == multipart/mixed
1375 
1376  // this is our *final* body part
1377  mNewBodyPart = new KMMessagePart;
1378 
1379  // this is the boundary depth of the surrounding MIME part
1380  mPreviousBoundaryLevel = 0;
1381 
1382  // whether the body must be signed/encrypted
1383  const bool doEncryptBody = doEncrypt && mEncryptBody;
1384  const bool doSignBody = doSign && mSignBody;
1385 
1386  // create temporary bodyPart for editor text
1387  // (and for all attachments, if mail is to be signed and/or encrypted)
1388  mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
1389 
1390  mAllAttachmentsAreInBody = mEarlyAddAttachments;
1391 
1392  // test whether there ARE attachments that can be included into the body
1393  if( mEarlyAddAttachments ) {
1394  bool someOk = false;
1395  for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1396  if ( it->encrypt == doEncryptBody && it->sign == doSignBody )
1397  someOk = true;
1398  else
1399  mAllAttachmentsAreInBody = false;
1400  }
1401  if( !mAllAttachmentsAreInBody && !someOk )
1402  mEarlyAddAttachments = false;
1403  }
1404 
1405  kdDebug(5006) << "mEarlyAddAttachments=" << mEarlyAddAttachments << " mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
1406 
1407  // if an html message is to be generated, make a text/plain and text/html part
1408  mMultipartMixedBoundary = "";
1409  if ( mEarlyAddAttachments ) {
1410  mOldBodyPart.setTypeStr( "multipart" );
1411  mOldBodyPart.setSubtypeStr( "mixed" );
1412  // calculate a boundary string
1413  DwMediaType tmpCT;
1414  tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1415  mMultipartMixedBoundary = tmpCT.Boundary().c_str();
1416  }
1417  else if ( mIsRichText ) {
1418  mOldBodyPart.setTypeStr( "multipart" );
1419  mOldBodyPart.setSubtypeStr( "alternative" );
1420  }
1421  else
1422  mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1423 
1424  mOldBodyPart.setContentDisposition( "inline" );
1425 
1426  if ( mIsRichText ) { // create a multipart body
1427  // calculate a boundary string
1428  TQCString boundaryCStr; // storing boundary string data
1429  TQCString newbody;
1430  DwMediaType tmpCT;
1431  tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
1432  boundaryCStr = KMail::Util::CString( tmpCT.Boundary() );
1433  TQValueList<int> allowedCTEs;
1434 
1435  KMMessagePart textBodyPart;
1436  textBodyPart.setTypeStr("text");
1437  textBodyPart.setSubtypeStr("plain");
1438 
1439  TQCString textbody = plainTextFromMarkup( mText /* converted to TQString */ );
1440 
1441  // the signed body must not be 8bit encoded
1442  textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
1443  !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1444  doSign );
1445  textBodyPart.setCharset( mCharset );
1446  textBodyPart.setBodyEncoded( textbody );
1447  DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart );
1448  textDwPart->Assemble();
1449  newbody += "--";
1450  newbody += boundaryCStr;
1451  newbody += "\n";
1452  newbody += textDwPart->AsString().c_str();
1453  delete textDwPart;
1454  textDwPart = 0;
1455 
1456  KMMessagePart htmlBodyPart;
1457  htmlBodyPart.setTypeStr("text");
1458  htmlBodyPart.setSubtypeStr("html");
1459  TQByteArray htmlbody = mText;
1460  // the signed body must not be 8bit encoded
1461  htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
1462  !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1463  doSign );
1464  htmlBodyPart.setCharset( mCharset );
1465  htmlBodyPart.setBodyEncodedBinary( htmlbody );
1466  DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart );
1467  htmlDwPart->Assemble();
1468  newbody += "\n--";
1469  newbody += boundaryCStr;
1470  newbody += "\n";
1471  newbody += htmlDwPart->AsString().c_str();
1472  delete htmlDwPart;
1473  htmlDwPart = 0;
1474 
1475  newbody += "--";
1476  newbody += boundaryCStr;
1477  newbody += "--\n";
1478  bodyData = KMail::Util::byteArrayFromTQCStringNoDetach( newbody );
1479  mOldBodyPart.setBodyEncodedBinary( bodyData );
1480 
1481  mSaveBoundary = tmpCT.Boundary();
1482  }
1483 
1484  // Prepare attachments that will be signed/encrypted
1485  for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1486  // signed/encrypted body parts must be either QP or base64 encoded
1487  // Why not 7 bit? Because the LF->CRLF canonicalization would render
1488  // e.g. 7 bit encoded shell scripts unusable because of the CRs.
1489  //
1490  // (marc) this is a workaround for the KMail bug that doesn't
1491  // respect the CRLF->LF de-canonicalisation. We should
1492  // eventually get rid of this:
1493  if( it->sign || it->encrypt ) {
1494  TQCString cte = it->part->cteStr().lower();
1495  if( ( "8bit" == cte && it->part->type() != DwMime::kTypeMessage )
1496  || ( ( it->part->type() == DwMime::kTypeText )
1497  && ( "7bit" == cte ) ) ) {
1498  const TQByteArray body = it->part->bodyDecodedBinary();
1499  TQValueList<int> dummy;
1500  it->part->setBodyAndGuessCte(body, dummy, false, it->sign);
1501  kdDebug(5006) << "Changed encoding of message part from "
1502  << cte << " to " << it->part->cteStr() << endl;
1503  }
1504  }
1505  }
1506 
1507  if( mEarlyAddAttachments ) {
1508  // add the normal body text
1509  KMMessagePart innerBodyPart;
1510  if ( mIsRichText ) {
1511  innerBodyPart.setTypeStr( "multipart");//text" );
1512  innerBodyPart.setSubtypeStr("alternative");//html");
1513  }
1514  else {
1515  innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
1516  }
1517  innerBodyPart.setContentDisposition( "inline" );
1518  TQValueList<int> allowedCTEs;
1519  // the signed body must not be 8bit encoded
1520  innerBodyPart.setBodyAndGuessCte( bodyData, allowedCTEs,
1521  !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1522  doSign );
1523  if ( !mIsRichText )
1524  innerBodyPart.setCharset( mCharset );
1525  innerBodyPart.setBodyEncodedBinary( bodyData ); // do we need this, since setBodyAndGuessCte does this already?
1526  DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
1527  innerDwPart->Assemble();
1528  TQByteArray tmpbody = KMail::Util::ByteArray( innerDwPart->AsString() );
1529  if ( mIsRichText ) { // and add our mp/a boundary
1530  int boundPos = tmpbody.find( '\n' );
1531  if( -1 < boundPos ) {
1532  TQCString bStr( ";\n boundary=\"" );
1533  bStr += mSaveBoundary.c_str();
1534  bStr += "\"";
1535  bodyData = tmpbody;
1536  KMail::Util::insert( bodyData, boundPos, bStr );
1537  KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
1538  }
1539  }
1540  else {
1541  bodyData = tmpbody;
1542  KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
1543  }
1544  delete innerDwPart;
1545  innerDwPart = 0;
1546  // add all matching Attachments
1547  // NOTE: This code will be changed when KMime is complete.
1548  for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1549  if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
1550  innerDwPart = theMessage.createDWBodyPart( it->part );
1551  innerDwPart->Assemble();
1552  KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "\n" ) );
1553  KMail::Util::append( bodyData, innerDwPart->AsString().c_str() );
1554  delete innerDwPart;
1555  innerDwPart = 0;
1556  }
1557  }
1558  KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "--\n" ) );
1559  } else { // !earlyAddAttachments
1560  TQValueList<int> allowedCTEs;
1561  // the signed body must not be 8bit encoded
1562  mOldBodyPart.setBodyAndGuessCte(bodyData, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
1563  doSign);
1564  if ( !mIsRichText )
1565  mOldBodyPart.setCharset(mCharset);
1566  }
1567  // create S/MIME body part for signing and/or encrypting
1568  mOldBodyPart.setBodyEncodedBinary( bodyData );
1569 
1570  if( doSignBody || doEncryptBody ) {
1571  // get string representation of body part (including the attachments)
1572 
1573  DwBodyPart* dwPart;
1574  if ( mIsRichText && !mEarlyAddAttachments ) {
1575  // if we are using richtext and not already have a mp/a body
1576  // make the body a mp/a body
1577  dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
1578  DwHeaders& headers = dwPart->Headers();
1579  DwMediaType& ct = headers.ContentType();
1580  ct.SetBoundary(mSaveBoundary);
1581  dwPart->Assemble();
1582  }
1583  else {
1584  dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
1585  dwPart->Assemble();
1586  }
1587  mEncodedBody = KMail::Util::ByteArray( dwPart->AsString() );
1588  delete dwPart;
1589  dwPart = 0;
1590 
1591  // manually add a boundary definition to the Content-Type header
1592  if( !mMultipartMixedBoundary.isEmpty() ) {
1593  int boundPos = mEncodedBody.find( '\n' );
1594  if( -1 < boundPos ) {
1595  // insert new "boundary" parameter
1596  TQCString bStr( ";\n boundary=\"" );
1597  bStr += mMultipartMixedBoundary;
1598  bStr += "\"";
1599  KMail::Util::insert( mEncodedBody, boundPos, bStr.data() );
1600  }
1601  }
1602 
1603  // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1604  // according to RfC 2633, 3.1.1 Canonicalization
1605  //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1606  mEncodedBody = KMail::Util::lf2crlf( mEncodedBody );
1607  }
1608 
1609  if ( doSignBody ) {
1610  mPerformingSignOperation = true; // this lets the KMComposeWin know if it is safe to close the window.
1611  pgpSignedMsg( mEncodedBody, format );
1612  mPerformingSignOperation = false;
1613 
1614  if ( mSignature.isEmpty() ) {
1615  kdDebug() << "signature was empty" << endl;
1616  mRc = false;
1617  return;
1618  }
1619  mRc = processStructuringInfo( TQString(),
1620  mOldBodyPart.contentDescription(),
1621  mOldBodyPart.typeStr(),
1622  mOldBodyPart.subtypeStr(),
1623  mOldBodyPart.contentDisposition(),
1624  mOldBodyPart.contentTransferEncodingStr(),
1625  mEncodedBody, "signature",
1626  mSignature,
1627  *mNewBodyPart, true, format );
1628  if ( mRc ) {
1629  if ( !makeMultiPartSigned( format ) ) {
1630  mNewBodyPart->setCharset( mCharset );
1631  }
1632  } else
1633  KMessageBox::sorry( mComposeWin,
1634  mErrorProcessingStructuringInfo );
1635  }
1636 
1637  if ( !mRc )
1638  return;
1639 
1640  continueComposeMessage( theMessage, doSign, doEncrypt, format );
1641 }
1642 
1643 // Do the encryption stuff
1644 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
1645  bool doSign, bool doEncrypt,
1646  Kleo::CryptoMessageFormat format )
1647 {
1648 
1649  const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
1650  = mKeyResolver->encryptionItems( format );
1651  kdWarning( splitInfos.empty() )
1652  << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
1653  << Kleo::cryptoMessageFormatToString( format ) << endl;
1654 
1655  if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
1656  mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
1657  mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
1658  Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
1659  false, mEncodedBody,
1660  mPreviousBoundaryLevel,
1661  /*mOldBodyPart,*/ mNewBodyPart,
1662  format, this ) );
1663  }
1664 
1665  for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
1666  mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
1667  doEncrypt, mEncodedBody,
1668  mPreviousBoundaryLevel,
1669  /*mOldBodyPart,*/ mNewBodyPart,
1670  format, this ) );
1671 }
1672 
1673 void MessageComposer::encryptMessage( KMMessage* msg,
1674  const Kleo::KeyResolver::SplitInfo & splitInfo,
1675  bool doSign, bool doEncrypt,
1676  KMMessagePart newBodyPart,
1677  Kleo::CryptoMessageFormat format )
1678 {
1679  if ( doEncrypt && splitInfo.keys.empty() ) {
1680  // the user wants to send the message unencrypted
1681  //mComposeWin->setEncryption( false, false );
1682  //FIXME why is this talkback needed? Till
1683  doEncrypt = false;
1684  }
1685 
1686  const bool doEncryptBody = doEncrypt && mEncryptBody;
1687  const bool doSignBody = doSign && mSignBody;
1688 
1689  if ( doEncryptBody ) {
1690  TQByteArray innerContent;
1691  if ( doSignBody ) {
1692  // extract signed body from newBodyPart
1693  DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
1694  dwPart->Assemble();
1695  innerContent = KMail::Util::ByteArray( dwPart->AsString() );
1696  delete dwPart;
1697  dwPart = 0;
1698  } else {
1699  innerContent = mEncodedBody;
1700  }
1701 
1702  // now do the encrypting:
1703  // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1704  // according to RfC 2633, 3.1.1 Canonicalization
1705  //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1706  innerContent = KMail::Util::lf2crlf( innerContent );
1707  //kdDebug(5006) << " done." << endl;
1708 
1709  TQByteArray encryptedBody;
1710  Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
1711  splitInfo.keys, format );
1712  if ( result != Kpgp::Ok ) {
1713  mRc = false;
1714  return;
1715  }
1716  mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
1717  newBodyPart.contentDescription(),
1718  newBodyPart.typeStr(),
1719  newBodyPart.subtypeStr(),
1720  newBodyPart.contentDisposition(),
1721  newBodyPart.contentTransferEncodingStr(),
1722  innerContent,
1723  "encrypted data",
1724  encryptedBody,
1725  newBodyPart, false, format );
1726  if ( !mRc )
1727  KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
1728  }
1729 
1730  // process the attachments that are not included into the body
1731  if( mRc ) {
1732  const bool useNewBodyPart = doSignBody || doEncryptBody;
1733  addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
1734  useNewBodyPart ? newBodyPart : mOldBodyPart, format );
1735  }
1736 }
1737 
1738 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
1739  const Kleo::KeyResolver::SplitInfo & splitInfo,
1740  bool doSign, bool doEncrypt,
1741  const KMMessagePart& ourFineBodyPart,
1742  Kleo::CryptoMessageFormat format )
1743 {
1744  const bool doEncryptBody = doEncrypt && mEncryptBody;
1745  const bool doSignBody = doSign && mSignBody;
1746 
1747  if( !mAttachments.empty()
1748  && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
1749  // set the content type header
1750  msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
1751  msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
1752  msg->headers().ContentType().CreateBoundary( 0 );
1753  kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
1754 
1755  // add our Body Part
1756  DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
1757  DwHeaders& headers = tmpDwPart->Headers();
1758  DwMediaType& ct = headers.ContentType();
1759  if ( !mSaveBoundary.empty() )
1760  ct.SetBoundary(mSaveBoundary);
1761  tmpDwPart->Assemble();
1762 
1763  //KMMessagePart newPart;
1764  //newPart.setBody(tmpDwPart->AsString().c_str());
1765  msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain
1766 
1767  // add Attachments
1768  // create additional bodyparts for the attachments (if any)
1769  KMMessagePart newAttachPart;
1770  for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
1771 
1772  const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
1773 
1774  if ( !cryptFlagsDifferent && mEarlyAddAttachments )
1775  continue;
1776 
1777  const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
1778  const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
1779 
1780  if ( !encryptThisNow && !signThisNow ) {
1781  msg->addBodyPart( it->part );
1782  // Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
1783  (void)msg->asDwMessage();
1784  continue;
1785  }
1786 
1787  KMMessagePart& rEncryptMessagePart( *it->part );
1788 
1789  DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
1790  innerDwPart->Assemble();
1791  TQByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
1792  delete innerDwPart;
1793  innerDwPart = 0;
1794 
1795  // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
1796  // according to RfC 2633, 3.1.1 Canonicalization
1797  //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
1798  encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
1799 
1800  // sign this attachment
1801  if( signThisNow ) {
1802  pgpSignedMsg( encodedAttachment, format );
1803  mRc = !mSignature.isEmpty();
1804  if( mRc ) {
1805  mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
1806  it->part->contentDescription(),
1807  it->part->typeStr(),
1808  it->part->subtypeStr(),
1809  it->part->contentDisposition(),
1810  it->part->contentTransferEncodingStr(),
1811  encodedAttachment,
1812  "signature",
1813  mSignature,
1814  newAttachPart, true, format );
1815  if( mRc ) {
1816  if( encryptThisNow ) {
1817  rEncryptMessagePart = newAttachPart;
1818  DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
1819  dwPart->Assemble();
1820  encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
1821  delete dwPart;
1822  dwPart = 0;
1823  }
1824  } else
1825  KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1826  } else {
1827  // quit the attachments' loop
1828  break;
1829  }
1830  }
1831  if( encryptThisNow ) {
1832  TQByteArray encryptedBody;
1833  Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
1834  encodedAttachment,
1835  splitInfo.keys,
1836  format );
1837 
1838  if( Kpgp::Ok == result ) {
1839  mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
1840  rEncryptMessagePart.contentDescription(),
1841  rEncryptMessagePart.typeStr(),
1842  rEncryptMessagePart.subtypeStr(),
1843  rEncryptMessagePart.contentDisposition(),
1844  rEncryptMessagePart.contentTransferEncodingStr(),
1845  encodedAttachment,
1846  "encrypted data",
1847  encryptedBody,
1848  newAttachPart, false, format );
1849  if ( !mRc )
1850  KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
1851  } else
1852  mRc = false;
1853  }
1854  msg->addBodyPart( &newAttachPart );
1855  (void)msg->asDwMessage(); // Assemble the message. One gets a completely empty message otherwise :/
1856  }
1857  } else { // no attachments in the final message
1858  if( !ourFineBodyPart.originalContentTypeStr().isNull() ) {
1859  msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
1860  msg->headers().ContentType().Parse();
1861  kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
1862  } else {
1863  TQCString ct = ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr();
1864  if ( ct == "multipart/mixed" )
1865  ct += ";\n\tboundary=\"" + mMultipartMixedBoundary + '"';
1866  else if ( ct == "multipart/alternative" )
1867  ct += ";\n\tboundary=\"" + TQCString(mSaveBoundary.c_str()) + '"';
1868  msg->headers().ContentType().FromString( ct );
1869  msg->headers().ContentType().Parse();
1870  kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
1871  }
1872  if ( !ourFineBodyPart.charset().isEmpty() )
1873  msg->setCharset( ourFineBodyPart.charset() );
1874  msg->setHeaderField( "Content-Transfer-Encoding",
1875  ourFineBodyPart.contentTransferEncodingStr() );
1876  msg->setHeaderField( "Content-Description",
1877  ourFineBodyPart.contentDescription() );
1878  msg->setHeaderField( "Content-Disposition",
1879  ourFineBodyPart.contentDisposition() );
1880 
1881  if ( mDebugComposerCrypto )
1882  kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
1883 
1884  // set body content
1885  msg->setBody( ourFineBodyPart.dwBody() );
1886 
1887  }
1888 
1889  msg->setHeaderField( "X-KMail-Recipients",
1890  splitInfo.recipients.join(", "), KMMessage::Address );
1891 
1892  if ( mDebugComposerCrypto ) {
1893  kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
1894  msg->headers().Assemble();
1895  kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
1896  }
1897 }
1898 
1899 //-----------------------------------------------------------------------------
1900 // This method does not call any crypto ops, so it does not need to be async
1901 bool MessageComposer::processStructuringInfo( const TQString bugURL,
1902  const TQString contentDescClear,
1903  const TQCString contentTypeClear,
1904  const TQCString contentSubtypeClear,
1905  const TQCString contentDispClear,
1906  const TQCString contentTEncClear,
1907  const TQByteArray& clearCStr,
1908  const TQString /*contentDescCiph*/,
1909  const TQByteArray& ciphertext,
1910  KMMessagePart& resultingPart,
1911  bool signing, Kleo::CryptoMessageFormat format )
1912 {
1913  assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' ); // I was called with a TQCString !?
1914  bool bOk = true;
1915 
1916  if ( makeMimeObject( format, signing ) ) {
1917  TQCString mainHeader = "Content-Type: ";
1918  const char * toplevelCT = toplevelContentType( format, signing );
1919  if ( toplevelCT )
1920  mainHeader += toplevelCT;
1921  else {
1922  if( makeMultiMime( format, signing ) )
1923  mainHeader += "text/plain";
1924  else
1925  mainHeader += contentTypeClear + '/' + contentSubtypeClear;
1926  }
1927 
1928  const TQCString boundaryCStr = KMime::multiPartBoundary();
1929  // add "boundary" parameter
1930  if ( makeMultiMime( format, signing ) )
1931  mainHeader.replace( "%boundary", boundaryCStr );
1932 
1933  if ( toplevelCT ) {
1934  if ( const char * str = toplevelContentDisposition( format, signing ) ) {
1935  mainHeader += "\nContent-Disposition: ";
1936  mainHeader += str;
1937  }
1938  if ( !makeMultiMime( format, signing ) &&
1939  binaryHint( format ) )
1940  mainHeader += "\nContent-Transfer-Encoding: base64";
1941  } else {
1942  if( 0 < contentDispClear.length() ) {
1943  mainHeader += "\nContent-Disposition: ";
1944  mainHeader += contentDispClear;
1945  }
1946  if( 0 < contentTEncClear.length() ) {
1947  mainHeader += "\nContent-Transfer-Encoding: ";
1948  mainHeader += contentTEncClear;
1949  }
1950  }
1951 
1952  //kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
1953 
1954  DwString mainDwStr;
1955  mainDwStr = TQCString(mainHeader + "\n\n").data();
1956  DwBodyPart mainDwPa( mainDwStr, 0 );
1957  mainDwPa.Parse();
1958  KMMessage::bodyPart( &mainDwPa, &resultingPart );
1959  if( !makeMultiMime( format, signing ) ) {
1960  if ( signing && includeCleartextWhenSigning( format ) ) {
1961  TQByteArray bodyText( clearCStr );
1962  KMail::Util::append( bodyText, "\n" );
1963  KMail::Util::append( bodyText, ciphertext );
1964  resultingPart.setBodyEncodedBinary( bodyText );
1965  } else {
1966  resultingPart.setBodyEncodedBinary( ciphertext );
1967  }
1968  } else {
1969  // Build the encapsulated MIME parts.
1970  // Build a MIME part holding the version information
1971  // taking the body contents returned in
1972  // structuring.data.bodyTextVersion.
1973  TQCString versCStr, codeCStr;
1974  if ( !signing && format == Kleo::OpenPGPMIMEFormat )
1975  versCStr =
1976  "Content-Type: application/pgp-encrypted\n"
1977  "Content-Disposition: attachment\n"
1978  "\n"
1979  "Version: 1";
1980 
1981  // Build a MIME part holding the code information
1982  // taking the body contents returned in ciphertext.
1983  const char * nestedCT = nestedContentType( format, signing );
1984  assert( nestedCT );
1985  codeCStr = "Content-Type: ";
1986  codeCStr += nestedCT;
1987  codeCStr += '\n';
1988  if ( const char * str = nestedContentDisposition( format, signing ) ) {
1989  codeCStr += "Content-Disposition: ";
1990  codeCStr += str;
1991  codeCStr += '\n';
1992  }
1993  if ( binaryHint( format ) ) {
1994  codeCStr += "Content-Transfer-Encoding: base64\n\n";
1995  codeCStr += KMime::Codec::codecForName( "base64" )->encodeToTQCString( ciphertext );
1996  } else
1997  codeCStr += '\n' + TQCString( ciphertext.data(), ciphertext.size() + 1 );
1998 
1999 
2000  TQByteArray mainStr;
2001  KMail::Util::append( mainStr, "--" );
2002  KMail::Util::append( mainStr, boundaryCStr );
2003  if ( signing && includeCleartextWhenSigning( format ) &&
2004  !clearCStr.isEmpty() ) {
2005  KMail::Util::append( mainStr, "\n" );
2006  // clearCStr is the one that can be very big for large attachments, don't merge with the other lines
2007  KMail::Util::append( mainStr, clearCStr );
2008  KMail::Util::append( mainStr, "\n--" + boundaryCStr );
2009  }
2010  if ( !versCStr.isEmpty() )
2011  KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
2012  if( !codeCStr.isEmpty() )
2013  KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
2014  KMail::Util::append( mainStr, "--\n" );
2015 
2016  //kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
2017  resultingPart.setBodyEncodedBinary( mainStr );
2018  }
2019 
2020  } else { // not making a mime object, build a plain message body.
2021 
2022  resultingPart.setContentDescription( contentDescClear );
2023  resultingPart.setTypeStr( contentTypeClear );
2024  resultingPart.setSubtypeStr( contentSubtypeClear );
2025  resultingPart.setContentDisposition( contentDispClear );
2026  resultingPart.setContentTransferEncodingStr( contentTEncClear );
2027  TQByteArray resultingBody;
2028 
2029  if ( signing && includeCleartextWhenSigning( format ) ) {
2030  if( !clearCStr.isEmpty() )
2031  KMail::Util::append( resultingBody, clearCStr );
2032  }
2033  if ( !ciphertext.isEmpty() )
2034  KMail::Util::append( resultingBody, ciphertext );
2035  else {
2036  // Plugin error!
2037  KMessageBox::sorry( mComposeWin,
2038  i18n( "<qt><p>Error: The backend did not return "
2039  "any encoded data.</p>"
2040  "<p>Please report this bug:<br>%2</p></qt>" )
2041  .arg( bugURL ) );
2042  bOk = false;
2043  }
2044  resultingPart.setBodyEncodedBinary( resultingBody );
2045  }
2046 
2047  return bOk;
2048 }
2049 
2050 //-----------------------------------------------------------------------------
2051 TQCString MessageComposer::plainTextFromMarkup( const TQString& markupText )
2052 {
2053  TQTextEdit *hackConspiratorTextEdit = new TQTextEdit( markupText );
2054  hackConspiratorTextEdit->setTextFormat(TQt::PlainText);
2055  if ( !mDisableBreaking ) {
2056  hackConspiratorTextEdit->setWordWrap( TQTextEdit::FixedColumnWidth );
2057  hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
2058  }
2059  TQString text = hackConspiratorTextEdit->text();
2060  TQCString textbody;
2061 
2062  const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2063  if( mCharset == "us-ascii" ) {
2064  textbody = KMMsgBase::toUsAscii( text );
2065  } else if( codec == 0 ) {
2066  kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
2067  textbody = text.local8Bit();
2068  } else {
2069  text = codec->toUnicode( text.latin1(), text.length() );
2070  textbody = codec->fromUnicode( text );
2071  }
2072  if (textbody.isNull()) textbody = "";
2073 
2074  delete hackConspiratorTextEdit;
2075  return textbody;
2076 }
2077 
2078 //-----------------------------------------------------------------------------
2079 TQByteArray MessageComposer::breakLinesAndApplyCodec()
2080 {
2081  TQString text;
2082  TQCString cText;
2083 
2084  if( mDisableBreaking || mIsRichText || !GlobalSettings::self()->wordWrap() )
2085  text = mComposeWin->mEditor->text();
2086  else
2087  text = mComposeWin->mEditor->brokenText();
2088  text.truncate( text.length() ); // to ensure text.size()==text.length()+1
2089 
2090  TQString newText;
2091  const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
2092 
2093  if( mCharset == "us-ascii" ) {
2094  cText = KMMsgBase::toUsAscii( text );
2095  newText = TQString::fromLatin1( cText );
2096  } else if( codec == 0 ) {
2097  kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
2098  cText = text.local8Bit();
2099  newText = TQString::fromLocal8Bit( cText );
2100  } else {
2101  cText = codec->fromUnicode( text );
2102  newText = codec->toUnicode( cText );
2103  }
2104  if (cText.isNull()) cText = "";
2105 
2106  if( !text.isEmpty() && (newText != text) ) {
2107  TQString oldText = mComposeWin->mEditor->text();
2108  mComposeWin->mEditor->setText( newText );
2109  KCursorSaver idle( KBusyPtr::idle() );
2110  bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
2111  i18n("<qt>Not all characters fit into the chosen"
2112  " encoding.<br><br>Send the message anyway?</qt>"),
2113  i18n("Some Characters Will Be Lost"),
2114  i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
2115  if( !anyway ) {
2116  mComposeWin->mEditor->setText(oldText);
2117  return TQByteArray();
2118  }
2119  }
2120 
2121  // From RFC 3156:
2122  // Note: The accepted OpenPGP convention is for signed data to end
2123  // with a <CR><LF> sequence. Note that the <CR><LF> sequence
2124  // immediately preceding a MIME boundary delimiter line is considered
2125  // to be part of the delimiter in [3], 5.1. Thus, it is not part of
2126  // the signed data preceding the delimiter line. An implementation
2127  // which elects to adhere to the OpenPGP convention has to make sure
2128  // it inserts a <CR><LF> pair on the last line of the data to be
2129  // signed and transmitted (signed message and transmitted message
2130  // MUST be identical).
2131  // So make sure that the body ends with a <LF>.
2132  if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
2133  kdDebug(5006) << "Added an <LF> on the last line" << endl;
2134  cText += "\n";
2135  }
2137 }
2138 
2139 
2140 //-----------------------------------------------------------------------------
2141 void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessageFormat format ) {
2142 
2143  assert( cText.isEmpty() || cText[cText.size()-1] != '\0' ); // I was called with a TQCString !?
2144  mSignature = TQByteArray();
2145 
2146  const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
2147  if ( signingKeys.empty() ) {
2148  KMessageBox::sorry( mComposeWin,
2149  i18n("This message could not be signed, "
2150  "since no valid signing keys have been found; "
2151  "this should actually never happen, "
2152  "please report this bug.") );
2153  return;
2154  }
2155 
2156  // TODO: ASync call? Likely, yes :-)
2157  const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2158  assert( cpf );
2159  const Kleo::CryptoBackend::Protocol * proto
2160  = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2161  assert( proto );
2162 
2163  std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
2164  textMode( format ) ) );
2165 
2166  if ( !job.get() ) {
2167  KMessageBox::sorry( mComposeWin,
2168  i18n("This message could not be signed, "
2169  "since the chosen backend does not seem to support "
2170  "signing; this should actually never happen, "
2171  "please report this bug.") );
2172  return;
2173  }
2174 
2175  TQByteArray signature;
2176  const GpgME::SigningResult res =
2177  job->exec( signingKeys, cText, signingMode( format ), signature );
2178  {
2179  std::stringstream ss;
2180  ss << res;
2181  kdDebug(5006) << ss.str().c_str() << endl;
2182  }
2183  if ( res.error().isCanceled() ) {
2184  kdDebug() << "signing was canceled by user" << endl;
2185  return;
2186  }
2187  if ( res.error() ) {
2188  kdDebug() << "signing failed: " << res.error().asString() << endl;
2189  job->showErrorDialog( mComposeWin );
2190  return;
2191  }
2192 
2193  if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2194  if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2195  Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Signing Operation") );
2196 
2197  mSignature = signature;
2198  if ( mSignature.isEmpty() ) {
2199  KMessageBox::sorry( mComposeWin,
2200  i18n( "The signing operation failed. "
2201  "Please make sure that the gpg-agent program "
2202  "is running." ) );
2203  }
2204 }
2205 
2206 //-----------------------------------------------------------------------------
2207 Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody,
2208  const TQByteArray& cText,
2209  const std::vector<GpgME::Key> & encryptionKeys,
2210  Kleo::CryptoMessageFormat format )
2211 {
2212  // TODO: ASync call? Likely, yes :-)
2213  const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2214  assert( cpf );
2215  const Kleo::CryptoBackend::Protocol * proto
2216  = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2217  assert( proto ); // hmmmm....?
2218 
2219  std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
2220  textMode( format ) ) );
2221  if ( !job.get() ) {
2222  KMessageBox::sorry( mComposeWin,
2223  i18n("This message could not be encrypted, "
2224  "since the chosen backend does not seem to support "
2225  "encryption; this should actually never happen, "
2226  "please report this bug.") );
2227  return Kpgp::Failure;
2228  }
2229 
2230  const GpgME::EncryptionResult res =
2231  job->exec( encryptionKeys, cText, true /* we do ownertrust ourselves */, encryptedBody );
2232  {
2233  std::stringstream ss;
2234  ss << res;
2235  kdDebug(5006) << ss.str().c_str() << endl;
2236  }
2237  if ( res.error().isCanceled() ) {
2238  kdDebug() << "encryption was canceled by user" << endl;
2239  return Kpgp::Canceled;
2240  }
2241  if ( res.error() ) {
2242  kdDebug() << "encryption failed: " << res.error().asString() << endl;
2243  job->showErrorDialog( mComposeWin );
2244  return Kpgp::Failure;
2245  }
2246 
2247  if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2248  if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2249  Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
2250 
2251  return Kpgp::Ok;
2252 }
2253 
2254 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedBody,
2255  const TQByteArray& cText,
2256  const std::vector<GpgME::Key> & signingKeys,
2257  const std::vector<GpgME::Key> & encryptionKeys,
2258  Kleo::CryptoMessageFormat format )
2259 {
2260  // TODO: ASync call? Likely, yes :-)
2261  const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
2262  assert( cpf );
2263  const Kleo::CryptoBackend::Protocol * proto
2264  = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
2265  assert( proto ); // hmmmm....?
2266 
2267  std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
2268  textMode( format ) ) );
2269  if ( !job.get() ) {
2270  KMessageBox::sorry( mComposeWin,
2271  i18n("This message could not be signed and encrypted, "
2272  "since the chosen backend does not seem to support "
2273  "combined signing and encryption; this should actually never happen, "
2274  "please report this bug.") );
2275  return Kpgp::Failure;
2276  }
2277 
2278  const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
2279  job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
2280  {
2281  std::stringstream ss;
2282  ss << res.first << '\n' << res.second;
2283  kdDebug(5006) << ss.str().c_str() << endl;
2284  }
2285  if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
2286  kdDebug() << "encrypt/sign was canceled by user" << endl;
2287  return Kpgp::Canceled;
2288  }
2289  if ( res.first.error() || res.second.error() ) {
2290  if ( res.first.error() )
2291  kdDebug() << "signing failed: " << res.first.error().asString() << endl;
2292  else
2293  kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
2294  job->showErrorDialog( mComposeWin );
2295  return Kpgp::Failure;
2296  }
2297 
2298  if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
2299  if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
2300  Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
2301 
2302  return Kpgp::Ok;
2303 }
2304 
2305 
2306 #include "messagecomposer.moc"