kmail

messagecomposer.cpp

00001 
00031 #ifdef HAVE_CONFIG_H
00032 #include <config.h>
00033 #endif
00034 
00035 #include "messagecomposer.h"
00036 #include "kmmsgpart.h"
00037 #define REALLY_WANT_KMCOMPOSEWIN_H
00038 #include "kmcomposewin.h"
00039 #undef REALLY_WANT_KMCOMPOSEWIN_H
00040 #include "klistboxdialog.h"
00041 #include "kcursorsaver.h"
00042 #include "messagesender.h"
00043 #include "kmfolder.h"
00044 #include "kmfoldercombobox.h"
00045 #include "keyresolver.h"
00046 #include "kleo_util.h"
00047 #include "globalsettings.h"
00048 #include "custommimeheader.h"
00049 #include "kmedit.h"
00050 #include "util.h"
00051 
00052 #include <libkpimidentities/identity.h>
00053 #include <libkpimidentities/identitymanager.h>
00054 #include <libemailfunctions/email.h>
00055 
00056 #include <ui/keyselectiondialog.h>
00057 #include <ui/keyapprovaldialog.h>
00058 #include <ui/messagebox.h>
00059 #include <kleo/cryptobackendfactory.h>
00060 #include <kleo/keylistjob.h>
00061 #include <kleo/encryptjob.h>
00062 #include <kleo/signencryptjob.h>
00063 #include <kleo/signjob.h>
00064 #include <kleo/specialjob.h>
00065 
00066 #include <kmime_util.h>
00067 #include <kmime_codecs.h>
00068 #include <kpgpblock.h>
00069 
00070 #include <mimelib/mimepp.h>
00071 
00072 #include <kmessagebox.h>
00073 #include <klocale.h>
00074 #include <kinputdialog.h>
00075 #include <kdebug.h>
00076 #include <kaction.h>
00077 #include <tqfile.h>
00078 #include <tqtextcodec.h>
00079 #include <tqtextedit.h>
00080 #include <tqtimer.h>
00081 
00082 #include <gpgmepp/key.h>
00083 #include <gpgmepp/keylistresult.h>
00084 #include <gpgmepp/encryptionresult.h>
00085 #include <gpgmepp/signingresult.h>
00086 #include <gpgmepp/context.h>
00087 
00088 #include <algorithm>
00089 #include <sstream>
00090 #include <memory>
00091 
00092 // ## keep default values in sync with configuredialog.cpp, Security::CryptoTab::setup()
00093 // This should be ported to a .kcfg one day I suppose (dfaure).
00094 
00095 static inline bool warnSendUnsigned() {
00096     KConfigGroup group( KMKernel::config(), "Composer" );
00097     return group.readBoolEntry( "crypto-warning-unsigned", false );
00098 }
00099 static inline bool warnSendUnencrypted() {
00100     KConfigGroup group( KMKernel::config(), "Composer" );
00101     return group.readBoolEntry( "crypto-warning-unencrypted", false );
00102 }
00103 static inline bool saveMessagesEncrypted() {
00104     KConfigGroup group( KMKernel::config(), "Composer" );
00105     return group.readBoolEntry( "crypto-store-encrypted", true );
00106 }
00107 static inline bool encryptToSelf() {
00108     // return !Kpgp::Module::getKpgp() || Kpgp::Module::getKpgp()->encryptToSelf();
00109     KConfigGroup group( KMKernel::config(), "Composer" );
00110     return group.readBoolEntry( "crypto-encrypt-to-self", true );
00111 }
00112 static inline bool showKeyApprovalDialog() {
00113     KConfigGroup group( KMKernel::config(), "Composer" );
00114     return group.readBoolEntry( "crypto-show-keys-for-approval", true );
00115 }
00116 
00117 static inline int encryptKeyNearExpiryWarningThresholdInDays() {
00118   const KConfigGroup composer( KMKernel::config(), "Composer" );
00119   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00120     return -1;
00121   const int num = composer.readNumEntry( "crypto-warn-encr-key-near-expire-int", 14 );
00122   return kMax( 1, num );
00123 }
00124 
00125 static inline int signingKeyNearExpiryWarningThresholdInDays() {
00126   const KConfigGroup composer( KMKernel::config(), "Composer" );
00127   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00128     return -1;
00129   const int num = composer.readNumEntry( "crypto-warn-sign-key-near-expire-int", 14 );
00130   return kMax( 1, num );
00131 }
00132 
00133 static inline int encryptRootCertNearExpiryWarningThresholdInDays() {
00134   const KConfigGroup composer( KMKernel::config(), "Composer" );
00135   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00136     return -1;
00137   const int num = composer.readNumEntry( "crypto-warn-encr-root-near-expire-int", 14 );
00138   return kMax( 1, num );
00139 }
00140 
00141 static inline int signingRootCertNearExpiryWarningThresholdInDays() {
00142   const KConfigGroup composer( KMKernel::config(), "Composer" );
00143   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00144     return -1;
00145   const int num = composer.readNumEntry( "crypto-warn-sign-root-near-expire-int", 14 );
00146   return kMax( 1, num );
00147 }
00148 
00149 static inline int encryptChainCertNearExpiryWarningThresholdInDays() {
00150   const KConfigGroup composer( KMKernel::config(), "Composer" );
00151   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00152     return -1;
00153   const int num = composer.readNumEntry( "crypto-warn-encr-chaincert-near-expire-int", 14 );
00154   return kMax( 1, num );
00155 }
00156 
00157 static inline int signingChainCertNearExpiryWarningThresholdInDays() {
00158   const KConfigGroup composer( KMKernel::config(), "Composer" );
00159   if ( ! composer.readBoolEntry( "crypto-warn-when-near-expire", true ) )
00160     return -1;
00161   const int num = composer.readNumEntry( "crypto-warn-sign-chaincert-near-expire-int", 14 );
00162   return kMax( 1, num );
00163 }
00164 
00165 /*
00166   Design of this:
00167 
00168   The idea is that the main run of applyChanges here makes two jobs:
00169   the first sets the flags for encryption/signing or not, and the other
00170   starts the encryption process.
00171 
00172   When a job is run, it has already been removed from the job queue. This
00173   means if one of the current jobs needs to add new jobs, it can add them
00174   to the front and that way control when new jobs are added.
00175 
00176   For example, the compose message job will add jobs that will do the
00177   actual encryption and signing.
00178 
00179   There are two types of jobs: synchronous and asynchronous:
00180 
00181   A synchronous job simply implments the execute() method and performs
00182   it's operation there and sets mComposer->mRc to false if the compose
00183   queue should be canceled.
00184 
00185   An asynchronous job only sets up and starts it's operation. Before
00186   returning, it connects to the result signals of the operation
00187   (e.g. Kleo::Job's result(...) signal) and sets mComposer->mHoldJobs
00188   to true. This makes the scheduler return to the event loop. The job
00189   is now responsible for giving control back to the scheduler by
00190   calling mComposer->doNextJob().
00191 */
00192 
00193 /*
00194  Test plan:
00195 
00196  For each message format (e.g. openPGP/MIME)
00197  1. Body signed
00198  2. Body encrypted
00199  3. Body signed and encrypted
00200  4. Body encrypted, attachments encrypted  (they must be encrypted together, mEarlyAddAttachments)
00201  5. Body encrypted, attachments not encrypted
00202  6. Body encrypted, attachment encrypted and signed (separately)
00203  7. Body not encrypted, one attachment encrypted+signed, one attachment encrypted only, one attachment signed only
00204        (https://intevation.de/roundup/aegypten/issue295)
00205        (this is the reason attachments can't be encrypted together)
00206  8. Body and attachments encrypted+signed (they must be encrypted+signed together, mEarlyAddAttachments)
00207  9. Body encrypted+signed, attachments encrypted
00208  10. Body encrypted+signed, one attachment signed, one attachment not encrypted nor signed
00209  ...
00210 
00211  I recorded a KDExecutor script sending all of the above (David)
00212 
00213  Further tests (which test opportunistic encryption):
00214  1. Send a message to a person with valid key but without encryption preference
00215     and answer the question whether the message should be encrypted with Yes.
00216  2. Send a message to a person with valid key but without encryption preference
00217     and answer the question whether the message should be encrypted with No.
00218  3. Send a message to a person with valid key and with encryption preference
00219     "Encrypt whenever possible" (aka opportunistic encryption).
00220 */
00221 
00222 static TQString mErrorProcessingStructuringInfo =
00223 i18n("<qt><p>Structuring information returned by the Crypto plug-in "
00224      "could not be processed correctly; the plug-in might be damaged.</p>"
00225      "<p>Please contact your system administrator.</p></qt>");
00226 static TQString mErrorNoCryptPlugAndNoBuildIn =
00227 i18n("<p>No active Crypto Plug-In was found and the built-in OpenPGP code "
00228      "did not run successfully.</p>"
00229      "<p>You can do two things to change this:</p>"
00230      "<ul><li><em>either</em> activate a Plug-In using the "
00231      "Settings->Configure KMail->Plug-In dialog.</li>"
00232      "<li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
00233      "Identity->Advanced tab.</li></ul>");
00234 
00235 
00236 class MessageComposerJob {
00237 public:
00238   MessageComposerJob( MessageComposer* composer ) : mComposer( composer ) {}
00239   virtual ~MessageComposerJob() {}
00240 
00241   virtual void execute() = 0;
00242 
00243 protected:
00244   // These are the methods that call the private MessageComposer methods
00245   // Workaround for friend not being inherited
00246   void adjustCryptFlags() { mComposer->adjustCryptFlags(); }
00247   void composeMessage() { mComposer->composeMessage(); }
00248   void continueComposeMessage( KMMessage& msg, bool doSign, bool doEncrypt,
00249                                Kleo::CryptoMessageFormat format )
00250   {
00251     mComposer->continueComposeMessage( msg, doSign, doEncrypt, format );
00252   }
00253   void chiasmusEncryptAllAttachments() {
00254     mComposer->chiasmusEncryptAllAttachments();
00255   }
00256 
00257   MessageComposer* mComposer;
00258 };
00259 
00260 class ChiasmusBodyPartEncryptJob : public MessageComposerJob {
00261 public:
00262   ChiasmusBodyPartEncryptJob( MessageComposer * composer )
00263     : MessageComposerJob( composer ) {}
00264 
00265   void execute() {
00266     chiasmusEncryptAllAttachments();
00267   }
00268 };
00269 
00270 class AdjustCryptFlagsJob : public MessageComposerJob {
00271 public:
00272   AdjustCryptFlagsJob( MessageComposer* composer )
00273     : MessageComposerJob( composer ) {}
00274 
00275   void execute() {
00276     adjustCryptFlags();
00277   }
00278 };
00279 
00280 class ComposeMessageJob : public MessageComposerJob {
00281 public:
00282   ComposeMessageJob( MessageComposer* composer )
00283     : MessageComposerJob( composer ) {}
00284 
00285   void execute() {
00286     composeMessage();
00287   }
00288 };
00289 
00290 MessageComposer::MessageComposer( KMComposeWin* win, const char* name )
00291   : TQObject( win, name ), mComposeWin( win ), mCurrentJob( 0 ),
00292     mReferenceMessage( 0 ), mKeyResolver( 0 ), 
00293     mUseOpportunisticEncryption( false ),
00294     mSignBody( false ), mEncryptBody( false ),
00295     mSigningRequested(  false ), mEncryptionRequested( false ),
00296     mDoSign( false ), mDoEncrypt( false ),
00297     mAllowedCryptoMessageFormats( 0 ),
00298     mDisableCrypto( false ),
00299     mDisableBreaking( false ),
00300     mDebugComposerCrypto( false ),
00301     mAutoCharset( true ),
00302     mIsRichText( false ),
00303     mIdentityUid( 0 ), mRc( true ),
00304     mHoldJobs( false ),
00305     mNewBodyPart( 0 ),
00306     mEarlyAddAttachments( false ), mAllAttachmentsAreInBody( false ),
00307     mPreviousBoundaryLevel( 0 ),
00308     mEncryptWithChiasmus( false ),
00309     mPerformingSignOperation( false )
00310 {
00311 }
00312 
00313 MessageComposer::~MessageComposer()
00314 {
00315   delete mKeyResolver; mKeyResolver = 0;
00316   delete mNewBodyPart; mNewBodyPart = 0;
00317 }
00318 
00319 void MessageComposer::applyChanges( bool disableCrypto )
00320 {
00321   // Do the initial setup
00322   if( getenv("KMAIL_DEBUG_COMPOSER_CRYPTO") != 0 ) {
00323     TQCString cE = getenv("KMAIL_DEBUG_COMPOSER_CRYPTO");
00324     mDebugComposerCrypto = cE == "1" || cE.upper() == "ON" || cE.upper() == "TRUE";
00325     kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = TRUE" << endl;
00326   } else {
00327     mDebugComposerCrypto = false;
00328     kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl;
00329   }
00330 
00331   mHoldJobs = false;
00332   mRc = true;
00333 
00334   mDisableCrypto = disableCrypto;
00335 
00336   // 1: Read everything from KMComposeWin and set all
00337   //    trivial parts of the message
00338   readFromComposeWin();
00339 
00340   // From now on, we're not supposed to read from the composer win
00341   // TODO: Make it so ;-)
00342   // 1.5: Replace all body parts with their chiasmus-encrypted equivalent
00343   mJobs.push_back( new ChiasmusBodyPartEncryptJob( this ) );
00344 
00345   // 2: Set encryption/signing options and resolve keys
00346   mJobs.push_back( new AdjustCryptFlagsJob( this ) );
00347 
00348   // 3: Build the message (makes the crypto jobs also)
00349   mJobs.push_back( new ComposeMessageJob( this ) );
00350 
00351   // Finally: Run the jobs
00352   doNextJob();
00353 }
00354 
00355 void MessageComposer::doNextJob()
00356 {
00357   delete mCurrentJob; mCurrentJob = 0;
00358 
00359   if( mJobs.isEmpty() ) {
00360     // No more jobs. Signal that we're done
00361     emitDone( mRc );
00362     return;
00363   }
00364 
00365   if( !mRc ) {
00366     // Something has gone wrong - stop the process and bail out
00367     while( !mJobs.isEmpty() ) {
00368       delete mJobs.front();
00369       mJobs.pop_front();
00370     }
00371     emitDone( false );
00372     return;
00373   }
00374 
00375   // We have more jobs to do, but allow others to come first
00376   TQTimer::singleShot( 0, this, TQT_SLOT( slotDoNextJob() ) );
00377 }
00378 
00379 void MessageComposer::emitDone( bool b )
00380 {
00381   // Save memory - before sending the mail
00382   mEncodedBody = TQByteArray();
00383   delete mNewBodyPart; mNewBodyPart = 0;
00384   mOldBodyPart.clear();
00385   emit done( b );
00386 }
00387 
00388 void MessageComposer::slotDoNextJob()
00389 {
00390   assert( !mCurrentJob );
00391   if( mHoldJobs )
00392     // Always make it run from now. If more than one job should be held,
00393     // The individual jobs must do this.
00394     mHoldJobs = false;
00395   else {
00396     assert( !mJobs.empty() );
00397     // Get the next job
00398     mCurrentJob = mJobs.front();
00399     assert( mCurrentJob );
00400     mJobs.pop_front();
00401 
00402     // Execute it
00403     mCurrentJob->execute();
00404   }
00405 
00406   // Finally run the next job if necessary
00407   if( !mHoldJobs )
00408     doNextJob();
00409 }
00410 
00411 void MessageComposer::readFromComposeWin()
00412 {
00413   // Copy necessary attributes over
00414   mDisableBreaking = false;
00415 
00416   mSignBody = mComposeWin->mSignAction->isChecked();
00417   mSigningRequested = mSignBody; // for now; will be adjusted depending on attachments
00418   mEncryptBody = mComposeWin->mEncryptAction->isChecked();
00419   mEncryptionRequested = mEncryptBody; // for now; will be adjusted depending on attachments
00420 
00421   mAutoCharset = mComposeWin->mAutoCharset;
00422   mCharset = mComposeWin->mCharset;
00423   mReferenceMessage = mComposeWin->mMsg;
00424   // if the user made any modifications to the message then the Content-Type
00425   // of the message is no longer reliable (e. g. if he editted a draft/resent a
00426   // message and then removed all attachments or changed from PGP/MIME signed
00427   // to clearsigned);
00428   // even if the user didn't make any modifications to the message the
00429   // Content-Type of the message might be wrong, e.g. when inline-forwarding
00430   // an mp/alt message then the Content-Type is set to mp/alt although it should
00431   // be text/plain (cf. bug 127526);
00432   // OTOH we must not reset the Content-Type of inline invitations;
00433   // therefore we reset the Content-Type to text/plain whenever the current
00434   // Content-Type is multipart/*.
00435   if ( mReferenceMessage->type() == DwMime::kTypeMultipart )
00436     mReferenceMessage->setHeaderField( "Content-Type", "text/plain" );
00437   mUseOpportunisticEncryption = GlobalSettings::self()->pgpAutoEncrypt();
00438   mAllowedCryptoMessageFormats = mComposeWin->cryptoMessageFormat();
00439 
00440   if( mAutoCharset ) {
00441     TQCString charset = KMMsgBase::autoDetectCharset( mCharset, KMMessage::preferredCharsets(), mComposeWin->mEditor->text() );
00442     if( charset.isEmpty() )
00443     {
00444       KMessageBox::sorry( mComposeWin,
00445                           i18n( "No suitable encoding could be found for "
00446                                 "your message.\nPlease set an encoding "
00447                                 "using the 'Options' menu." ) );
00448       mRc = false;
00449       return;
00450     }
00451     mCharset = charset;
00452     // Also apply this to the composer window
00453     mComposeWin->mCharset = charset;
00454   }
00455   mReferenceMessage->setCharset(mCharset);
00456 
00457   mReferenceMessage->setTo(mComposeWin->to());
00458   mReferenceMessage->setFrom(mComposeWin->from());
00459   mReferenceMessage->setCc(mComposeWin->cc());
00460   mReferenceMessage->setSubject(mComposeWin->subject());
00461   mReferenceMessage->setReplyTo(mComposeWin->replyTo());
00462   mReferenceMessage->setBcc(mComposeWin->bcc());
00463 
00464   const KPIM::Identity & id = mComposeWin->identity();
00465 
00466   KMFolder *f = mComposeWin->mFcc->getFolder();
00467   assert( f != 0 );
00468   if ( f->idString() == id.fcc() )
00469     mReferenceMessage->removeHeaderField("X-KMail-Fcc");
00470   else
00471     mReferenceMessage->setFcc( f->idString() );
00472 
00473   // set the correct drafts folder
00474   mReferenceMessage->setDrafts( id.drafts() );
00475 
00476   if (id.isDefault())
00477     mReferenceMessage->removeHeaderField("X-KMail-Identity");
00478   else mReferenceMessage->setHeaderField("X-KMail-Identity", TQString::number( id.uoid() ));
00479 
00480   TQString replyAddr;
00481   if (!mComposeWin->replyTo().isEmpty()) replyAddr = mComposeWin->replyTo();
00482   else replyAddr = mComposeWin->from();
00483 
00484   if (mComposeWin->mRequestMDNAction->isChecked())
00485     mReferenceMessage->setHeaderField("Disposition-Notification-To", replyAddr);
00486   else
00487     mReferenceMessage->removeHeaderField("Disposition-Notification-To");
00488 
00489   if (mComposeWin->mUrgentAction->isChecked()) {
00490     mReferenceMessage->setHeaderField("X-PRIORITY", "2 (High)");
00491     mReferenceMessage->setHeaderField("Priority", "urgent");
00492   } else {
00493     mReferenceMessage->removeHeaderField("X-PRIORITY");
00494     mReferenceMessage->removeHeaderField("Priority");
00495   }
00496 
00497   int num = GlobalSettings::self()->custHeaderCount();
00498   for(int ix=0; ix<num; ix++) {
00499     CustomMimeHeader customMimeHeader( TQString::number(ix) );
00500     customMimeHeader.readConfig();
00501     mReferenceMessage->setHeaderField(
00502         KMMsgBase::toUsAscii( customMimeHeader.custHeaderName() ),
00503         customMimeHeader.custHeaderValue() );
00504   }
00505 
00506 
00507   // we have to remember the Bcc because it might have been overwritten
00508   // by a custom header (therefore we can't use bcc() later) and because
00509   // mimelib removes addresses without domain part (therefore we can't use
00510   // mReferenceMessage->bcc() later and also not now. So get the Bcc from
00511   // the composer window.)
00512   mBcc = mComposeWin->bcc();
00513   mTo = KPIM::splitEmailAddrList( mComposeWin->to().stripWhiteSpace() );
00514   mCc = KPIM::splitEmailAddrList( mComposeWin->cc().stripWhiteSpace() );
00515   mBccList = KPIM::splitEmailAddrList( mBcc.stripWhiteSpace() );
00516 
00517   for ( unsigned int i = 0 ; i < mComposeWin->mAtmList.count() ; ++i )
00518     mAttachments.push_back( Attachment( mComposeWin->mAtmList.at(i),
00519                     mComposeWin->signFlagOfAttachment( i ),
00520                     mComposeWin->encryptFlagOfAttachment( i ) ) );
00521 
00522   mEncryptWithChiasmus = mComposeWin->mEncryptWithChiasmus;
00523 
00524   mIsRichText = mComposeWin->mEditor->textFormat() == Qt::RichText;
00525   mIdentityUid = mComposeWin->identityUid();
00526   mText = breakLinesAndApplyCodec();
00527   assert( mText.isEmpty() || mText[mText.size()-1] == '\n' );
00528   // Hopefully we can get rid of this eventually, it's needed to be able
00529   // to break the plain/text version of a multipart/alternative (html) mail
00530   // according to the line breaks of the richtext version.
00531   mLineBreakColumn = mComposeWin->mEditor->lineBreakColumn();
00532 }
00533 
00534 static TQCString escape_quoted_string( const TQCString & str ) {
00535   TQCString result;
00536   const unsigned int str_len = str.length();
00537   result.resize( 2*str_len + 1 );
00538   char * d = result.data();
00539   for ( unsigned int i = 0 ; i < str_len ; ++i )
00540     switch ( const char ch = str[i] ) {
00541     case '\\':
00542     case '"':
00543       *d++ = '\\';
00544     default: // fall through:
00545       *d++ = ch;
00546     }
00547   result.truncate( d - result.begin() );
00548   return result;
00549 }
00550 
00551 bool MessageComposer::encryptWithChiasmus( const Kleo::CryptoBackend::Protocol * chiasmus,
00552                                            const TQByteArray& body,
00553                                            TQByteArray& resultData )
00554 {
00555   std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-encrypt", TQMap<TQString,TQVariant>() ) );
00556   if ( !job.get() ) {
00557     const TQString msg = i18n( "Chiasmus backend does not offer the "
00558                               "\"x-encrypt\" function. Please report this bug." );
00559     KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00560     return false;
00561   }
00562   if ( !job->setProperty( "key", GlobalSettings::chiasmusKey() ) ||
00563        !job->setProperty( "options", GlobalSettings::chiasmusOptions() ) ||
00564        !job->setProperty( "input", body ) ) {
00565     const TQString msg = i18n( "The \"x-encrypt\" function does not accept "
00566                               "the expected parameters. Please report this bug." );
00567     KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00568     return false;
00569   }
00570   const GpgME::Error err = job->exec();
00571   if ( err.isCanceled() || err ) {
00572     if ( err )
00573       job->showErrorDialog( mComposeWin, i18n( "Chiasmus Encryption Error" ) );
00574     return false;
00575   }
00576   const TQVariant result = job->property( "result" );
00577   if ( result.type() != TQVariant::ByteArray ) {
00578     const TQString msg = i18n( "Unexpected return value from Chiasmus backend: "
00579                               "The \"x-encrypt\" function did not return a "
00580                               "byte array. Please report this bug." );
00581     KMessageBox::error( mComposeWin, msg, i18n( "Chiasmus Backend Error" ) );
00582     return false;
00583   }
00584   resultData = result.toByteArray();
00585   return true;
00586 }
00587 
00588 void MessageComposer::chiasmusEncryptAllAttachments() {
00589   if ( !mEncryptWithChiasmus )
00590     return;
00591   assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
00592   if ( mAttachments.empty() )
00593     return;
00594   const Kleo::CryptoBackend::Protocol * chiasmus
00595     = Kleo::CryptoBackendFactory::instance()->protocol( "Chiasmus" );
00596   assert( chiasmus ); // kmcomposewin code should have made sure
00597 
00598 
00599   for ( TQValueVector<Attachment>::iterator it = mAttachments.begin(), end = mAttachments.end() ; it != end ; ++it ) {
00600     KMMessagePart * part = it->part;
00601     const TQString filename = part->fileName();
00602     if ( filename.endsWith( ".xia", false ) )
00603       continue; // already encrypted
00604     const TQByteArray body = part->bodyDecodedBinary();
00605     TQByteArray resultData;
00606     if ( !encryptWithChiasmus( chiasmus, body, resultData ) ) {
00607       mRc = false;
00608       return;
00609     }
00610     // everything ok, so let's fill in the part again:
00611     TQValueList<int> dummy;
00612     part->setBodyAndGuessCte( resultData, dummy );
00613     part->setTypeStr( "application" );
00614     part->setSubtypeStr( "vnd.de.bund.bsi.chiasmus" );
00615     part->setName( filename + ".xia" );
00616     const TQCString enc_name = KMMsgBase::encodeRFC2231StringAutoDetectCharset(
00617         filename + ".xia", part->charset() );
00618     const TQCString cDisp = "attachment;\n\tfilename"
00619                            + ( TQString( enc_name ) != filename + ".xia"
00620                                ? "*=" + enc_name
00621                                : "=\"" + escape_quoted_string( enc_name ) + '\"' );
00622     part->setContentDisposition( cDisp );
00623   }
00624 }
00625 
00626 void MessageComposer::adjustCryptFlags()
00627 {
00628   if ( !mDisableCrypto &&
00629        mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat &&
00630        !mAttachments.empty() &&
00631        ( mSigningRequested || mEncryptionRequested ) )
00632   {
00633     int ret;
00634     if ( mAllowedCryptoMessageFormats == Kleo::InlineOpenPGPFormat ) {
00635       ret = KMessageBox::warningYesNoCancel( mComposeWin,
00636                                              i18n("The inline OpenPGP crypto message format "
00637                                                   "does not support encryption or signing "
00638                                                   "of attachments.\n"
00639                                                   "Really use deprecated inline OpenPGP?"),
00640                                              i18n("Insecure Message Format"),
00641                                              i18n("Use Inline OpenPGP"),
00642                                              i18n("Use OpenPGP/MIME") );
00643     }
00644     else {
00645       // if other crypto message formats are allowed then simply don't use
00646       // inline OpenPGP
00647       ret = KMessageBox::No;
00648     }
00649 
00650     if ( ret == KMessageBox::Cancel ) {
00651       mRc = false;
00652       return;
00653     } else if ( ret == KMessageBox::No ) {
00654       mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat;
00655       mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat;
00656       if ( mSigningRequested ) {
00657         // The composer window disabled signing on the attachments, re-enable it
00658         for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00659           mAttachments[idx].sign = true;
00660       }
00661       if ( mEncryptionRequested ) {
00662         // The composer window disabled encrypting on the attachments, re-enable it
00663         // We assume this is what the user wants - after all he chose OpenPGP/MIME for this.
00664         for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00665           mAttachments[idx].encrypt = true;
00666       }
00667     }
00668   }
00669 
00670   mKeyResolver =
00671     new Kleo::KeyResolver( encryptToSelf(), showKeyApprovalDialog(),
00672                mUseOpportunisticEncryption, mAllowedCryptoMessageFormats,
00673                encryptKeyNearExpiryWarningThresholdInDays(),
00674                signingKeyNearExpiryWarningThresholdInDays(),
00675                encryptRootCertNearExpiryWarningThresholdInDays(),
00676                signingRootCertNearExpiryWarningThresholdInDays(),
00677                encryptChainCertNearExpiryWarningThresholdInDays(),
00678                signingChainCertNearExpiryWarningThresholdInDays() );
00679 
00680   if ( !mDisableCrypto ) {
00681     const KPIM::Identity & id =
00682       kmkernel->identityManager()->identityForUoidOrDefault( mIdentityUid );
00683 
00684     TQStringList encryptToSelfKeys;
00685     if ( !id.pgpEncryptionKey().isEmpty() )
00686       encryptToSelfKeys.push_back( id.pgpEncryptionKey() );
00687     if ( !id.smimeEncryptionKey().isEmpty() )
00688       encryptToSelfKeys.push_back( id.smimeEncryptionKey() );
00689     if ( mKeyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) {
00690       mRc = false;
00691       return;
00692     }
00693 
00694     TQStringList signKeys;
00695     if ( !id.pgpSigningKey().isEmpty() )
00696       signKeys.push_back( mPGPSigningKey = id.pgpSigningKey() );
00697     if ( !id.smimeSigningKey().isEmpty() )
00698       signKeys.push_back( mSMIMESigningKey = id.smimeSigningKey() );
00699     if ( mKeyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) {
00700       mRc = false;
00701       return;
00702     }
00703   }
00704 
00705   mKeyResolver->setPrimaryRecipients( mTo + mCc );
00706   mKeyResolver->setSecondaryRecipients( mBccList );
00707 
00708   // check settings of composer buttons *and* attachment check boxes
00709   bool doSignCompletely    = mSigningRequested;
00710   bool doEncryptCompletely = mEncryptionRequested;
00711   for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) {
00712     if ( mAttachments[idx].encrypt )
00713       mEncryptionRequested = true;
00714     else
00715       doEncryptCompletely = false;
00716     if ( mAttachments[idx].sign )
00717       mSigningRequested = true;
00718     else
00719       doSignCompletely = false;
00720   }
00721 
00722   mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely );
00723 
00724   if ( !mRc )
00725     return;
00726 
00727   mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely );
00728 
00729   if ( !mRc )
00730     return;
00731 
00732   // resolveAllKeys needs to run even if mDisableCrypto == true, since
00733   // we depend on it collecting all recipients into one dummy
00734   // SplitInfo to avoid special-casing all over the place:
00735   if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok )
00736     mRc = false;
00737 }
00738 
00739 bool MessageComposer::determineWhetherToSign( bool doSignCompletely ) {
00740   bool sign = false;
00741   switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) {
00742   case Kleo::DoIt:
00743     if ( !mSigningRequested ) {
00744       markAllAttachmentsForSigning( true );
00745       return true;
00746     }
00747     sign = true;
00748     break;
00749   case Kleo::DontDoIt:
00750     sign = false;
00751     break;
00752   case Kleo::AskOpportunistic:
00753     assert( 0 );
00754   case Kleo::Ask:
00755     {
00756       // the user wants to be asked or has to be asked
00757       const KCursorSaver idle( KBusyPtr::idle() );
00758       const TQString msg = i18n("Examination of the recipient's signing preferences "
00759                    "yielded that you be asked whether or not to sign "
00760                    "this message.\n"
00761                    "Sign this message?");
00762       switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00763                          i18n("Sign Message?"),
00764                          i18n("to sign","&Sign"),
00765                          i18n("Do &Not Sign") ) ) {
00766       case KMessageBox::Cancel:
00767     mRc = false;
00768     return false;
00769       case KMessageBox::Yes:
00770     markAllAttachmentsForSigning( true );
00771     return true;
00772       case KMessageBox::No:
00773     markAllAttachmentsForSigning( false );
00774     return false;
00775       }
00776     }
00777     break;
00778   case Kleo::Conflict:
00779     {
00780       // warn the user that there are conflicting signing preferences
00781       const KCursorSaver idle( KBusyPtr::idle() );
00782       const TQString msg = i18n("There are conflicting signing preferences "
00783                    "for these recipients.\n"
00784                    "Sign this message?");
00785       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00786                         i18n("Sign Message?"),
00787                         i18n("to sign","&Sign"),
00788                         i18n("Do &Not Sign") ) ) {
00789       case KMessageBox::Cancel:
00790     mRc = false;
00791     return false;
00792       case KMessageBox::Yes:
00793     markAllAttachmentsForSigning( true );
00794     return true;
00795       case KMessageBox::No:
00796     markAllAttachmentsForSigning( false );
00797     return false;
00798       }
00799     }
00800     break;
00801   case Kleo::Impossible:
00802     {
00803       const KCursorSaver idle( KBusyPtr::idle() );
00804       const TQString msg = i18n("You have requested to sign this message, "
00805                    "but no valid signing keys have been configured "
00806                    "for this identity.");
00807       if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00808                            i18n("Send Unsigned?"),
00809                                                i18n("Send &Unsigned") )
00810        == KMessageBox::Cancel ) {
00811     mRc = false;
00812     return false;
00813       } else {
00814     markAllAttachmentsForSigning( false );
00815     return false;
00816       }
00817     }
00818   }
00819 
00820   if ( !sign || !doSignCompletely ) {
00821     if ( warnSendUnsigned() ) {
00822       const KCursorSaver idle( KBusyPtr::idle() );
00823       const TQString msg = sign && !doSignCompletely
00824     ? i18n("Some parts of this message will not be signed.\n"
00825            "Sending only partially signed messages might violate site policy.\n"
00826            "Sign all parts instead?") // oh, I hate this...
00827     : i18n("This message will not be signed.\n"
00828            "Sending unsigned message might violate site policy.\n"
00829            "Sign message instead?") ; // oh, I hate this...
00830       const TQString buttonText = sign && !doSignCompletely
00831     ? i18n("&Sign All Parts") : i18n("&Sign") ;
00832       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00833                         i18n("Unsigned-Message Warning"),
00834                         buttonText,
00835                         i18n("Send &As Is") ) ) {
00836       case KMessageBox::Cancel:
00837     mRc = false;
00838     return false;
00839       case KMessageBox::Yes:
00840     markAllAttachmentsForSigning( true );
00841     return true;
00842       case KMessageBox::No:
00843     return sign || doSignCompletely;
00844       }
00845     }
00846   }
00847 
00848   return sign || doSignCompletely ;
00849 }
00850 
00851 bool MessageComposer::determineWhetherToEncrypt( bool doEncryptCompletely ) {
00852   bool encrypt = false;
00853   bool opportunistic = false;
00854   switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) {
00855   case Kleo::DoIt:
00856     if ( !mEncryptionRequested ) {
00857       markAllAttachmentsForEncryption( true );
00858       return true;
00859     }
00860     encrypt = true;
00861     break;
00862   case Kleo::DontDoIt:
00863     encrypt = false;
00864     break;
00865   case Kleo::AskOpportunistic:
00866     opportunistic = true;
00867     // fall through...
00868   case Kleo::Ask:
00869     {
00870       // the user wants to be asked or has to be asked
00871       const KCursorSaver idle( KBusyPtr::idle() );
00872       const TQString msg = opportunistic
00873     ? i18n("Valid trusted encryption keys were found for all recipients.\n"
00874            "Encrypt this message?")
00875     : i18n("Examination of the recipient's encryption preferences "
00876            "yielded that you be asked whether or not to encrypt "
00877            "this message.\n"
00878            "Encrypt this message?");
00879       switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00880                          i18n("Encrypt Message?"),
00881                          mDoSign
00882                          ? i18n("Sign && &Encrypt")
00883                          : i18n("&Encrypt"),
00884                          mDoSign
00885                          ? i18n("&Sign Only")
00886                          : i18n("&Send As-Is") ) ) {
00887       case KMessageBox::Cancel:
00888     mRc = false;
00889     return false;
00890       case KMessageBox::Yes:
00891     markAllAttachmentsForEncryption( true );
00892     return true;
00893       case KMessageBox::No:
00894     markAllAttachmentsForEncryption( false );
00895     return false;
00896       }
00897     }
00898     break;
00899   case Kleo::Conflict:
00900     {
00901       // warn the user that there are conflicting encryption preferences
00902       const KCursorSaver idle( KBusyPtr::idle() );
00903       const TQString msg = i18n("There are conflicting encryption preferences "
00904                    "for these recipients.\n"
00905                    "Encrypt this message?");
00906       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00907                         i18n("Encrypt Message?"),
00908                         i18n("&Encrypt"),
00909                         i18n("Do &Not Encrypt") ) ) {
00910       case KMessageBox::Cancel:
00911     mRc = false;
00912     return false;
00913       case KMessageBox::Yes:
00914     markAllAttachmentsForEncryption( true );
00915     return true;
00916       case KMessageBox::No:
00917     markAllAttachmentsForEncryption( false );
00918     return false;
00919       }
00920     }
00921     break;
00922   case Kleo::Impossible:
00923     {
00924       const KCursorSaver idle( KBusyPtr::idle() );
00925       const TQString msg = i18n("You have requested to encrypt this message, "
00926                    "and to encrypt a copy to yourself, "
00927                    "but no valid trusted encryption keys have been "
00928                    "configured for this identity.");
00929       if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00930                            i18n("Send Unencrypted?"),
00931                                                i18n("Send &Unencrypted") )
00932        == KMessageBox::Cancel ) {
00933     mRc = false;
00934     return false;
00935       } else {
00936     markAllAttachmentsForEncryption( false );
00937     return false;
00938       }
00939     }
00940   }
00941 
00942   if ( !encrypt || !doEncryptCompletely ) {
00943     if ( warnSendUnencrypted() ) {
00944       const KCursorSaver idle( KBusyPtr::idle() );
00945       const TQString msg = !doEncryptCompletely
00946     ? i18n("Some parts of this message will not be encrypted.\n"
00947            "Sending only partially encrypted messages might violate site policy "
00948            "and/or leak sensitive information.\n"
00949            "Encrypt all parts instead?") // oh, I hate this...
00950     : i18n("This message will not be encrypted.\n"
00951            "Sending unencrypted messages might violate site policy and/or "
00952            "leak sensitive information.\n"
00953            "Encrypt messages instead?") ; // oh, I hate this...
00954       const TQString buttonText = !doEncryptCompletely
00955     ? i18n("&Encrypt All Parts") : i18n("&Encrypt") ;
00956       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00957                         i18n("Unencrypted Message Warning"),
00958                         buttonText,
00959                         mDoSign
00960                         ? i18n("&Sign Only")
00961                         : i18n("&Send As-Is") ) ) {
00962       case KMessageBox::Cancel:
00963     mRc = false;
00964     return false;
00965       case KMessageBox::Yes:
00966     markAllAttachmentsForEncryption( true );
00967     return true;
00968       case KMessageBox::No:
00969     return encrypt || doEncryptCompletely;
00970       }
00971     }
00972   }
00973 
00974   return encrypt || doEncryptCompletely ;
00975 }
00976 
00977 void MessageComposer::markAllAttachmentsForSigning( bool sign ) {
00978   mSignBody = sign;
00979   for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00980     it->sign = sign;
00981 }
00982 
00983 void MessageComposer::markAllAttachmentsForEncryption( bool enc ) {
00984   mEncryptBody = enc;
00985   for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00986     it->encrypt = enc;
00987 }
00988 
00989 
00990 void MessageComposer::composeMessage()
00991 {
00992   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
00993     if ( mKeyResolver->encryptionItems( concreteCryptoMessageFormats[i] ).empty() )
00994       continue;
00995     KMMessage * msg = new KMMessage( *mReferenceMessage );
00996     composeMessage( *msg, mDoSign, mDoEncrypt, concreteCryptoMessageFormats[i] );
00997     if ( !mRc )
00998       return;
00999   }
01000 }
01001 
01002 //
01003 // These are replacements for StructuringInfo(Wrapper):
01004 //
01005 
01006 // check whether to use multipart/{signed,encrypted}
01007 static inline bool makeMultiMime( Kleo::CryptoMessageFormat f, bool sign ) {
01008   switch ( f ) {
01009   default:
01010   case Kleo::InlineOpenPGPFormat:
01011   case Kleo::SMIMEOpaqueFormat:   return false;
01012   case Kleo::OpenPGPMIMEFormat:   return true;
01013   case Kleo::SMIMEFormat:         return sign; // only on sign - there's no mp/encrypted for S/MIME
01014   }
01015 }
01016 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
01017   return makeMultiMime( f, true );
01018 }
01019 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
01020   return makeMultiMime( f, false );
01021 }
01022 
01023 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool /*signing*/ ) {
01024   return f != Kleo::InlineOpenPGPFormat;
01025 }
01026 
01027 static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f, bool signing ) {
01028   switch ( f ) {
01029   default:
01030   case Kleo::InlineOpenPGPFormat: return 0;
01031   case Kleo::OpenPGPMIMEFormat:
01032     return signing ?
01033       "multipart/signed;\n\t"
01034       "boundary=\"%boundary\";\n\t"
01035       "protocol=\"application/pgp-signature\";\n\t"
01036       "micalg=pgp-sha1" // FIXME: obtain this parameter from gpgme!
01037       :
01038       "multipart/encrypted;\n\t"
01039       "boundary=\"%boundary\";\n\t"
01040       "protocol=\"application/pgp-encrypted\""
01041       ;
01042   case Kleo::SMIMEFormat:
01043     if ( signing )
01044       return
01045     "multipart/signed;\n\t"
01046     "boundary=\"%boundary\";\n\t"
01047     "protocol=\"application/pkcs7-signature\";\n\t"
01048     "micalg=sha1"; // FIXME: obtain this parameter from gpgme!
01049     // fall through (for encryption, there's no difference between
01050     // SMIME and SMIMEOpaque, since there is no mp/encrypted for
01051     // S/MIME):
01052   case Kleo::SMIMEOpaqueFormat:
01053     return signing ?
01054       "application/pkcs7-mime;\n\t"
01055       "smime-type=signed-data;\n\t"
01056       "name=\"smime.p7m\";\n\t"
01057       :
01058       "application/pkcs7-mime;\n\t"
01059       "smime-type=enveloped-data;\n\t"
01060       "name=\"smime.p7m\";\n\t"
01061       ;
01062   }
01063 }
01064 
01065 static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01066   switch ( f ) {
01067   default:
01068   case Kleo::InlineOpenPGPFormat:
01069   case Kleo::OpenPGPMIMEFormat:
01070     return 0;
01071   case Kleo::SMIMEFormat:
01072     if ( signing )
01073       return 0;
01074   case Kleo::SMIMEOpaqueFormat:
01075     return "attachment; filename=\"smime.p7m\"";
01076   }
01077 }
01078 
01079 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
01080   return makeMultiPartSigned( f );
01081 }
01082 
01083 static inline const char * nestedContentType( Kleo::CryptoMessageFormat f, bool signing ) {
01084   switch ( f ) {
01085   case Kleo::OpenPGPMIMEFormat:
01086     return signing ? "application/pgp-signature; name=signature.asc \nContent-Description: This is a digitally signed message part." : "application/octet-stream" ;
01087   case Kleo::SMIMEFormat:
01088     if ( signing )
01089       return "application/pkcs7-signature; name=\"smime.p7s\"";
01090     // fall through:
01091   default:
01092   case Kleo::InlineOpenPGPFormat:
01093   case Kleo::SMIMEOpaqueFormat:
01094     return 0;
01095   }
01096 }
01097 
01098 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01099   if ( !signing && f == Kleo::OpenPGPMIMEFormat )
01100     return "inline; filename=\"msg.asc\"";
01101   if ( signing && f == Kleo::SMIMEFormat )
01102     return "attachment; filename=\"smime.p7s\"";
01103   return 0;
01104 }
01105 
01106 static inline bool binaryHint( Kleo::CryptoMessageFormat f ) {
01107   switch ( f ) {
01108   case Kleo::SMIMEFormat:
01109   case Kleo::SMIMEOpaqueFormat:
01110     return true;
01111   default:
01112   case Kleo::OpenPGPMIMEFormat:
01113   case Kleo::InlineOpenPGPFormat:
01114     return false;
01115   }
01116 }
01117 
01118 static inline bool armor( Kleo::CryptoMessageFormat f ) {
01119   return !binaryHint( f );
01120 }
01121 
01122 static inline bool textMode( Kleo::CryptoMessageFormat f ) {
01123   return f == Kleo::InlineOpenPGPFormat;
01124 }
01125 
01126 static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) {
01127   switch ( f ) {
01128   case Kleo::SMIMEOpaqueFormat:
01129     return GpgME::Context::Normal;
01130   case Kleo::InlineOpenPGPFormat:
01131     return GpgME::Context::Clearsigned;
01132   default:
01133   case Kleo::SMIMEFormat:
01134   case Kleo::OpenPGPMIMEFormat:
01135     return GpgME::Context::Detached;
01136   }
01137 }
01138 
01139 //
01140 // END replacements for StructuringInfo(Wrapper)
01141 //
01142 
01143 class EncryptMessageJob : public MessageComposerJob {
01144 public:
01145   EncryptMessageJob( KMMessage* msg, const Kleo::KeyResolver::SplitInfo & si,
01146                      bool doSign, bool doEncrypt, const TQByteArray& encodedBody,
01147                      int boundaryLevel, /*const KMMessagePart& oldBodyPart,*/
01148                      KMMessagePart* newBodyPart, Kleo::CryptoMessageFormat format,
01149              MessageComposer* composer )
01150     : MessageComposerJob( composer ), mMsg( msg ), mSplitInfo( si ),
01151       mDoSign( doSign ), mDoEncrypt( doEncrypt ), mEncodedBody( encodedBody ),
01152       mBoundaryLevel( boundaryLevel ), /*mOldBodyPart( oldBodyPart ),*/
01153       mNewBodyPart( newBodyPart ), mFormat( format ) {}
01154 
01155   void execute() {
01156     KMMessagePart tmpNewBodyPart;
01157     tmpNewBodyPart.duplicate( *mNewBodyPart ); // slow - we duplicate everything again
01158 
01159     // TODO: Async call
01160 
01161     mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt,
01162                                tmpNewBodyPart, mFormat );
01163     if ( !mComposer->mRc ) {
01164       delete mMsg; mMsg = 0;
01165       return;
01166     }
01167     mComposer->mMessageList.push_back( mMsg );
01168   }
01169 
01170 private:
01171   KMMessage* mMsg;
01172   Kleo::KeyResolver::SplitInfo mSplitInfo;
01173   bool mDoSign, mDoEncrypt;
01174   TQByteArray mEncodedBody;
01175   int mBoundaryLevel;
01176   //KMMessagePart mOldBodyPart;
01177   KMMessagePart* mNewBodyPart;
01178   Kleo::CryptoMessageFormat mFormat;
01179 };
01180 
01181 class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
01182 public:
01183   SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer )
01184     : MessageComposerJob( composer ) {}
01185 
01186   void execute() {
01187     KMMessage * last = mComposer->mMessageList.back();
01188     mComposer->mMessageList.pop_back();
01189     mComposer->mMessageList.back()->setUnencryptedMsg( last );
01190   }
01191 };
01192 
01193 void MessageComposer::composeInlineOpenPGPMessage( KMMessage& theMessage,
01194                                                    bool doSign, bool doEncrypt )
01195 {
01196   // preprocess the body text
01197   const TQByteArray bodyData = mText;
01198   if (bodyData.isNull()) {
01199     mRc = false;
01200     return;
01201   }
01202 
01203   mNewBodyPart = 0; // unused
01204   mEarlyAddAttachments = false;
01205   mAllAttachmentsAreInBody = false;
01206 
01207   // set the main headers
01208   theMessage.deleteBodyParts();
01209   TQString oldContentType = theMessage.headerField( "Content-Type" );
01210   theMessage.removeHeaderField("Content-Type");
01211   theMessage.removeHeaderField("Content-Transfer-Encoding");
01212 
01213   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01214     = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat );
01215   kdWarning( splitInfos.empty() )
01216     << "MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat"
01217     << endl;
01218   std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it;
01219   for ( it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) {
01220     const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01221     KMMessage* msg = new KMMessage( theMessage );
01222     if ( doEncrypt ) {
01223       Kpgp::Result result;
01224       TQByteArray encryptedBody;
01225       if ( doSign ) {  // Sign and encrypt
01226         const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat );
01227         result = pgpSignedAndEncryptedMsg( encryptedBody, bodyData, signingKeys,
01228                                            splitInfo.keys, Kleo::InlineOpenPGPFormat );
01229       } else { // Encrypt but don't sign
01230         result = pgpEncryptedMsg( encryptedBody, bodyData,
01231                                   splitInfo.keys, Kleo::InlineOpenPGPFormat );
01232       }
01233       if ( result != Kpgp::Ok ) {
01234         mRc = false;
01235         return;
01236       }
01237       assert( !encryptedBody.isNull() ); // if you hit this, check gpg-agent is running, then blame gpgme.
01238       mOldBodyPart.setBodyEncodedBinary( encryptedBody );
01239     } else {
01240       if ( doSign ) { // Sign but don't encrypt
01241         pgpSignedMsg( bodyData, Kleo::InlineOpenPGPFormat );
01242         if ( mSignature.isNull() ) {
01243           mRc = false;
01244           return;
01245         }
01246         mOldBodyPart.setBodyEncodedBinary( mSignature );
01247       } else { // don't sign nor encrypt -> nothing to do
01248         assert( !bodyData.isNull() );
01249         mOldBodyPart.setBodyEncodedBinary( bodyData );
01250       }
01251     }
01252     mOldBodyPart.setContentDisposition( "inline" );
01253     mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01254     mOldBodyPart.setCharset(mCharset);
01255     addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01256     mMessageList.push_back( msg );
01257     if ( it == splitInfos.begin() ) {
01258       if ( doEncrypt && !saveMessagesEncrypted() ) {
01259         mOldBodyPart.setBodyEncodedBinary( bodyData );
01260         KMMessage* msgUnenc = new KMMessage( theMessage );
01261         addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01262         msg->setUnencryptedMsg( msgUnenc );
01263       }
01264     }
01265   } // end for
01266 }
01267 
01268 // very much inspired by composeInlineOpenPGPMessage
01269 void MessageComposer::composeChiasmusMessage( KMMessage& theMessage, Kleo::CryptoMessageFormat format )
01270 {
01271   assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
01272   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
01273   assert( cpf );
01274   const Kleo::CryptoBackend::Protocol * chiasmus
01275     = cpf->protocol( "Chiasmus" );
01276   assert( chiasmus ); // kmcomposewin code should have made sure
01277 
01278   // preprocess the body text
01279   const TQByteArray bodyData = mText;
01280   if (bodyData.isNull()) {
01281     mRc = false;
01282     return;
01283   }
01284 
01285   mNewBodyPart = 0; // unused
01286   mEarlyAddAttachments = false;
01287   mAllAttachmentsAreInBody = false;
01288 
01289   // set the main headers
01290   theMessage.deleteBodyParts();
01291   TQString oldContentType = theMessage.headerField( "Content-Type" );
01292   theMessage.removeHeaderField("Content-Type");
01293   theMessage.removeHeaderField("Content-Transfer-Encoding");
01294 
01295   // This reads strange, but we know that AdjustCryptFlagsJob created a single splitinfo,
01296   // under the given "format" (usually openpgp/mime; doesn't matter)
01297   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01298     = mKeyResolver->encryptionItems( format );
01299   assert( splitInfos.size() == 1 );
01300   for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01301   {
01302     const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01303     KMMessage* msg = new KMMessage( theMessage );
01304     TQByteArray encryptedBody;
01305 
01306     if ( !encryptWithChiasmus( chiasmus, bodyData, encryptedBody ) ) {
01307       mRc = false;
01308       return;
01309     }
01310     assert( !encryptedBody.isNull() );
01311     // This leaves CTE==7-bit, no good
01312     //mOldBodyPart.setBodyEncodedBinary( encryptedBody );
01313 
01314     bool doSign = false;
01315     TQValueList<int> allowedCTEs;
01316     mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
01317                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01318                      doSign );
01319 
01320 
01321     mOldBodyPart.setContentDisposition( "inline" );
01322     // Used in case of no attachments
01323     mOldBodyPart.setOriginalContentTypeStr( "application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
01324     // Used in case of attachments
01325     mOldBodyPart.setTypeStr( "application" );
01326     mOldBodyPart.setSubtypeStr( "vnd.de.bund.bsi.chiasmus-text" );
01327     mOldBodyPart.setAdditionalCTypeParamStr( TQCString( "chiasmus-charset=" + mCharset ) );
01328     addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01329     mMessageList.push_back( msg );
01330 
01331     if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
01332       mOldBodyPart.setBodyEncodedBinary( bodyData );
01333       KMMessage* msgUnenc = new KMMessage( theMessage );
01334       addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01335       msg->setUnencryptedMsg( msgUnenc );
01336     }
01337   }
01338 }
01339 
01340 void MessageComposer::composeMessage( KMMessage& theMessage,
01341                                       bool doSign, bool doEncrypt,
01342                                       Kleo::CryptoMessageFormat format )
01343 {
01344 #ifdef DEBUG
01345   kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl;
01346 #endif
01347   if ( format == Kleo::InlineOpenPGPFormat ) {
01348     composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
01349     return;
01350   }
01351 
01352   if ( mEncryptWithChiasmus )
01353   {
01354     composeChiasmusMessage( theMessage, format );
01355     return;
01356   }
01357 
01358   // create informative header for those that have no mime-capable
01359   // email client
01360   theMessage.setBody( "This message is in MIME format." );
01361 
01362   // preprocess the body text
01363   TQByteArray bodyData = mText;
01364   if (bodyData.isNull()) {
01365     mRc = false;
01366     return;
01367   }
01368 
01369   // set the main headers
01370   TQString oldContentType = theMessage.headerField( "Content-Type" );
01371   theMessage.deleteBodyParts();
01372   theMessage.removeHeaderField("Content-Type");
01373   theMessage.removeHeaderField("Content-Transfer-Encoding");
01374   theMessage.setAutomaticFields(true); // == multipart/mixed
01375 
01376   // this is our *final* body part
01377   mNewBodyPart = new KMMessagePart;
01378 
01379   // this is the boundary depth of the surrounding MIME part
01380   mPreviousBoundaryLevel = 0;
01381 
01382   // whether the body must be signed/encrypted
01383   const bool doEncryptBody = doEncrypt && mEncryptBody;
01384   const bool doSignBody = doSign && mSignBody;
01385 
01386   // create temporary bodyPart for editor text
01387   // (and for all attachments, if mail is to be signed and/or encrypted)
01388   mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
01389 
01390   mAllAttachmentsAreInBody = mEarlyAddAttachments;
01391 
01392   // test whether there ARE attachments that can be included into the body
01393   if( mEarlyAddAttachments ) {
01394     bool someOk = false;
01395     for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01396       if ( it->encrypt == doEncryptBody && it->sign == doSignBody )
01397         someOk = true;
01398       else
01399         mAllAttachmentsAreInBody = false;
01400     }
01401     if( !mAllAttachmentsAreInBody && !someOk )
01402       mEarlyAddAttachments = false;
01403   }
01404 
01405   kdDebug(5006) << "mEarlyAddAttachments=" << mEarlyAddAttachments << " mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
01406 
01407   // if an html message is to be generated, make a text/plain and text/html part
01408   mMultipartMixedBoundary = "";
01409   if ( mEarlyAddAttachments ) {
01410     mOldBodyPart.setTypeStr( "multipart" );
01411     mOldBodyPart.setSubtypeStr( "mixed" );
01412     // calculate a boundary string
01413     DwMediaType tmpCT;
01414     tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
01415     mMultipartMixedBoundary = tmpCT.Boundary().c_str();
01416   }
01417   else if ( mIsRichText ) {
01418     mOldBodyPart.setTypeStr( "multipart" );
01419     mOldBodyPart.setSubtypeStr( "alternative" );
01420   }
01421   else
01422     mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01423 
01424   mOldBodyPart.setContentDisposition( "inline" );
01425 
01426   if ( mIsRichText ) { // create a multipart body
01427     // calculate a boundary string
01428     TQCString boundaryCStr;  // storing boundary string data
01429     TQCString newbody;
01430     DwMediaType tmpCT;
01431     tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
01432     boundaryCStr = KMail::Util::CString( tmpCT.Boundary() );
01433     TQValueList<int> allowedCTEs;
01434 
01435     KMMessagePart textBodyPart;
01436     textBodyPart.setTypeStr("text");
01437     textBodyPart.setSubtypeStr("plain");
01438 
01439     TQCString textbody = plainTextFromMarkup( mText /* converted to TQString */ );
01440 
01441     // the signed body must not be 8bit encoded
01442     textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
01443                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01444                      doSign );
01445     textBodyPart.setCharset( mCharset );
01446     textBodyPart.setBodyEncoded( textbody );
01447     DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart );
01448     textDwPart->Assemble();
01449     newbody += "--";
01450     newbody +=     boundaryCStr;
01451     newbody +=                 "\n";
01452     newbody += textDwPart->AsString().c_str();
01453     delete textDwPart;
01454     textDwPart = 0;
01455 
01456     KMMessagePart htmlBodyPart;
01457     htmlBodyPart.setTypeStr("text");
01458     htmlBodyPart.setSubtypeStr("html");
01459     TQByteArray htmlbody = mText;
01460     // the signed body must not be 8bit encoded
01461     htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
01462                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01463                      doSign );
01464     htmlBodyPart.setCharset( mCharset );
01465     htmlBodyPart.setBodyEncodedBinary( htmlbody );
01466     DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart );
01467     htmlDwPart->Assemble();
01468     newbody += "\n--";
01469     newbody +=     boundaryCStr;
01470     newbody +=                 "\n";
01471     newbody += htmlDwPart->AsString().c_str();
01472     delete htmlDwPart;
01473     htmlDwPart = 0;
01474 
01475     newbody += "--";
01476     newbody +=     boundaryCStr;
01477     newbody +=                 "--\n";
01478     bodyData = KMail::Util::byteArrayFromQCStringNoDetach( newbody );
01479     mOldBodyPart.setBodyEncodedBinary( bodyData );
01480 
01481     mSaveBoundary = tmpCT.Boundary();
01482   }
01483 
01484   // Prepare attachments that will be signed/encrypted
01485   for ( TQValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01486     // signed/encrypted body parts must be either QP or base64 encoded
01487     // Why not 7 bit? Because the LF->CRLF canonicalization would render
01488     // e.g. 7 bit encoded shell scripts unusable because of the CRs.
01489     //
01490     // (marc) this is a workaround for the KMail bug that doesn't
01491     // respect the CRLF->LF de-canonicalisation. We should
01492     // eventually get rid of this:
01493     if( it->sign || it->encrypt ) {
01494       TQCString cte = it->part->cteStr().lower();
01495       if( ( "8bit" == cte && it->part->type() != DwMime::kTypeMessage )
01496           || ( ( it->part->type() == DwMime::kTypeText )
01497                && ( "7bit" == cte ) ) ) {
01498         const TQByteArray body = it->part->bodyDecodedBinary();
01499         TQValueList<int> dummy;
01500         it->part->setBodyAndGuessCte(body, dummy, false, it->sign);
01501         kdDebug(5006) << "Changed encoding of message part from "
01502                       << cte << " to " << it->part->cteStr() << endl;
01503       }
01504     }
01505   }
01506 
01507   if( mEarlyAddAttachments ) {
01508     // add the normal body text
01509     KMMessagePart innerBodyPart;
01510     if ( mIsRichText ) {
01511       innerBodyPart.setTypeStr(   "multipart");//text" );
01512       innerBodyPart.setSubtypeStr("alternative");//html");
01513     }
01514     else {
01515       innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01516     }
01517     innerBodyPart.setContentDisposition( "inline" );
01518     TQValueList<int> allowedCTEs;
01519     // the signed body must not be 8bit encoded
01520     innerBodyPart.setBodyAndGuessCte( bodyData, allowedCTEs,
01521                                       !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01522                                       doSign );
01523     if ( !mIsRichText )
01524       innerBodyPart.setCharset( mCharset );
01525     innerBodyPart.setBodyEncodedBinary( bodyData ); // do we need this, since setBodyAndGuessCte does this already?
01526     DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
01527     innerDwPart->Assemble();
01528     TQByteArray tmpbody = KMail::Util::ByteArray( innerDwPart->AsString() );
01529     if ( mIsRichText ) { // and add our mp/a boundary
01530         int boundPos = tmpbody.find( '\n' );
01531         if( -1 < boundPos ) {
01532           TQCString bStr( ";\n  boundary=\"" );
01533           bStr += mSaveBoundary.c_str();
01534           bStr += "\"";
01535           bodyData = tmpbody;
01536           KMail::Util::insert( bodyData, boundPos, bStr );
01537           KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
01538         }
01539     }
01540     else {
01541       bodyData = tmpbody;
01542       KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
01543     }
01544     delete innerDwPart;
01545     innerDwPart = 0;
01546     // add all matching Attachments
01547     // NOTE: This code will be changed when KMime is complete.
01548     for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01549       if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
01550         innerDwPart = theMessage.createDWBodyPart( it->part );
01551         innerDwPart->Assemble();
01552         KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "\n" ) );
01553         KMail::Util::append( bodyData, innerDwPart->AsString().c_str() );
01554         delete innerDwPart;
01555         innerDwPart = 0;
01556       }
01557     }
01558     KMail::Util::append( bodyData, TQCString( "\n--" + mMultipartMixedBoundary + "--\n" ) );
01559   } else { // !earlyAddAttachments
01560     TQValueList<int> allowedCTEs;
01561     // the signed body must not be 8bit encoded
01562     mOldBodyPart.setBodyAndGuessCte(bodyData, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01563                                     doSign);
01564     if ( !mIsRichText )
01565       mOldBodyPart.setCharset(mCharset);
01566   }
01567   // create S/MIME body part for signing and/or encrypting
01568   mOldBodyPart.setBodyEncodedBinary( bodyData );
01569 
01570   if( doSignBody || doEncryptBody ) {
01571     // get string representation of body part (including the attachments)
01572 
01573     DwBodyPart* dwPart;
01574     if ( mIsRichText && !mEarlyAddAttachments ) {
01575       // if we are using richtext and not already have a mp/a body
01576       // make the body a mp/a body
01577       dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01578       DwHeaders& headers = dwPart->Headers();
01579       DwMediaType& ct = headers.ContentType();
01580       ct.SetBoundary(mSaveBoundary);
01581       dwPart->Assemble();
01582     }
01583     else {
01584       dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01585       dwPart->Assemble();
01586     }
01587     mEncodedBody = KMail::Util::ByteArray( dwPart->AsString() );
01588     delete dwPart;
01589     dwPart = 0;
01590 
01591     // manually add a boundary definition to the Content-Type header
01592     if( !mMultipartMixedBoundary.isEmpty() ) {
01593       int boundPos = mEncodedBody.find( '\n' );
01594       if( -1 < boundPos ) {
01595         // insert new "boundary" parameter
01596         TQCString bStr( ";\n  boundary=\"" );
01597         bStr += mMultipartMixedBoundary;
01598         bStr += "\"";
01599         KMail::Util::insert( mEncodedBody, boundPos, bStr.data() );
01600       }
01601     }
01602 
01603     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01604     // according to RfC 2633, 3.1.1 Canonicalization
01605     //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01606     mEncodedBody = KMail::Util::lf2crlf( mEncodedBody );
01607   }
01608 
01609   if ( doSignBody ) {
01610     mPerformingSignOperation = true;  // this lets the KMComposeWin know if it is safe to close the window.
01611     pgpSignedMsg( mEncodedBody, format );
01612     mPerformingSignOperation = false;
01613 
01614     if ( mSignature.isEmpty() ) {
01615       kdDebug() << "signature was empty" << endl;
01616       mRc = false;
01617       return;
01618     }
01619     mRc = processStructuringInfo( TQString::null,
01620                   mOldBodyPart.contentDescription(),
01621                   mOldBodyPart.typeStr(),
01622                   mOldBodyPart.subtypeStr(),
01623                   mOldBodyPart.contentDisposition(),
01624                   mOldBodyPart.contentTransferEncodingStr(),
01625                   mEncodedBody, "signature",
01626                   mSignature,
01627                   *mNewBodyPart, true, format );
01628     if ( mRc ) {
01629       if ( !makeMultiPartSigned( format ) ) {
01630     mNewBodyPart->setCharset( mCharset );
01631       }
01632     } else
01633       KMessageBox::sorry( mComposeWin,
01634               mErrorProcessingStructuringInfo );
01635   }
01636 
01637   if ( !mRc )
01638     return;
01639 
01640   continueComposeMessage( theMessage, doSign, doEncrypt, format );
01641 }
01642 
01643 // Do the encryption stuff
01644 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
01645                                               bool doSign, bool doEncrypt,
01646                                               Kleo::CryptoMessageFormat format )
01647 {
01648 
01649   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01650     = mKeyResolver->encryptionItems( format );
01651   kdWarning( splitInfos.empty() )
01652     << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
01653     << Kleo::cryptoMessageFormatToString( format ) << endl;
01654 
01655   if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
01656     mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
01657     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
01658                          Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
01659                          false, mEncodedBody,
01660                          mPreviousBoundaryLevel,
01661                          /*mOldBodyPart,*/ mNewBodyPart,
01662                          format, this ) );
01663   }
01664 
01665   for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01666     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
01667                          doEncrypt, mEncodedBody,
01668                          mPreviousBoundaryLevel,
01669                          /*mOldBodyPart,*/ mNewBodyPart,
01670                          format, this ) );
01671 }
01672 
01673 void MessageComposer::encryptMessage( KMMessage* msg,
01674                       const Kleo::KeyResolver::SplitInfo & splitInfo,
01675                                       bool doSign, bool doEncrypt,
01676                                       KMMessagePart newBodyPart,
01677                       Kleo::CryptoMessageFormat format )
01678 {
01679   if ( doEncrypt && splitInfo.keys.empty() ) {
01680     // the user wants to send the message unencrypted
01681     //mComposeWin->setEncryption( false, false );
01682     //FIXME why is this talkback needed? Till
01683     doEncrypt = false;
01684   }
01685 
01686   const bool doEncryptBody = doEncrypt && mEncryptBody;
01687   const bool doSignBody = doSign && mSignBody;
01688 
01689   if ( doEncryptBody ) {
01690     TQByteArray innerContent;
01691     if ( doSignBody ) {
01692       // extract signed body from newBodyPart
01693       DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
01694       dwPart->Assemble();
01695       innerContent = KMail::Util::ByteArray( dwPart->AsString() );
01696       delete dwPart;
01697       dwPart = 0;
01698     } else {
01699       innerContent = mEncodedBody;
01700     }
01701 
01702     // now do the encrypting:
01703     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01704     // according to RfC 2633, 3.1.1 Canonicalization
01705     //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01706     innerContent = KMail::Util::lf2crlf( innerContent );
01707     //kdDebug(5006) << "                                                       done." << endl;
01708 
01709     TQByteArray encryptedBody;
01710     Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
01711                                            splitInfo.keys, format );
01712     if ( result != Kpgp::Ok ) {
01713       mRc = false;
01714       return;
01715     }
01716     mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01717                   newBodyPart.contentDescription(),
01718                   newBodyPart.typeStr(),
01719                   newBodyPart.subtypeStr(),
01720                   newBodyPart.contentDisposition(),
01721                   newBodyPart.contentTransferEncodingStr(),
01722                   innerContent,
01723                   "encrypted data",
01724                   encryptedBody,
01725                   newBodyPart, false, format );
01726     if ( !mRc )
01727       KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
01728   }
01729 
01730   // process the attachments that are not included into the body
01731   if( mRc ) {
01732     const bool useNewBodyPart = doSignBody || doEncryptBody;
01733     addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
01734       useNewBodyPart ? newBodyPart : mOldBodyPart, format );
01735   }
01736 }
01737 
01738 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
01739                                              const Kleo::KeyResolver::SplitInfo & splitInfo,
01740                                              bool doSign, bool doEncrypt,
01741                                              const KMMessagePart& ourFineBodyPart,
01742                                              Kleo::CryptoMessageFormat format )
01743 {
01744   const bool doEncryptBody = doEncrypt && mEncryptBody;
01745   const bool doSignBody = doSign && mSignBody;
01746 
01747   if( !mAttachments.empty()
01748       && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
01749     // set the content type header
01750     msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
01751     msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
01752     msg->headers().ContentType().CreateBoundary( 0 );
01753     kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
01754 
01755     // add our Body Part
01756     DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
01757     DwHeaders& headers = tmpDwPart->Headers();
01758     DwMediaType& ct = headers.ContentType();
01759     if ( !mSaveBoundary.empty() )
01760       ct.SetBoundary(mSaveBoundary);
01761     tmpDwPart->Assemble();
01762 
01763     //KMMessagePart newPart;
01764     //newPart.setBody(tmpDwPart->AsString().c_str());
01765     msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain
01766 
01767     // add Attachments
01768     // create additional bodyparts for the attachments (if any)
01769     KMMessagePart newAttachPart;
01770     for ( TQValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01771 
01772       const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
01773 
01774       if ( !cryptFlagsDifferent && mEarlyAddAttachments )
01775         continue;
01776 
01777       const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
01778       const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
01779 
01780       if ( !encryptThisNow && !signThisNow ) {
01781         msg->addBodyPart( it->part );
01782         // Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
01783         (void)msg->asDwMessage();
01784         continue;
01785       }
01786 
01787       KMMessagePart& rEncryptMessagePart( *it->part );
01788 
01789       DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
01790       innerDwPart->Assemble();
01791       TQByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
01792       delete innerDwPart;
01793       innerDwPart = 0;
01794 
01795       // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01796       // according to RfC 2633, 3.1.1 Canonicalization
01797       //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01798       encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
01799 
01800       // sign this attachment
01801       if( signThisNow ) {
01802         pgpSignedMsg( encodedAttachment, format );
01803         mRc = !mSignature.isEmpty();
01804         if( mRc ) {
01805           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01806                                         it->part->contentDescription(),
01807                                         it->part->typeStr(),
01808                                         it->part->subtypeStr(),
01809                                         it->part->contentDisposition(),
01810                                         it->part->contentTransferEncodingStr(),
01811                                         encodedAttachment,
01812                                         "signature",
01813                                         mSignature,
01814                                         newAttachPart, true, format );
01815           if( mRc ) {
01816             if( encryptThisNow ) {
01817               rEncryptMessagePart = newAttachPart;
01818               DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
01819               dwPart->Assemble();
01820               encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
01821               delete dwPart;
01822               dwPart = 0;
01823             }
01824           } else
01825             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01826         } else {
01827           // quit the attachments' loop
01828           break;
01829         }
01830       }
01831       if( encryptThisNow ) {
01832         TQByteArray encryptedBody;
01833         Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
01834                                                encodedAttachment,
01835                                                splitInfo.keys,
01836                                                format );
01837 
01838         if( Kpgp::Ok == result ) {
01839           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01840                                         rEncryptMessagePart.contentDescription(),
01841                                         rEncryptMessagePart.typeStr(),
01842                                         rEncryptMessagePart.subtypeStr(),
01843                                         rEncryptMessagePart.contentDisposition(),
01844                                         rEncryptMessagePart.contentTransferEncodingStr(),
01845                                         encodedAttachment,
01846                                         "encrypted data",
01847                                         encryptedBody,
01848                                         newAttachPart, false, format );
01849           if ( !mRc )
01850             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01851         } else
01852           mRc = false;
01853       }
01854       msg->addBodyPart( &newAttachPart );
01855       (void)msg->asDwMessage(); // Assemble the message. One gets a completely empty message otherwise :/
01856     }
01857   } else { // no attachments in the final message
01858     if( ourFineBodyPart.originalContentTypeStr() ) {
01859       msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
01860       msg->headers().ContentType().Parse();
01861       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
01862     } else {
01863       TQCString ct = ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr();
01864       if ( ct == "multipart/mixed" )
01865         ct += ";\n\tboundary=\"" + mMultipartMixedBoundary + '"';
01866       else if ( ct == "multipart/alternative" )
01867         ct += ";\n\tboundary=\"" + TQCString(mSaveBoundary.c_str()) + '"';
01868       msg->headers().ContentType().FromString( ct );
01869       msg->headers().ContentType().Parse();
01870       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
01871     }
01872     if ( !ourFineBodyPart.charset().isEmpty() )
01873       msg->setCharset( ourFineBodyPart.charset() );
01874     msg->setHeaderField( "Content-Transfer-Encoding",
01875                          ourFineBodyPart.contentTransferEncodingStr() );
01876     msg->setHeaderField( "Content-Description",
01877                          ourFineBodyPart.contentDescription() );
01878     msg->setHeaderField( "Content-Disposition",
01879                          ourFineBodyPart.contentDisposition() );
01880 
01881     if ( mDebugComposerCrypto )
01882       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
01883 
01884     // set body content
01885     msg->setBody( ourFineBodyPart.dwBody() );
01886 
01887   }
01888 
01889   msg->setHeaderField( "X-KMail-Recipients",
01890                        splitInfo.recipients.join(", "), KMMessage::Address );
01891 
01892   if ( mDebugComposerCrypto ) {
01893     kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n      Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
01894     msg->headers().Assemble();
01895     kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n      Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
01896   }
01897 }
01898 
01899 //-----------------------------------------------------------------------------
01900 // This method does not call any crypto ops, so it does not need to be async
01901 bool MessageComposer::processStructuringInfo( const TQString bugURL,
01902                                               const TQString contentDescClear,
01903                                               const TQCString contentTypeClear,
01904                                               const TQCString contentSubtypeClear,
01905                                               const TQCString contentDispClear,
01906                                               const TQCString contentTEncClear,
01907                                               const TQByteArray& clearCStr,
01908                                               const TQString /*contentDescCiph*/,
01909                                               const TQByteArray& ciphertext,
01910                                               KMMessagePart& resultingPart,
01911                           bool signing, Kleo::CryptoMessageFormat format )
01912 {
01913   assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' ); // I was called with a TQCString !?
01914   bool bOk = true;
01915 
01916   if ( makeMimeObject( format, signing ) ) {
01917     TQCString mainHeader = "Content-Type: ";
01918     const char * toplevelCT = toplevelContentType( format, signing );
01919     if ( toplevelCT )
01920       mainHeader += toplevelCT;
01921     else {
01922       if( makeMultiMime( format, signing ) )
01923         mainHeader += "text/plain";
01924       else
01925         mainHeader += contentTypeClear + '/' + contentSubtypeClear;
01926     }
01927 
01928     const TQCString boundaryCStr = KMime::multiPartBoundary();
01929     // add "boundary" parameter
01930     if ( makeMultiMime( format, signing ) )
01931       mainHeader.replace( "%boundary", boundaryCStr );
01932 
01933     if ( toplevelCT ) {
01934       if ( const char * str = toplevelContentDisposition( format, signing ) ) {
01935         mainHeader += "\nContent-Disposition: ";
01936         mainHeader += str;
01937       }
01938       if ( !makeMultiMime( format, signing ) &&
01939        binaryHint( format ) )
01940         mainHeader += "\nContent-Transfer-Encoding: base64";
01941     } else {
01942       if( 0 < contentDispClear.length() ) {
01943         mainHeader += "\nContent-Disposition: ";
01944         mainHeader += contentDispClear;
01945       }
01946       if( 0 < contentTEncClear.length() ) {
01947         mainHeader += "\nContent-Transfer-Encoding: ";
01948         mainHeader += contentTEncClear;
01949       }
01950     }
01951 
01952     //kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
01953 
01954     DwString mainDwStr;
01955     mainDwStr = mainHeader + "\n\n";
01956     DwBodyPart mainDwPa( mainDwStr, 0 );
01957     mainDwPa.Parse();
01958     KMMessage::bodyPart( &mainDwPa, &resultingPart );
01959     if( !makeMultiMime( format, signing ) ) {
01960       if ( signing && includeCleartextWhenSigning( format ) ) {
01961         TQByteArray bodyText( clearCStr );
01962         KMail::Util::append( bodyText, "\n" );
01963         KMail::Util::append( bodyText, ciphertext );
01964         resultingPart.setBodyEncodedBinary( bodyText );
01965       } else {
01966         resultingPart.setBodyEncodedBinary( ciphertext );
01967       }
01968     } else {
01969       // Build the encapsulated MIME parts.
01970       // Build a MIME part holding the version information
01971       // taking the body contents returned in
01972       // structuring.data.bodyTextVersion.
01973       TQCString versCStr, codeCStr;
01974       if ( !signing && format == Kleo::OpenPGPMIMEFormat )
01975         versCStr =
01976       "Content-Type: application/pgp-encrypted\n"
01977       "Content-Disposition: attachment\n"
01978       "\n"
01979       "Version: 1";
01980 
01981       // Build a MIME part holding the code information
01982       // taking the body contents returned in ciphertext.
01983       const char * nestedCT = nestedContentType( format, signing );
01984       assert( nestedCT );
01985       codeCStr = "Content-Type: ";
01986       codeCStr += nestedCT;
01987       codeCStr += '\n';
01988       if ( const char * str = nestedContentDisposition( format, signing ) ) {
01989     codeCStr += "Content-Disposition: ";
01990     codeCStr += str;
01991     codeCStr += '\n';
01992       }
01993       if ( binaryHint( format ) ) {
01994     codeCStr += "Content-Transfer-Encoding: base64\n\n";
01995     codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext );
01996       } else
01997     codeCStr += '\n' + TQCString( ciphertext.data(), ciphertext.size() + 1 );
01998 
01999 
02000       TQByteArray mainStr;
02001       KMail::Util::append( mainStr, "--" );
02002       KMail::Util::append( mainStr, boundaryCStr );
02003       if ( signing && includeCleartextWhenSigning( format ) &&
02004        !clearCStr.isEmpty() ) {
02005         KMail::Util::append( mainStr, "\n" );
02006         // clearCStr is the one that can be very big for large attachments, don't merge with the other lines
02007         KMail::Util::append( mainStr, clearCStr );
02008         KMail::Util::append( mainStr, "\n--" + boundaryCStr );
02009       }
02010       if ( !versCStr.isEmpty() )
02011         KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
02012       if( !codeCStr.isEmpty() )
02013         KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
02014       KMail::Util::append( mainStr, "--\n" );
02015 
02016       //kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
02017       resultingPart.setBodyEncodedBinary( mainStr );
02018     }
02019 
02020   } else { //  not making a mime object, build a plain message body.
02021 
02022     resultingPart.setContentDescription( contentDescClear );
02023     resultingPart.setTypeStr( contentTypeClear );
02024     resultingPart.setSubtypeStr( contentSubtypeClear );
02025     resultingPart.setContentDisposition( contentDispClear );
02026     resultingPart.setContentTransferEncodingStr( contentTEncClear );
02027     TQByteArray resultingBody;
02028 
02029     if ( signing && includeCleartextWhenSigning( format ) ) {
02030       if( !clearCStr.isEmpty() )
02031         KMail::Util::append( resultingBody, clearCStr );
02032     }
02033     if ( !ciphertext.isEmpty() )
02034       KMail::Util::append( resultingBody, ciphertext );
02035     else {
02036       // Plugin error!
02037       KMessageBox::sorry( mComposeWin,
02038                           i18n( "<qt><p>Error: The backend did not return "
02039                                 "any encoded data.</p>"
02040                                 "<p>Please report this bug:<br>%2</p></qt>" )
02041                           .arg( bugURL ) );
02042       bOk = false;
02043     }
02044     resultingPart.setBodyEncodedBinary( resultingBody );
02045   }
02046 
02047   return bOk;
02048 }
02049 
02050 //-----------------------------------------------------------------------------
02051 TQCString MessageComposer::plainTextFromMarkup( const TQString& markupText )
02052 {
02053   TQTextEdit *hackConspiratorTextEdit = new TQTextEdit( markupText );
02054   hackConspiratorTextEdit->setTextFormat(Qt::PlainText);
02055   if ( !mDisableBreaking ) {
02056     hackConspiratorTextEdit->setWordWrap( TQTextEdit::FixedColumnWidth );
02057     hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
02058   }
02059   TQString text = hackConspiratorTextEdit->text();
02060   TQCString textbody;
02061 
02062   const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
02063   if( mCharset == "us-ascii" ) {
02064     textbody = KMMsgBase::toUsAscii( text );
02065   } else if( codec == 0 ) {
02066     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02067     textbody = text.local8Bit();
02068   } else {
02069     text = codec->toUnicode( text.latin1(), text.length() );
02070     textbody = codec->fromUnicode( text );
02071   }
02072   if (textbody.isNull()) textbody = "";
02073 
02074   delete hackConspiratorTextEdit;
02075   return textbody;
02076 }
02077 
02078 //-----------------------------------------------------------------------------
02079 TQByteArray MessageComposer::breakLinesAndApplyCodec()
02080 {
02081   TQString text;
02082   TQCString cText;
02083 
02084   if( mDisableBreaking || mIsRichText || !GlobalSettings::self()->wordWrap() )
02085     text = mComposeWin->mEditor->text();
02086   else
02087     text = mComposeWin->mEditor->brokenText();
02088   text.truncate( text.length() ); // to ensure text.size()==text.length()+1
02089 
02090   TQString newText;
02091   const TQTextCodec *codec = KMMsgBase::codecForName( mCharset );
02092 
02093   if( mCharset == "us-ascii" ) {
02094     cText = KMMsgBase::toUsAscii( text );
02095     newText = TQString::fromLatin1( cText );
02096   } else if( codec == 0 ) {
02097     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02098     cText = text.local8Bit();
02099     newText = TQString::fromLocal8Bit( cText );
02100   } else {
02101     cText = codec->fromUnicode( text );
02102     newText = codec->toUnicode( cText );
02103   }
02104   if (cText.isNull()) cText = "";
02105 
02106   if( !text.isEmpty() && (newText != text) ) {
02107     TQString oldText = mComposeWin->mEditor->text();
02108     mComposeWin->mEditor->setText( newText );
02109     KCursorSaver idle( KBusyPtr::idle() );
02110     bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
02111                                                i18n("<qt>Not all characters fit into the chosen"
02112                                                     " encoding.<br><br>Send the message anyway?</qt>"),
02113                                                i18n("Some Characters Will Be Lost"),
02114                                                i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
02115     if( !anyway ) {
02116       mComposeWin->mEditor->setText(oldText);
02117       return TQByteArray();
02118     }
02119   }
02120 
02121   // From RFC 3156:
02122   //  Note: The accepted OpenPGP convention is for signed data to end
02123   //  with a <CR><LF> sequence.  Note that the <CR><LF> sequence
02124   //  immediately preceding a MIME boundary delimiter line is considered
02125   //  to be part of the delimiter in [3], 5.1.  Thus, it is not part of
02126   //  the signed data preceding the delimiter line.  An implementation
02127   //  which elects to adhere to the OpenPGP convention has to make sure
02128   //  it inserts a <CR><LF> pair on the last line of the data to be
02129   //  signed and transmitted (signed message and transmitted message
02130   //  MUST be identical).
02131   // So make sure that the body ends with a <LF>.
02132   if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
02133     kdDebug(5006) << "Added an <LF> on the last line" << endl;
02134     cText += "\n";
02135   }
02136   return KMail::Util::byteArrayFromQCStringNoDetach( cText );
02137 }
02138 
02139 
02140 //-----------------------------------------------------------------------------
02141 void MessageComposer::pgpSignedMsg( const TQByteArray& cText, Kleo::CryptoMessageFormat format ) {
02142 
02143   assert( cText.isEmpty() || cText[cText.size()-1] != '\0' ); // I was called with a TQCString !?
02144   mSignature = TQByteArray();
02145 
02146   const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
02147   if ( signingKeys.empty() ) {
02148     KMessageBox::sorry( mComposeWin,
02149             i18n("This message could not be signed, "
02150                  "since no valid signing keys have been found; "
02151                  "this should actually never happen, "
02152                  "please report this bug.") );
02153     return;
02154   }
02155 
02156   // TODO: ASync call? Likely, yes :-)
02157   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02158   assert( cpf );
02159   const Kleo::CryptoBackend::Protocol * proto
02160     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02161   assert( proto ); 
02162 
02163   std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
02164                             textMode( format ) ) );
02165 
02166   if ( !job.get() ) {
02167     KMessageBox::sorry( mComposeWin,
02168             i18n("This message could not be signed, "
02169                  "since the chosen backend does not seem to support "
02170                  "signing; this should actually never happen, "
02171                  "please report this bug.") );
02172     return;
02173   }
02174 
02175   TQByteArray signature;
02176   const GpgME::SigningResult res =
02177     job->exec( signingKeys, cText, signingMode( format ), signature );
02178   {
02179       std::stringstream ss;
02180       ss << res;
02181       kdDebug(5006) << ss.str().c_str() << endl;
02182   }
02183   if ( res.error().isCanceled() ) {
02184     kdDebug() << "signing was canceled by user" << endl;
02185     return;
02186   }
02187   if ( res.error() ) {
02188     kdDebug() << "signing failed: " << res.error().asString() << endl;
02189     job->showErrorDialog( mComposeWin );
02190     return;
02191   }
02192 
02193   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02194       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02195       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Signing Operation") );
02196 
02197   mSignature = signature;
02198   if ( mSignature.isEmpty() ) {
02199     KMessageBox::sorry( mComposeWin,
02200                         i18n( "The signing operation failed. "
02201                               "Please make sure that the gpg-agent program "
02202                               "is running." ) );
02203   }
02204 }
02205 
02206 //-----------------------------------------------------------------------------
02207 Kpgp::Result MessageComposer::pgpEncryptedMsg( TQByteArray & encryptedBody,
02208                                                const TQByteArray& cText,
02209                                                const std::vector<GpgME::Key> & encryptionKeys,
02210                            Kleo::CryptoMessageFormat format )
02211 {
02212   // TODO: ASync call? Likely, yes :-)
02213   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02214   assert( cpf );
02215   const Kleo::CryptoBackend::Protocol * proto
02216     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02217   assert( proto ); // hmmmm....?
02218 
02219   std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
02220                                                           textMode( format ) ) );
02221   if ( !job.get() ) {
02222     KMessageBox::sorry( mComposeWin,
02223                         i18n("This message could not be encrypted, "
02224                              "since the chosen backend does not seem to support "
02225                              "encryption; this should actually never happen, "
02226                              "please report this bug.") );
02227     return Kpgp::Failure;
02228   }
02229 
02230   const GpgME::EncryptionResult res =
02231     job->exec( encryptionKeys, cText, true /* we do ownertrust ourselves */, encryptedBody );
02232   {
02233       std::stringstream ss;
02234       ss << res;
02235       kdDebug(5006) << ss.str().c_str() << endl;
02236   }
02237   if ( res.error().isCanceled() ) {
02238     kdDebug() << "encryption was canceled by user" << endl;
02239     return Kpgp::Canceled;
02240   }
02241   if ( res.error() ) {
02242     kdDebug() << "encryption failed: " << res.error().asString() << endl;
02243     job->showErrorDialog( mComposeWin );
02244     return Kpgp::Failure;
02245   }
02246 
02247   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02248       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02249       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
02250 
02251   return Kpgp::Ok;
02252 }
02253 
02254 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( TQByteArray & encryptedBody,
02255                             const TQByteArray& cText,
02256                             const std::vector<GpgME::Key> & signingKeys,
02257                             const std::vector<GpgME::Key> & encryptionKeys,
02258                             Kleo::CryptoMessageFormat format )
02259 {
02260   // TODO: ASync call? Likely, yes :-)
02261   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02262   assert( cpf );
02263   const Kleo::CryptoBackend::Protocol * proto
02264     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02265   assert( proto ); // hmmmm....?
02266 
02267   std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
02268                                   textMode( format ) ) );
02269   if ( !job.get() ) {
02270     KMessageBox::sorry( mComposeWin,
02271             i18n("This message could not be signed and encrypted, "
02272                  "since the chosen backend does not seem to support "
02273                  "combined signing and encryption; this should actually never happen, "
02274                  "please report this bug.") );
02275     return Kpgp::Failure;
02276   }
02277 
02278   const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
02279     job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
02280   {
02281       std::stringstream ss;
02282       ss << res.first << '\n' << res.second;
02283       kdDebug(5006) << ss.str().c_str() << endl;
02284   }
02285   if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
02286     kdDebug() << "encrypt/sign was canceled by user" << endl;
02287     return Kpgp::Canceled;
02288   }
02289   if ( res.first.error() || res.second.error() ) {
02290     if ( res.first.error() )
02291       kdDebug() << "signing failed: " << res.first.error().asString() << endl;
02292     else
02293       kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
02294     job->showErrorDialog( mComposeWin );
02295     return Kpgp::Failure;
02296   }
02297 
02298   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02299       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02300       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
02301 
02302   return Kpgp::Ok;
02303 }
02304 
02305 
02306 #include "messagecomposer.moc"