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 
95 static inline bool warnSendUnsigned() {
96  TDEConfigGroup group( KMKernel::config(), "Composer" );
97  return group.readBoolEntry( "crypto-warning-unsigned", false );
98 }
99 static inline bool warnSendUnencrypted() {
100  TDEConfigGroup group( KMKernel::config(), "Composer" );
101  return group.readBoolEntry( "crypto-warning-unencrypted", false );
102 }
103 static inline bool saveMessagesEncrypted() {
104  TDEConfigGroup 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  TDEConfigGroup group( KMKernel::config(), "Composer" );
110  return group.readBoolEntry( "crypto-encrypt-to-self", true );
111 }
112 static inline bool showKeyApprovalDialog() {
113  TDEConfigGroup group( KMKernel::config(), "Composer" );
114  return group.readBoolEntry( "crypto-show-keys-for-approval", true );
115 }
116 
117 static 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 
125 static 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 
133 static 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 
141 static 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 
149 static 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 
157 static 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 
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, TQ_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::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 
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  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
1271 void 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 
1342 void 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
1646 void 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 
1675 void 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 
1740 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
1741  const Kleo::KeyResolver::SplitInfo & splitInfo,
1742  bool doSign, bool doEncrypt,
1743  const KMMessagePart& ourFineBodyPart,
1744  Kleo::CryptoMessageFormat format )
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
1903 bool MessageComposer::processStructuringInfo( const TQString bugURL,
1904  const TQString contentDescClear,
1905  const TQCString contentTypeClear,
1906  const TQCString contentSubtypeClear,
1907  const TQCString contentDispClear,
1908  const TQCString contentTEncClear,
1909  const TQByteArray& clearCStr,
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 //-----------------------------------------------------------------------------
2053 TQCString 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 //-----------------------------------------------------------------------------
2081 TQByteArray 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 //-----------------------------------------------------------------------------
2143 void 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 //-----------------------------------------------------------------------------
2209 Kpgp::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 
2256 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedBody,
2257  const TQByteArray& cText,
2258  const std::vector<GpgME::Key> & signingKeys,
2259  const std::vector<GpgME::Key> & encryptionKeys,
2260  Kleo::CryptoMessageFormat format )
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