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 <qfile.h>
00078 #include <qtextcodec.h>
00079 #include <qtextedit.h>
00080 #include <qtimer.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 QString 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 QString 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   : QObject( 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     QCString 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   QTimer::singleShot( 0, this, SLOT( slotDoNextJob() ) );
00377 }
00378 
00379 void MessageComposer::emitDone( bool b )
00380 {
00381   // Save memory - before sending the mail
00382   mEncodedBody = QByteArray();
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     QCString 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", QString::number( id.uoid() ));
00479 
00480   QString 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( QString::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 QCString escape_quoted_string( const QCString & str ) {
00535   QCString 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 QByteArray& body,
00553                                            QByteArray& resultData )
00554 {
00555   std::auto_ptr<Kleo::SpecialJob> job( chiasmus->specialJob( "x-encrypt", QMap<QString,QVariant>() ) );
00556   if ( !job.get() ) {
00557     const QString 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 QString 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 QVariant result = job->property( "result" );
00577   if ( result.type() != QVariant::ByteArray ) {
00578     const QString 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 ( QValueVector<Attachment>::iterator it = mAttachments.begin(), end = mAttachments.end() ; it != end ; ++it ) {
00600     KMMessagePart * part = it->part;
00601     const QString filename = part->fileName();
00602     if ( filename.endsWith( ".xia", false ) )
00603       continue; // already encrypted
00604     const QByteArray body = part->bodyDecodedBinary();
00605     QByteArray 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     QValueList<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     // this is taken from kmmsgpartdlg.cpp:
00617     QCString encoding = KMMsgBase::autoDetectCharset( part->charset(), KMMessage::preferredCharsets(), filename );
00618     if ( encoding.isEmpty() )
00619       encoding = "utf-8";
00620     const QCString enc_name = KMMsgBase::encodeRFC2231String( filename + ".xia", encoding );
00621     const QCString cDisp = "attachment;\n\tfilename"
00622                            + ( QString( enc_name ) != filename + ".xia"
00623                                ? "*=" + enc_name
00624                                : "=\"" + escape_quoted_string( enc_name ) + '\"' );
00625     part->setContentDisposition( cDisp );
00626   }
00627 }
00628 
00629 void MessageComposer::adjustCryptFlags()
00630 {
00631   if ( !mDisableCrypto &&
00632        mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat &&
00633        !mAttachments.empty() &&
00634        ( mSigningRequested || mEncryptionRequested ) )
00635   {
00636     int ret;
00637     if ( mAllowedCryptoMessageFormats == Kleo::InlineOpenPGPFormat ) {
00638       ret = KMessageBox::warningYesNoCancel( mComposeWin,
00639                                              i18n("The inline OpenPGP crypto message format "
00640                                                   "does not support encryption or signing "
00641                                                   "of attachments.\n"
00642                                                   "Really use deprecated inline OpenPGP?"),
00643                                              i18n("Insecure Message Format"),
00644                                              i18n("Use Inline OpenPGP"),
00645                                              i18n("Use OpenPGP/MIME") );
00646     }
00647     else {
00648       // if other crypto message formats are allowed then simply don't use
00649       // inline OpenPGP
00650       ret = KMessageBox::No;
00651     }
00652 
00653     if ( ret == KMessageBox::Cancel ) {
00654       mRc = false;
00655       return;
00656     } else if ( ret == KMessageBox::No ) {
00657       mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat;
00658       mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat;
00659       if ( mSigningRequested ) {
00660         // The composer window disabled signing on the attachments, re-enable it
00661         for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00662           mAttachments[idx].sign = true;
00663       }
00664       if ( mEncryptionRequested ) {
00665         // The composer window disabled encrypting on the attachments, re-enable it
00666         // We assume this is what the user wants - after all he chose OpenPGP/MIME for this.
00667         for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx )
00668           mAttachments[idx].encrypt = true;
00669       }
00670     }
00671   }
00672 
00673   mKeyResolver =
00674     new Kleo::KeyResolver( encryptToSelf(), showKeyApprovalDialog(),
00675                mUseOpportunisticEncryption, mAllowedCryptoMessageFormats,
00676                encryptKeyNearExpiryWarningThresholdInDays(),
00677                signingKeyNearExpiryWarningThresholdInDays(),
00678                encryptRootCertNearExpiryWarningThresholdInDays(),
00679                signingRootCertNearExpiryWarningThresholdInDays(),
00680                encryptChainCertNearExpiryWarningThresholdInDays(),
00681                signingChainCertNearExpiryWarningThresholdInDays() );
00682 
00683   if ( !mDisableCrypto ) {
00684     const KPIM::Identity & id =
00685       kmkernel->identityManager()->identityForUoidOrDefault( mIdentityUid );
00686 
00687     QStringList encryptToSelfKeys;
00688     if ( !id.pgpEncryptionKey().isEmpty() )
00689       encryptToSelfKeys.push_back( id.pgpEncryptionKey() );
00690     if ( !id.smimeEncryptionKey().isEmpty() )
00691       encryptToSelfKeys.push_back( id.smimeEncryptionKey() );
00692     if ( mKeyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) {
00693       mRc = false;
00694       return;
00695     }
00696 
00697     QStringList signKeys;
00698     if ( !id.pgpSigningKey().isEmpty() )
00699       signKeys.push_back( mPGPSigningKey = id.pgpSigningKey() );
00700     if ( !id.smimeSigningKey().isEmpty() )
00701       signKeys.push_back( mSMIMESigningKey = id.smimeSigningKey() );
00702     if ( mKeyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) {
00703       mRc = false;
00704       return;
00705     }
00706   }
00707 
00708   mKeyResolver->setPrimaryRecipients( mTo + mCc );
00709   mKeyResolver->setSecondaryRecipients( mBccList );
00710 
00711   // check settings of composer buttons *and* attachment check boxes
00712   bool doSignCompletely    = mSigningRequested;
00713   bool doEncryptCompletely = mEncryptionRequested;
00714   for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) {
00715     if ( mAttachments[idx].encrypt )
00716       mEncryptionRequested = true;
00717     else
00718       doEncryptCompletely = false;
00719     if ( mAttachments[idx].sign )
00720       mSigningRequested = true;
00721     else
00722       doSignCompletely = false;
00723   }
00724 
00725   mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely );
00726 
00727   if ( !mRc )
00728     return;
00729 
00730   mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely );
00731 
00732   if ( !mRc )
00733     return;
00734 
00735   // resolveAllKeys needs to run even if mDisableCrypto == true, since
00736   // we depend on it collecting all recipients into one dummy
00737   // SplitInfo to avoid special-casing all over the place:
00738   if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok )
00739     mRc = false;
00740 }
00741 
00742 bool MessageComposer::determineWhetherToSign( bool doSignCompletely ) {
00743   bool sign = false;
00744   switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) {
00745   case Kleo::DoIt:
00746     if ( !mSigningRequested ) {
00747       markAllAttachmentsForSigning( true );
00748       return true;
00749     }
00750     sign = true;
00751     break;
00752   case Kleo::DontDoIt:
00753     sign = false;
00754     break;
00755   case Kleo::AskOpportunistic:
00756     assert( 0 );
00757   case Kleo::Ask:
00758     {
00759       // the user wants to be asked or has to be asked
00760       const KCursorSaver idle( KBusyPtr::idle() );
00761       const QString msg = i18n("Examination of the recipient's signing preferences "
00762                    "yielded that you be asked whether or not to sign "
00763                    "this message.\n"
00764                    "Sign this message?");
00765       switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00766                          i18n("Sign Message?"),
00767                          i18n("to sign","&Sign"),
00768                          i18n("Do &Not Sign") ) ) {
00769       case KMessageBox::Cancel:
00770     mRc = false;
00771     return false;
00772       case KMessageBox::Yes:
00773     markAllAttachmentsForSigning( true );
00774     return true;
00775       case KMessageBox::No:
00776     markAllAttachmentsForSigning( false );
00777     return false;
00778       }
00779     }
00780     break;
00781   case Kleo::Conflict:
00782     {
00783       // warn the user that there are conflicting signing preferences
00784       const KCursorSaver idle( KBusyPtr::idle() );
00785       const QString msg = i18n("There are conflicting signing preferences "
00786                    "for these recipients.\n"
00787                    "Sign this message?");
00788       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00789                         i18n("Sign Message?"),
00790                         i18n("to sign","&Sign"),
00791                         i18n("Do &Not Sign") ) ) {
00792       case KMessageBox::Cancel:
00793     mRc = false;
00794     return false;
00795       case KMessageBox::Yes:
00796     markAllAttachmentsForSigning( true );
00797     return true;
00798       case KMessageBox::No:
00799     markAllAttachmentsForSigning( false );
00800     return false;
00801       }
00802     }
00803     break;
00804   case Kleo::Impossible:
00805     {
00806       const KCursorSaver idle( KBusyPtr::idle() );
00807       const QString msg = i18n("You have requested to sign this message, "
00808                    "but no valid signing keys have been configured "
00809                    "for this identity.");
00810       if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00811                            i18n("Send Unsigned?"),
00812                                                i18n("Send &Unsigned") )
00813        == KMessageBox::Cancel ) {
00814     mRc = false;
00815     return false;
00816       } else {
00817     markAllAttachmentsForSigning( false );
00818     return false;
00819       }
00820     }
00821   }
00822 
00823   if ( !sign || !doSignCompletely ) {
00824     if ( warnSendUnsigned() ) {
00825       const KCursorSaver idle( KBusyPtr::idle() );
00826       const QString msg = sign && !doSignCompletely
00827     ? i18n("Some parts of this message will not be signed.\n"
00828            "Sending only partially signed messages might violate site policy.\n"
00829            "Sign all parts instead?") // oh, I hate this...
00830     : i18n("This message will not be signed.\n"
00831            "Sending unsigned message might violate site policy.\n"
00832            "Sign message instead?") ; // oh, I hate this...
00833       const QString buttonText = sign && !doSignCompletely
00834     ? i18n("&Sign All Parts") : i18n("&Sign") ;
00835       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00836                         i18n("Unsigned-Message Warning"),
00837                         buttonText,
00838                         i18n("Send &As Is") ) ) {
00839       case KMessageBox::Cancel:
00840     mRc = false;
00841     return false;
00842       case KMessageBox::Yes:
00843     markAllAttachmentsForSigning( true );
00844     return true;
00845       case KMessageBox::No:
00846     return sign || doSignCompletely;
00847       }
00848     }
00849   }
00850 
00851   return sign || doSignCompletely ;
00852 }
00853 
00854 bool MessageComposer::determineWhetherToEncrypt( bool doEncryptCompletely ) {
00855   bool encrypt = false;
00856   bool opportunistic = false;
00857   switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) {
00858   case Kleo::DoIt:
00859     if ( !mEncryptionRequested ) {
00860       markAllAttachmentsForEncryption( true );
00861       return true;
00862     }
00863     encrypt = true;
00864     break;
00865   case Kleo::DontDoIt:
00866     encrypt = false;
00867     break;
00868   case Kleo::AskOpportunistic:
00869     opportunistic = true;
00870     // fall through...
00871   case Kleo::Ask:
00872     {
00873       // the user wants to be asked or has to be asked
00874       const KCursorSaver idle( KBusyPtr::idle() );
00875       const QString msg = opportunistic
00876     ? i18n("Valid trusted encryption keys were found for all recipients.\n"
00877            "Encrypt this message?")
00878     : i18n("Examination of the recipient's encryption preferences "
00879            "yielded that you be asked whether or not to encrypt "
00880            "this message.\n"
00881            "Encrypt this message?");
00882       switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg,
00883                          i18n("Encrypt Message?"),
00884                          mDoSign
00885                          ? i18n("Sign && &Encrypt")
00886                          : i18n("&Encrypt"),
00887                          mDoSign
00888                          ? i18n("&Sign Only")
00889                          : i18n("&Send As-Is") ) ) {
00890       case KMessageBox::Cancel:
00891     mRc = false;
00892     return false;
00893       case KMessageBox::Yes:
00894     markAllAttachmentsForEncryption( true );
00895     return true;
00896       case KMessageBox::No:
00897     markAllAttachmentsForEncryption( false );
00898     return false;
00899       }
00900     }
00901     break;
00902   case Kleo::Conflict:
00903     {
00904       // warn the user that there are conflicting encryption preferences
00905       const KCursorSaver idle( KBusyPtr::idle() );
00906       const QString msg = i18n("There are conflicting encryption preferences "
00907                    "for these recipients.\n"
00908                    "Encrypt this message?");
00909       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00910                         i18n("Encrypt Message?"),
00911                         i18n("&Encrypt"),
00912                         i18n("Do &Not Encrypt") ) ) {
00913       case KMessageBox::Cancel:
00914     mRc = false;
00915     return false;
00916       case KMessageBox::Yes:
00917     markAllAttachmentsForEncryption( true );
00918     return true;
00919       case KMessageBox::No:
00920     markAllAttachmentsForEncryption( false );
00921     return false;
00922       }
00923     }
00924     break;
00925   case Kleo::Impossible:
00926     {
00927       const KCursorSaver idle( KBusyPtr::idle() );
00928       const QString msg = i18n("You have requested to encrypt this message, "
00929                    "and to encrypt a copy to yourself, "
00930                    "but no valid trusted encryption keys have been "
00931                    "configured for this identity.");
00932       if ( KMessageBox::warningContinueCancel( mComposeWin, msg,
00933                            i18n("Send Unencrypted?"),
00934                                                i18n("Send &Unencrypted") )
00935        == KMessageBox::Cancel ) {
00936     mRc = false;
00937     return false;
00938       } else {
00939     markAllAttachmentsForEncryption( false );
00940     return false;
00941       }
00942     }
00943   }
00944 
00945   if ( !encrypt || !doEncryptCompletely ) {
00946     if ( warnSendUnencrypted() ) {
00947       const KCursorSaver idle( KBusyPtr::idle() );
00948       const QString msg = !doEncryptCompletely
00949     ? i18n("Some parts of this message will not be encrypted.\n"
00950            "Sending only partially encrypted messages might violate site policy "
00951            "and/or leak sensitive information.\n"
00952            "Encrypt all parts instead?") // oh, I hate this...
00953     : i18n("This message will not be encrypted.\n"
00954            "Sending unencrypted messages might violate site policy and/or "
00955            "leak sensitive information.\n"
00956            "Encrypt messages instead?") ; // oh, I hate this...
00957       const QString buttonText = !doEncryptCompletely
00958     ? i18n("&Encrypt All Parts") : i18n("&Encrypt") ;
00959       switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg,
00960                         i18n("Unencrypted Message Warning"),
00961                         buttonText,
00962                         mDoSign
00963                         ? i18n("&Sign Only")
00964                         : i18n("&Send As-Is") ) ) {
00965       case KMessageBox::Cancel:
00966     mRc = false;
00967     return false;
00968       case KMessageBox::Yes:
00969     markAllAttachmentsForEncryption( true );
00970     return true;
00971       case KMessageBox::No:
00972     return encrypt || doEncryptCompletely;
00973       }
00974     }
00975   }
00976 
00977   return encrypt || doEncryptCompletely ;
00978 }
00979 
00980 void MessageComposer::markAllAttachmentsForSigning( bool sign ) {
00981   mSignBody = sign;
00982   for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00983     it->sign = sign;
00984 }
00985 
00986 void MessageComposer::markAllAttachmentsForEncryption( bool enc ) {
00987   mEncryptBody = enc;
00988   for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it )
00989     it->encrypt = enc;
00990 }
00991 
00992 
00993 void MessageComposer::composeMessage()
00994 {
00995   for ( unsigned int i = 0 ; i < numConcreteCryptoMessageFormats ; ++i ) {
00996     if ( mKeyResolver->encryptionItems( concreteCryptoMessageFormats[i] ).empty() )
00997       continue;
00998     KMMessage * msg = new KMMessage( *mReferenceMessage );
00999     composeMessage( *msg, mDoSign, mDoEncrypt, concreteCryptoMessageFormats[i] );
01000     if ( !mRc )
01001       return;
01002   }
01003 }
01004 
01005 //
01006 // These are replacements for StructuringInfo(Wrapper):
01007 //
01008 
01009 // check whether to use multipart/{signed,encrypted}
01010 static inline bool makeMultiMime( Kleo::CryptoMessageFormat f, bool sign ) {
01011   switch ( f ) {
01012   default:
01013   case Kleo::InlineOpenPGPFormat:
01014   case Kleo::SMIMEOpaqueFormat:   return false;
01015   case Kleo::OpenPGPMIMEFormat:   return true;
01016   case Kleo::SMIMEFormat:         return sign; // only on sign - there's no mp/encrypted for S/MIME
01017   }
01018 }
01019 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) {
01020   return makeMultiMime( f, true );
01021 }
01022 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) {
01023   return makeMultiMime( f, false );
01024 }
01025 
01026 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool /*signing*/ ) {
01027   return f != Kleo::InlineOpenPGPFormat;
01028 }
01029 
01030 static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f, bool signing ) {
01031   switch ( f ) {
01032   default:
01033   case Kleo::InlineOpenPGPFormat: return 0;
01034   case Kleo::OpenPGPMIMEFormat:
01035     return signing ?
01036       "multipart/signed;\n\t"
01037       "boundary=\"%boundary\";\n\t"
01038       "protocol=\"application/pgp-signature\";\n\t"
01039       "micalg=pgp-sha1" // FIXME: obtain this parameter from gpgme!
01040       :
01041       "multipart/encrypted;\n\t"
01042       "boundary=\"%boundary\";\n\t"
01043       "protocol=\"application/pgp-encrypted\""
01044       ;
01045   case Kleo::SMIMEFormat:
01046     if ( signing )
01047       return
01048     "multipart/signed;\n\t"
01049     "boundary=\"%boundary\";\n\t"
01050     "protocol=\"application/pkcs7-signature\";\n\t"
01051     "micalg=sha1"; // FIXME: obtain this parameter from gpgme!
01052     // fall through (for encryption, there's no difference between
01053     // SMIME and SMIMEOpaque, since there is no mp/encrypted for
01054     // S/MIME):
01055   case Kleo::SMIMEOpaqueFormat:
01056     return signing ?
01057       "application/pkcs7-mime;\n\t"
01058       "smime-type=signed-data;\n\t"
01059       "name=\"smime.p7m\";\n\t"
01060       :
01061       "application/pkcs7-mime;\n\t"
01062       "smime-type=enveloped-data;\n\t"
01063       "name=\"smime.p7m\";\n\t"
01064       ;
01065   }
01066 }
01067 
01068 static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01069   switch ( f ) {
01070   default:
01071   case Kleo::InlineOpenPGPFormat:
01072   case Kleo::OpenPGPMIMEFormat:
01073     return 0;
01074   case Kleo::SMIMEFormat:
01075     if ( signing )
01076       return 0;
01077   case Kleo::SMIMEOpaqueFormat:
01078     return "attachment; filename=\"smime.p7m\"";
01079   }
01080 }
01081 
01082 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) {
01083   return makeMultiPartSigned( f );
01084 }
01085 
01086 static inline const char * nestedContentType( Kleo::CryptoMessageFormat f, bool signing ) {
01087   switch ( f ) {
01088   case Kleo::OpenPGPMIMEFormat:
01089     return signing ? "application/pgp-signature; name=signature.asc \nContent-Description: This is a digitally signed message part." : "application/octet-stream" ;
01090   case Kleo::SMIMEFormat:
01091     if ( signing )
01092       return "application/pkcs7-signature; name=\"smime.p7s\"";
01093     // fall through:
01094   default:
01095   case Kleo::InlineOpenPGPFormat:
01096   case Kleo::SMIMEOpaqueFormat:
01097     return 0;
01098   }
01099 }
01100 
01101 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) {
01102   if ( !signing && f == Kleo::OpenPGPMIMEFormat )
01103     return "inline; filename=\"msg.asc\"";
01104   if ( signing && f == Kleo::SMIMEFormat )
01105     return "attachment; filename=\"smime.p7s\"";
01106   return 0;
01107 }
01108 
01109 static inline bool binaryHint( Kleo::CryptoMessageFormat f ) {
01110   switch ( f ) {
01111   case Kleo::SMIMEFormat:
01112   case Kleo::SMIMEOpaqueFormat:
01113     return true;
01114   default:
01115   case Kleo::OpenPGPMIMEFormat:
01116   case Kleo::InlineOpenPGPFormat:
01117     return false;
01118   }
01119 }
01120 
01121 static inline bool armor( Kleo::CryptoMessageFormat f ) {
01122   return !binaryHint( f );
01123 }
01124 
01125 static inline bool textMode( Kleo::CryptoMessageFormat f ) {
01126   return f == Kleo::InlineOpenPGPFormat;
01127 }
01128 
01129 static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) {
01130   switch ( f ) {
01131   case Kleo::SMIMEOpaqueFormat:
01132     return GpgME::Context::Normal;
01133   case Kleo::InlineOpenPGPFormat:
01134     return GpgME::Context::Clearsigned;
01135   default:
01136   case Kleo::SMIMEFormat:
01137   case Kleo::OpenPGPMIMEFormat:
01138     return GpgME::Context::Detached;
01139   }
01140 }
01141 
01142 //
01143 // END replacements for StructuringInfo(Wrapper)
01144 //
01145 
01146 class EncryptMessageJob : public MessageComposerJob {
01147 public:
01148   EncryptMessageJob( KMMessage* msg, const Kleo::KeyResolver::SplitInfo & si,
01149                      bool doSign, bool doEncrypt, const QByteArray& encodedBody,
01150                      int boundaryLevel, /*const KMMessagePart& oldBodyPart,*/
01151                      KMMessagePart* newBodyPart, Kleo::CryptoMessageFormat format,
01152              MessageComposer* composer )
01153     : MessageComposerJob( composer ), mMsg( msg ), mSplitInfo( si ),
01154       mDoSign( doSign ), mDoEncrypt( doEncrypt ), mEncodedBody( encodedBody ),
01155       mBoundaryLevel( boundaryLevel ), /*mOldBodyPart( oldBodyPart ),*/
01156       mNewBodyPart( newBodyPart ), mFormat( format ) {}
01157 
01158   void execute() {
01159     KMMessagePart tmpNewBodyPart;
01160     tmpNewBodyPart.duplicate( *mNewBodyPart ); // slow - we duplicate everything again
01161 
01162     // TODO: Async call
01163 
01164     mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt,
01165                                tmpNewBodyPart, mFormat );
01166     if ( !mComposer->mRc ) {
01167       delete mMsg; mMsg = 0;
01168       return;
01169     }
01170     mComposer->mMessageList.push_back( mMsg );
01171   }
01172 
01173 private:
01174   KMMessage* mMsg;
01175   Kleo::KeyResolver::SplitInfo mSplitInfo;
01176   bool mDoSign, mDoEncrypt;
01177   QByteArray mEncodedBody;
01178   int mBoundaryLevel;
01179   //KMMessagePart mOldBodyPart;
01180   KMMessagePart* mNewBodyPart;
01181   Kleo::CryptoMessageFormat mFormat;
01182 };
01183 
01184 class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob {
01185 public:
01186   SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer )
01187     : MessageComposerJob( composer ) {}
01188 
01189   void execute() {
01190     KMMessage * last = mComposer->mMessageList.back();
01191     mComposer->mMessageList.pop_back();
01192     mComposer->mMessageList.back()->setUnencryptedMsg( last );
01193   }
01194 };
01195 
01196 void MessageComposer::composeInlineOpenPGPMessage( KMMessage& theMessage,
01197                                                    bool doSign, bool doEncrypt )
01198 {
01199   // preprocess the body text
01200   const QByteArray bodyData = mText;
01201   if (bodyData.isNull()) {
01202     mRc = false;
01203     return;
01204   }
01205 
01206   mNewBodyPart = 0; // unused
01207   mEarlyAddAttachments = false;
01208   mAllAttachmentsAreInBody = false;
01209 
01210   // set the main headers
01211   theMessage.deleteBodyParts();
01212   QString oldContentType = theMessage.headerField( "Content-Type" );
01213   theMessage.removeHeaderField("Content-Type");
01214   theMessage.removeHeaderField("Content-Transfer-Encoding");
01215 
01216   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01217     = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat );
01218   kdWarning( splitInfos.empty() )
01219     << "MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat"
01220     << endl;
01221   std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it;
01222   for ( it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) {
01223     const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01224     KMMessage* msg = new KMMessage( theMessage );
01225     if ( doEncrypt ) {
01226       Kpgp::Result result;
01227       QByteArray encryptedBody;
01228       if ( doSign ) {  // Sign and encrypt
01229         const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat );
01230         result = pgpSignedAndEncryptedMsg( encryptedBody, bodyData, signingKeys,
01231                                            splitInfo.keys, Kleo::InlineOpenPGPFormat );
01232       } else { // Encrypt but don't sign
01233         result = pgpEncryptedMsg( encryptedBody, bodyData,
01234                                   splitInfo.keys, Kleo::InlineOpenPGPFormat );
01235       }
01236       if ( result != Kpgp::Ok ) {
01237         mRc = false;
01238         return;
01239       }
01240       assert( !encryptedBody.isNull() ); // if you hit this, check gpg-agent is running, then blame gpgme.
01241       mOldBodyPart.setBodyEncodedBinary( encryptedBody );
01242     } else {
01243       if ( doSign ) { // Sign but don't encrypt
01244         pgpSignedMsg( bodyData, Kleo::InlineOpenPGPFormat );
01245         if ( mSignature.isNull() ) {
01246           mRc = false;
01247           return;
01248         }
01249         mOldBodyPart.setBodyEncodedBinary( mSignature );
01250       } else { // don't sign nor encrypt -> nothing to do
01251         assert( !bodyData.isNull() );
01252         mOldBodyPart.setBodyEncodedBinary( bodyData );
01253       }
01254     }
01255     mOldBodyPart.setContentDisposition( "inline" );
01256     mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01257     mOldBodyPart.setCharset(mCharset);
01258     addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01259     mMessageList.push_back( msg );
01260     if ( it == splitInfos.begin() ) {
01261       if ( doEncrypt && !saveMessagesEncrypted() ) {
01262         mOldBodyPart.setBodyEncodedBinary( bodyData );
01263         KMMessage* msgUnenc = new KMMessage( theMessage );
01264         addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01265         msg->setUnencryptedMsg( msgUnenc );
01266       }
01267     }
01268   } // end for
01269 }
01270 
01271 // very much inspired by composeInlineOpenPGPMessage
01272 void MessageComposer::composeChiasmusMessage( KMMessage& theMessage, Kleo::CryptoMessageFormat format )
01273 {
01274   assert( !GlobalSettings::chiasmusKey().isEmpty() ); // kmcomposewin code should have made sure
01275   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
01276   assert( cpf );
01277   const Kleo::CryptoBackend::Protocol * chiasmus
01278     = cpf->protocol( "Chiasmus" );
01279   assert( chiasmus ); // kmcomposewin code should have made sure
01280 
01281   // preprocess the body text
01282   const QByteArray bodyData = mText;
01283   if (bodyData.isNull()) {
01284     mRc = false;
01285     return;
01286   }
01287 
01288   mNewBodyPart = 0; // unused
01289   mEarlyAddAttachments = false;
01290   mAllAttachmentsAreInBody = false;
01291 
01292   // set the main headers
01293   theMessage.deleteBodyParts();
01294   QString oldContentType = theMessage.headerField( "Content-Type" );
01295   theMessage.removeHeaderField("Content-Type");
01296   theMessage.removeHeaderField("Content-Transfer-Encoding");
01297 
01298   // This reads strange, but we know that AdjustCryptFlagsJob created a single splitinfo,
01299   // under the given "format" (usually openpgp/mime; doesn't matter)
01300   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01301     = mKeyResolver->encryptionItems( format );
01302   assert( splitInfos.size() == 1 );
01303   for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01304   {
01305     const Kleo::KeyResolver::SplitInfo& splitInfo = *it;
01306     KMMessage* msg = new KMMessage( theMessage );
01307     QByteArray encryptedBody;
01308 
01309     if ( !encryptWithChiasmus( chiasmus, bodyData, encryptedBody ) ) {
01310       mRc = false;
01311       return;
01312     }
01313     assert( !encryptedBody.isNull() );
01314     // This leaves CTE==7-bit, no good
01315     //mOldBodyPart.setBodyEncodedBinary( encryptedBody );
01316 
01317     bool doSign = false;
01318     QValueList<int> allowedCTEs;
01319     mOldBodyPart.setBodyAndGuessCte( encryptedBody, allowedCTEs,
01320                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01321                      doSign );
01322 
01323 
01324     mOldBodyPart.setContentDisposition( "inline" );
01325     // Used in case of no attachments
01326     mOldBodyPart.setOriginalContentTypeStr( "application/vnd.de.bund.bsi.chiasmus-text;chiasmus-charset=" + mCharset );
01327     // Used in case of attachments
01328     mOldBodyPart.setTypeStr( "application" );
01329     mOldBodyPart.setSubtypeStr( "vnd.de.bund.bsi.chiasmus-text" );
01330     mOldBodyPart.setAdditionalCTypeParamStr( QCString( "chiasmus-charset=" + mCharset ) );
01331     addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01332     mMessageList.push_back( msg );
01333 
01334     if ( it == splitInfos.begin() && !saveMessagesEncrypted() ) {
01335       mOldBodyPart.setBodyEncodedBinary( bodyData );
01336       KMMessage* msgUnenc = new KMMessage( theMessage );
01337       addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat );
01338       msg->setUnencryptedMsg( msgUnenc );
01339     }
01340   }
01341 }
01342 
01343 void MessageComposer::composeMessage( KMMessage& theMessage,
01344                                       bool doSign, bool doEncrypt,
01345                                       Kleo::CryptoMessageFormat format )
01346 {
01347 #ifdef DEBUG
01348   kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl;
01349 #endif
01350   if ( format == Kleo::InlineOpenPGPFormat ) {
01351     composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt );
01352     return;
01353   }
01354 
01355   if ( mEncryptWithChiasmus )
01356   {
01357     composeChiasmusMessage( theMessage, format );
01358     return;
01359   }
01360 
01361   // create informative header for those that have no mime-capable
01362   // email client
01363   theMessage.setBody( "This message is in MIME format." );
01364 
01365   // preprocess the body text
01366   QByteArray bodyData = mText;
01367   if (bodyData.isNull()) {
01368     mRc = false;
01369     return;
01370   }
01371 
01372   // set the main headers
01373   QString oldContentType = theMessage.headerField( "Content-Type" );
01374   theMessage.deleteBodyParts();
01375   theMessage.removeHeaderField("Content-Type");
01376   theMessage.removeHeaderField("Content-Transfer-Encoding");
01377   theMessage.setAutomaticFields(true); // == multipart/mixed
01378 
01379   // this is our *final* body part
01380   mNewBodyPart = new KMMessagePart;
01381 
01382   // this is the boundary depth of the surrounding MIME part
01383   mPreviousBoundaryLevel = 0;
01384 
01385   // whether the body must be signed/encrypted
01386   const bool doEncryptBody = doEncrypt && mEncryptBody;
01387   const bool doSignBody = doSign && mSignBody;
01388 
01389   // create temporary bodyPart for editor text
01390   // (and for all attachments, if mail is to be signed and/or encrypted)
01391   mEarlyAddAttachments = !mAttachments.empty() && ( doSignBody || doEncryptBody );
01392 
01393   mAllAttachmentsAreInBody = mEarlyAddAttachments;
01394 
01395   // test whether there ARE attachments that can be included into the body
01396   if( mEarlyAddAttachments ) {
01397     bool someOk = false;
01398     for ( QValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01399       if ( it->encrypt == doEncryptBody && it->sign == doSignBody )
01400         someOk = true;
01401       else
01402         mAllAttachmentsAreInBody = false;
01403     }
01404     if( !mAllAttachmentsAreInBody && !someOk )
01405       mEarlyAddAttachments = false;
01406   }
01407 
01408   kdDebug(5006) << "mEarlyAddAttachments=" << mEarlyAddAttachments << " mAllAttachmentsAreInBody=" << mAllAttachmentsAreInBody << endl;
01409 
01410   // if an html message is to be generated, make a text/plain and text/html part
01411   mMultipartMixedBoundary = "";
01412   if ( mEarlyAddAttachments ) {
01413     mOldBodyPart.setTypeStr( "multipart" );
01414     mOldBodyPart.setSubtypeStr( "mixed" );
01415     // calculate a boundary string
01416     DwMediaType tmpCT;
01417     tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
01418     mMultipartMixedBoundary = tmpCT.Boundary().c_str();
01419   }
01420   else if ( mIsRichText ) {
01421     mOldBodyPart.setTypeStr( "multipart" );
01422     mOldBodyPart.setSubtypeStr( "alternative" );
01423   }
01424   else
01425     mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01426 
01427   mOldBodyPart.setContentDisposition( "inline" );
01428 
01429   if ( mIsRichText ) { // create a multipart body
01430     // calculate a boundary string
01431     QCString boundaryCStr;  // storing boundary string data
01432     QCString newbody;
01433     DwMediaType tmpCT;
01434     tmpCT.CreateBoundary( ++mPreviousBoundaryLevel );
01435     boundaryCStr = KMail::Util::CString( tmpCT.Boundary() );
01436     QValueList<int> allowedCTEs;
01437 
01438     KMMessagePart textBodyPart;
01439     textBodyPart.setTypeStr("text");
01440     textBodyPart.setSubtypeStr("plain");
01441 
01442     QCString textbody = plainTextFromMarkup( mText /* converted to QString */ );
01443 
01444     // the signed body must not be 8bit encoded
01445     textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs,
01446                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01447                      doSign );
01448     textBodyPart.setCharset( mCharset );
01449     textBodyPart.setBodyEncoded( textbody );
01450     DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart );
01451     textDwPart->Assemble();
01452     newbody += "--";
01453     newbody +=     boundaryCStr;
01454     newbody +=                 "\n";
01455     newbody += textDwPart->AsString().c_str();
01456     delete textDwPart;
01457     textDwPart = 0;
01458 
01459     KMMessagePart htmlBodyPart;
01460     htmlBodyPart.setTypeStr("text");
01461     htmlBodyPart.setSubtypeStr("html");
01462     QByteArray htmlbody = mText;
01463     // the signed body must not be 8bit encoded
01464     htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs,
01465                                      !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01466                      doSign );
01467     htmlBodyPart.setCharset( mCharset );
01468     htmlBodyPart.setBodyEncodedBinary( htmlbody );
01469     DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart );
01470     htmlDwPart->Assemble();
01471     newbody += "\n--";
01472     newbody +=     boundaryCStr;
01473     newbody +=                 "\n";
01474     newbody += htmlDwPart->AsString().c_str();
01475     delete htmlDwPart;
01476     htmlDwPart = 0;
01477 
01478     newbody += "--";
01479     newbody +=     boundaryCStr;
01480     newbody +=                 "--\n";
01481     bodyData = KMail::Util::byteArrayFromQCStringNoDetach( newbody );
01482     mOldBodyPart.setBodyEncodedBinary( bodyData );
01483 
01484     mSaveBoundary = tmpCT.Boundary();
01485   }
01486 
01487   // Prepare attachments that will be signed/encrypted
01488   for ( QValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01489     // signed/encrypted body parts must be either QP or base64 encoded
01490     // Why not 7 bit? Because the LF->CRLF canonicalization would render
01491     // e.g. 7 bit encoded shell scripts unusable because of the CRs.
01492     //
01493     // (marc) this is a workaround for the KMail bug that doesn't
01494     // respect the CRLF->LF de-canonicalisation. We should
01495     // eventually get rid of this:
01496     if( it->sign || it->encrypt ) {
01497       QCString cte = it->part->cteStr().lower();
01498       if( ( "8bit" == cte && it->part->type() != DwMime::kTypeMessage )
01499           || ( ( it->part->type() == DwMime::kTypeText )
01500                && ( "7bit" == cte ) ) ) {
01501         const QByteArray body = it->part->bodyDecodedBinary();
01502         QValueList<int> dummy;
01503         it->part->setBodyAndGuessCte(body, dummy, false, it->sign);
01504         kdDebug(5006) << "Changed encoding of message part from "
01505                       << cte << " to " << it->part->cteStr() << endl;
01506       }
01507     }
01508   }
01509 
01510   if( mEarlyAddAttachments ) {
01511     // add the normal body text
01512     KMMessagePart innerBodyPart;
01513     if ( mIsRichText ) {
01514       innerBodyPart.setTypeStr(   "multipart");//text" );
01515       innerBodyPart.setSubtypeStr("alternative");//html");
01516     }
01517     else {
01518       innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() );
01519     }
01520     innerBodyPart.setContentDisposition( "inline" );
01521     QValueList<int> allowedCTEs;
01522     // the signed body must not be 8bit encoded
01523     innerBodyPart.setBodyAndGuessCte( bodyData, allowedCTEs,
01524                                       !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01525                                       doSign );
01526     if ( !mIsRichText )
01527       innerBodyPart.setCharset( mCharset );
01528     innerBodyPart.setBodyEncodedBinary( bodyData ); // do we need this, since setBodyAndGuessCte does this already?
01529     DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
01530     innerDwPart->Assemble();
01531     QByteArray tmpbody = KMail::Util::ByteArray( innerDwPart->AsString() );
01532     if ( mIsRichText ) { // and add our mp/a boundary
01533         int boundPos = tmpbody.find( '\n' );
01534         if( -1 < boundPos ) {
01535           QCString bStr( ";\n  boundary=\"" );
01536           bStr += mSaveBoundary.c_str();
01537           bStr += "\"";
01538           bodyData = tmpbody;
01539           KMail::Util::insert( bodyData, boundPos, bStr );
01540           KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
01541         }
01542     }
01543     else {
01544       bodyData = tmpbody;
01545       KMail::Util::insert( bodyData, 0, "--" + mMultipartMixedBoundary + "\n" ); // prepend
01546     }
01547     delete innerDwPart;
01548     innerDwPart = 0;
01549     // add all matching Attachments
01550     // NOTE: This code will be changed when KMime is complete.
01551     for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01552       if ( it->encrypt == doEncryptBody && it->sign == doSignBody ) {
01553         innerDwPart = theMessage.createDWBodyPart( it->part );
01554         innerDwPart->Assemble();
01555         KMail::Util::append( bodyData, QCString( "\n--" + mMultipartMixedBoundary + "\n" ) );
01556         KMail::Util::append( bodyData, innerDwPart->AsString().c_str() );
01557         delete innerDwPart;
01558         innerDwPart = 0;
01559       }
01560     }
01561     KMail::Util::append( bodyData, QCString( "\n--" + mMultipartMixedBoundary + "--\n" ) );
01562   } else { // !earlyAddAttachments
01563     QValueList<int> allowedCTEs;
01564     // the signed body must not be 8bit encoded
01565     mOldBodyPart.setBodyAndGuessCte(bodyData, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign,
01566                                     doSign);
01567     if ( !mIsRichText )
01568       mOldBodyPart.setCharset(mCharset);
01569   }
01570   // create S/MIME body part for signing and/or encrypting
01571   mOldBodyPart.setBodyEncodedBinary( bodyData );
01572 
01573   if( doSignBody || doEncryptBody ) {
01574     // get string representation of body part (including the attachments)
01575 
01576     DwBodyPart* dwPart;
01577     if ( mIsRichText && !mEarlyAddAttachments ) {
01578       // if we are using richtext and not already have a mp/a body
01579       // make the body a mp/a body
01580       dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01581       DwHeaders& headers = dwPart->Headers();
01582       DwMediaType& ct = headers.ContentType();
01583       ct.SetBoundary(mSaveBoundary);
01584       dwPart->Assemble();
01585     }
01586     else {
01587       dwPart = theMessage.createDWBodyPart( &mOldBodyPart );
01588       dwPart->Assemble();
01589     }
01590     mEncodedBody = KMail::Util::ByteArray( dwPart->AsString() );
01591     delete dwPart;
01592     dwPart = 0;
01593 
01594     // manually add a boundary definition to the Content-Type header
01595     if( !mMultipartMixedBoundary.isEmpty() ) {
01596       int boundPos = mEncodedBody.find( '\n' );
01597       if( -1 < boundPos ) {
01598         // insert new "boundary" parameter
01599         QCString bStr( ";\n  boundary=\"" );
01600         bStr += mMultipartMixedBoundary;
01601         bStr += "\"";
01602         KMail::Util::insert( mEncodedBody, boundPos, bStr.data() );
01603       }
01604     }
01605 
01606     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01607     // according to RfC 2633, 3.1.1 Canonicalization
01608     //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01609     mEncodedBody = KMail::Util::lf2crlf( mEncodedBody );
01610   }
01611 
01612   if ( doSignBody ) {
01613     mPerformingSignOperation = true;  // this lets the KMComposeWin know if it is safe to close the window.
01614     pgpSignedMsg( mEncodedBody, format );
01615     mPerformingSignOperation = false;
01616 
01617     if ( mSignature.isEmpty() ) {
01618       kdDebug() << "signature was empty" << endl;
01619       mRc = false;
01620       return;
01621     }
01622     mRc = processStructuringInfo( QString::null,
01623                   mOldBodyPart.contentDescription(),
01624                   mOldBodyPart.typeStr(),
01625                   mOldBodyPart.subtypeStr(),
01626                   mOldBodyPart.contentDisposition(),
01627                   mOldBodyPart.contentTransferEncodingStr(),
01628                   mEncodedBody, "signature",
01629                   mSignature,
01630                   *mNewBodyPart, true, format );
01631     if ( mRc ) {
01632       if ( !makeMultiPartSigned( format ) ) {
01633     mNewBodyPart->setCharset( mCharset );
01634       }
01635     } else
01636       KMessageBox::sorry( mComposeWin,
01637               mErrorProcessingStructuringInfo );
01638   }
01639 
01640   if ( !mRc )
01641     return;
01642 
01643   continueComposeMessage( theMessage, doSign, doEncrypt, format );
01644 }
01645 
01646 // Do the encryption stuff
01647 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
01648                                               bool doSign, bool doEncrypt,
01649                                               Kleo::CryptoMessageFormat format )
01650 {
01651 
01652   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01653     = mKeyResolver->encryptionItems( format );
01654   kdWarning( splitInfos.empty() )
01655     << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
01656     << Kleo::cryptoMessageFormatToString( format ) << endl;
01657 
01658   if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
01659     mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
01660     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
01661                          Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
01662                          false, mEncodedBody,
01663                          mPreviousBoundaryLevel,
01664                          /*mOldBodyPart,*/ mNewBodyPart,
01665                          format, this ) );
01666   }
01667 
01668   for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01669     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
01670                          doEncrypt, mEncodedBody,
01671                          mPreviousBoundaryLevel,
01672                          /*mOldBodyPart,*/ mNewBodyPart,
01673                          format, this ) );
01674 }
01675 
01676 void MessageComposer::encryptMessage( KMMessage* msg,
01677                       const Kleo::KeyResolver::SplitInfo & splitInfo,
01678                                       bool doSign, bool doEncrypt,
01679                                       KMMessagePart newBodyPart,
01680                       Kleo::CryptoMessageFormat format )
01681 {
01682   if ( doEncrypt && splitInfo.keys.empty() ) {
01683     // the user wants to send the message unencrypted
01684     //mComposeWin->setEncryption( false, false );
01685     //FIXME why is this talkback needed? Till
01686     doEncrypt = false;
01687   }
01688 
01689   const bool doEncryptBody = doEncrypt && mEncryptBody;
01690   const bool doSignBody = doSign && mSignBody;
01691 
01692   if ( doEncryptBody ) {
01693     QByteArray innerContent;
01694     if ( doSignBody ) {
01695       // extract signed body from newBodyPart
01696       DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
01697       dwPart->Assemble();
01698       innerContent = KMail::Util::ByteArray( dwPart->AsString() );
01699       delete dwPart;
01700       dwPart = 0;
01701     } else {
01702       innerContent = mEncodedBody;
01703     }
01704 
01705     // now do the encrypting:
01706     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01707     // according to RfC 2633, 3.1.1 Canonicalization
01708     //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01709     innerContent = KMail::Util::lf2crlf( innerContent );
01710     //kdDebug(5006) << "                                                       done." << endl;
01711 
01712     QByteArray encryptedBody;
01713     Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
01714                                            splitInfo.keys, format );
01715     if ( result != Kpgp::Ok ) {
01716       mRc = false;
01717       return;
01718     }
01719     mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01720                   newBodyPart.contentDescription(),
01721                   newBodyPart.typeStr(),
01722                   newBodyPart.subtypeStr(),
01723                   newBodyPart.contentDisposition(),
01724                   newBodyPart.contentTransferEncodingStr(),
01725                   innerContent,
01726                   "encrypted data",
01727                   encryptedBody,
01728                   newBodyPart, false, format );
01729     if ( !mRc )
01730       KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
01731   }
01732 
01733   // process the attachments that are not included into the body
01734   if( mRc ) {
01735     const bool useNewBodyPart = doSignBody || doEncryptBody;
01736     addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
01737       useNewBodyPart ? newBodyPart : mOldBodyPart, format );
01738   }
01739 }
01740 
01741 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
01742                                              const Kleo::KeyResolver::SplitInfo & splitInfo,
01743                                              bool doSign, bool doEncrypt,
01744                                              const KMMessagePart& ourFineBodyPart,
01745                                              Kleo::CryptoMessageFormat format )
01746 {
01747   const bool doEncryptBody = doEncrypt && mEncryptBody;
01748   const bool doSignBody = doSign && mSignBody;
01749 
01750   if( !mAttachments.empty()
01751       && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
01752     // set the content type header
01753     msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
01754     msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
01755     msg->headers().ContentType().CreateBoundary( 0 );
01756     kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
01757 
01758     // add our Body Part
01759     DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
01760     DwHeaders& headers = tmpDwPart->Headers();
01761     DwMediaType& ct = headers.ContentType();
01762     if ( !mSaveBoundary.empty() )
01763       ct.SetBoundary(mSaveBoundary);
01764     tmpDwPart->Assemble();
01765 
01766     //KMMessagePart newPart;
01767     //newPart.setBody(tmpDwPart->AsString().c_str());
01768     msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain
01769 
01770     // add Attachments
01771     // create additional bodyparts for the attachments (if any)
01772     KMMessagePart newAttachPart;
01773     for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01774 
01775       const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
01776 
01777       if ( !cryptFlagsDifferent && mEarlyAddAttachments )
01778         continue;
01779 
01780       const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
01781       const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
01782 
01783       if ( !encryptThisNow && !signThisNow ) {
01784         msg->addBodyPart( it->part );
01785         // Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
01786         (void)msg->asDwMessage();
01787         continue;
01788       }
01789 
01790       KMMessagePart& rEncryptMessagePart( *it->part );
01791 
01792       DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
01793       innerDwPart->Assemble();
01794       QByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
01795       delete innerDwPart;
01796       innerDwPart = 0;
01797 
01798       // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01799       // according to RfC 2633, 3.1.1 Canonicalization
01800       //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01801       encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
01802 
01803       // sign this attachment
01804       if( signThisNow ) {
01805         pgpSignedMsg( encodedAttachment, format );
01806         mRc = !mSignature.isEmpty();
01807         if( mRc ) {
01808           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01809                                         it->part->contentDescription(),
01810                                         it->part->typeStr(),
01811                                         it->part->subtypeStr(),
01812                                         it->part->contentDisposition(),
01813                                         it->part->contentTransferEncodingStr(),
01814                                         encodedAttachment,
01815                                         "signature",
01816                                         mSignature,
01817                                         newAttachPart, true, format );
01818           if( mRc ) {
01819             if( encryptThisNow ) {
01820               rEncryptMessagePart = newAttachPart;
01821               DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
01822               dwPart->Assemble();
01823               encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
01824               delete dwPart;
01825               dwPart = 0;
01826             }
01827           } else
01828             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01829         } else {
01830           // quit the attachments' loop
01831           break;
01832         }
01833       }
01834       if( encryptThisNow ) {
01835         QByteArray encryptedBody;
01836         Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
01837                                                encodedAttachment,
01838                                                splitInfo.keys,
01839                                                format );
01840 
01841         if( Kpgp::Ok == result ) {
01842           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01843                                         rEncryptMessagePart.contentDescription(),
01844                                         rEncryptMessagePart.typeStr(),
01845                                         rEncryptMessagePart.subtypeStr(),
01846                                         rEncryptMessagePart.contentDisposition(),
01847                                         rEncryptMessagePart.contentTransferEncodingStr(),
01848                                         encodedAttachment,
01849                                         "encrypted data",
01850                                         encryptedBody,
01851                                         newAttachPart, false, format );
01852           if ( !mRc )
01853             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01854         } else
01855           mRc = false;
01856       }
01857       msg->addBodyPart( &newAttachPart );
01858       (void)msg->asDwMessage(); // Assemble the message. One gets a completely empty message otherwise :/
01859     }
01860   } else { // no attachments in the final message
01861     if( ourFineBodyPart.originalContentTypeStr() ) {
01862       msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
01863       msg->headers().ContentType().Parse();
01864       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
01865     } else {
01866       QCString ct = ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr();
01867       if ( ct == "multipart/mixed" )
01868         ct += ";\n\tboundary=\"" + mMultipartMixedBoundary + '"';
01869       else if ( ct == "multipart/alternative" )
01870         ct += ";\n\tboundary=\"" + QCString(mSaveBoundary.c_str()) + '"';
01871       msg->headers().ContentType().FromString( ct );
01872       msg->headers().ContentType().Parse();
01873       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
01874     }
01875     if ( !ourFineBodyPart.charset().isEmpty() )
01876       msg->setCharset( ourFineBodyPart.charset() );
01877     msg->setHeaderField( "Content-Transfer-Encoding",
01878                          ourFineBodyPart.contentTransferEncodingStr() );
01879     msg->setHeaderField( "Content-Description",
01880                          ourFineBodyPart.contentDescription() );
01881     msg->setHeaderField( "Content-Disposition",
01882                          ourFineBodyPart.contentDisposition() );
01883 
01884     if ( mDebugComposerCrypto )
01885       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
01886 
01887     // set body content
01888     msg->setBody( ourFineBodyPart.dwBody() );
01889 
01890   }
01891 
01892   msg->setHeaderField( "X-KMail-Recipients",
01893                        splitInfo.recipients.join(", "), KMMessage::Address );
01894 
01895   if ( mDebugComposerCrypto ) {
01896     kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n      Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
01897     msg->headers().Assemble();
01898     kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n      Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
01899   }
01900 }
01901 
01902 //-----------------------------------------------------------------------------
01903 // This method does not call any crypto ops, so it does not need to be async
01904 bool MessageComposer::processStructuringInfo( const QString bugURL,
01905                                               const QString contentDescClear,
01906                                               const QCString contentTypeClear,
01907                                               const QCString contentSubtypeClear,
01908                                               const QCString contentDispClear,
01909                                               const QCString contentTEncClear,
01910                                               const QByteArray& clearCStr,
01911                                               const QString /*contentDescCiph*/,
01912                                               const QByteArray& ciphertext,
01913                                               KMMessagePart& resultingPart,
01914                           bool signing, Kleo::CryptoMessageFormat format )
01915 {
01916   assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' ); // I was called with a QCString !?
01917   bool bOk = true;
01918 
01919   if ( makeMimeObject( format, signing ) ) {
01920     QCString mainHeader = "Content-Type: ";
01921     const char * toplevelCT = toplevelContentType( format, signing );
01922     if ( toplevelCT )
01923       mainHeader += toplevelCT;
01924     else {
01925       if( makeMultiMime( format, signing ) )
01926         mainHeader += "text/plain";
01927       else
01928         mainHeader += contentTypeClear + '/' + contentSubtypeClear;
01929     }
01930 
01931     const QCString boundaryCStr = KMime::multiPartBoundary();
01932     // add "boundary" parameter
01933     if ( makeMultiMime( format, signing ) )
01934       mainHeader.replace( "%boundary", boundaryCStr );
01935 
01936     if ( toplevelCT ) {
01937       if ( const char * str = toplevelContentDisposition( format, signing ) ) {
01938         mainHeader += "\nContent-Disposition: ";
01939         mainHeader += str;
01940       }
01941       if ( !makeMultiMime( format, signing ) &&
01942        binaryHint( format ) )
01943         mainHeader += "\nContent-Transfer-Encoding: base64";
01944     } else {
01945       if( 0 < contentDispClear.length() ) {
01946         mainHeader += "\nContent-Disposition: ";
01947         mainHeader += contentDispClear;
01948       }
01949       if( 0 < contentTEncClear.length() ) {
01950         mainHeader += "\nContent-Transfer-Encoding: ";
01951         mainHeader += contentTEncClear;
01952       }
01953     }
01954 
01955     //kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
01956 
01957     DwString mainDwStr;
01958     mainDwStr = mainHeader + "\n\n";
01959     DwBodyPart mainDwPa( mainDwStr, 0 );
01960     mainDwPa.Parse();
01961     KMMessage::bodyPart( &mainDwPa, &resultingPart );
01962     if( !makeMultiMime( format, signing ) ) {
01963       if ( signing && includeCleartextWhenSigning( format ) ) {
01964         QByteArray bodyText( clearCStr );
01965         KMail::Util::append( bodyText, "\n" );
01966         KMail::Util::append( bodyText, ciphertext );
01967         resultingPart.setBodyEncodedBinary( bodyText );
01968       } else {
01969         resultingPart.setBodyEncodedBinary( ciphertext );
01970       }
01971     } else {
01972       // Build the encapsulated MIME parts.
01973       // Build a MIME part holding the version information
01974       // taking the body contents returned in
01975       // structuring.data.bodyTextVersion.
01976       QCString versCStr, codeCStr;
01977       if ( !signing && format == Kleo::OpenPGPMIMEFormat )
01978         versCStr =
01979       "Content-Type: application/pgp-encrypted\n"
01980       "Content-Disposition: attachment\n"
01981       "\n"
01982       "Version: 1";
01983 
01984       // Build a MIME part holding the code information
01985       // taking the body contents returned in ciphertext.
01986       const char * nestedCT = nestedContentType( format, signing );
01987       assert( nestedCT );
01988       codeCStr = "Content-Type: ";
01989       codeCStr += nestedCT;
01990       codeCStr += '\n';
01991       if ( const char * str = nestedContentDisposition( format, signing ) ) {
01992     codeCStr += "Content-Disposition: ";
01993     codeCStr += str;
01994     codeCStr += '\n';
01995       }
01996       if ( binaryHint( format ) ) {
01997     codeCStr += "Content-Transfer-Encoding: base64\n\n";
01998     codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext );
01999       } else
02000     codeCStr += '\n' + QCString( ciphertext.data(), ciphertext.size() + 1 );
02001 
02002 
02003       QByteArray mainStr;
02004       KMail::Util::append( mainStr, "--" );
02005       KMail::Util::append( mainStr, boundaryCStr );
02006       if ( signing && includeCleartextWhenSigning( format ) &&
02007        !clearCStr.isEmpty() ) {
02008         KMail::Util::append( mainStr, "\n" );
02009         // clearCStr is the one that can be very big for large attachments, don't merge with the other lines
02010         KMail::Util::append( mainStr, clearCStr );
02011         KMail::Util::append( mainStr, "\n--" + boundaryCStr );
02012       }
02013       if ( !versCStr.isEmpty() )
02014         KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
02015       if( !codeCStr.isEmpty() )
02016         KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
02017       KMail::Util::append( mainStr, "--\n" );
02018 
02019       //kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
02020       resultingPart.setBodyEncodedBinary( mainStr );
02021     }
02022 
02023   } else { //  not making a mime object, build a plain message body.
02024 
02025     resultingPart.setContentDescription( contentDescClear );
02026     resultingPart.setTypeStr( contentTypeClear );
02027     resultingPart.setSubtypeStr( contentSubtypeClear );
02028     resultingPart.setContentDisposition( contentDispClear );
02029     resultingPart.setContentTransferEncodingStr( contentTEncClear );
02030     QByteArray resultingBody;
02031 
02032     if ( signing && includeCleartextWhenSigning( format ) ) {
02033       if( !clearCStr.isEmpty() )
02034         KMail::Util::append( resultingBody, clearCStr );
02035     }
02036     if ( !ciphertext.isEmpty() )
02037       KMail::Util::append( resultingBody, ciphertext );
02038     else {
02039       // Plugin error!
02040       KMessageBox::sorry( mComposeWin,
02041                           i18n( "<qt><p>Error: The backend did not return "
02042                                 "any encoded data.</p>"
02043                                 "<p>Please report this bug:<br>%2</p></qt>" )
02044                           .arg( bugURL ) );
02045       bOk = false;
02046     }
02047     resultingPart.setBodyEncodedBinary( resultingBody );
02048   }
02049 
02050   return bOk;
02051 }
02052 
02053 //-----------------------------------------------------------------------------
02054 QCString MessageComposer::plainTextFromMarkup( const QString& markupText )
02055 {
02056   QTextEdit *hackConspiratorTextEdit = new QTextEdit( markupText );
02057   hackConspiratorTextEdit->setTextFormat(Qt::PlainText);
02058   if ( !mDisableBreaking ) {
02059     hackConspiratorTextEdit->setWordWrap( QTextEdit::FixedColumnWidth );
02060     hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
02061   }
02062   QString text = hackConspiratorTextEdit->text();
02063   QCString textbody;
02064 
02065   const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02066   if( mCharset == "us-ascii" ) {
02067     textbody = KMMsgBase::toUsAscii( text );
02068   } else if( codec == 0 ) {
02069     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02070     textbody = text.local8Bit();
02071   } else {
02072     text = codec->toUnicode( text.latin1(), text.length() );
02073     textbody = codec->fromUnicode( text );
02074   }
02075   if (textbody.isNull()) textbody = "";
02076 
02077   delete hackConspiratorTextEdit;
02078   return textbody;
02079 }
02080 
02081 //-----------------------------------------------------------------------------
02082 QByteArray MessageComposer::breakLinesAndApplyCodec()
02083 {
02084   QString text;
02085   QCString cText;
02086 
02087   if( mDisableBreaking || mIsRichText || !GlobalSettings::self()->wordWrap() )
02088     text = mComposeWin->mEditor->text();
02089   else
02090     text = mComposeWin->mEditor->brokenText();
02091   text.truncate( text.length() ); // to ensure text.size()==text.length()+1
02092 
02093   QString newText;
02094   const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02095 
02096   if( mCharset == "us-ascii" ) {
02097     cText = KMMsgBase::toUsAscii( text );
02098     newText = QString::fromLatin1( cText );
02099   } else if( codec == 0 ) {
02100     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02101     cText = text.local8Bit();
02102     newText = QString::fromLocal8Bit( cText );
02103   } else {
02104     cText = codec->fromUnicode( text );
02105     newText = codec->toUnicode( cText );
02106   }
02107   if (cText.isNull()) cText = "";
02108 
02109   if( !text.isEmpty() && (newText != text) ) {
02110     QString oldText = mComposeWin->mEditor->text();
02111     mComposeWin->mEditor->setText( newText );
02112     KCursorSaver idle( KBusyPtr::idle() );
02113     bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
02114                                                i18n("<qt>Not all characters fit into the chosen"
02115                                                     " encoding.<br><br>Send the message anyway?</qt>"),
02116                                                i18n("Some Characters Will Be Lost"),
02117                                                i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
02118     if( !anyway ) {
02119       mComposeWin->mEditor->setText(oldText);
02120       return QByteArray();
02121     }
02122   }
02123 
02124   // From RFC 3156:
02125   //  Note: The accepted OpenPGP convention is for signed data to end
02126   //  with a <CR><LF> sequence.  Note that the <CR><LF> sequence
02127   //  immediately preceding a MIME boundary delimiter line is considered
02128   //  to be part of the delimiter in [3], 5.1.  Thus, it is not part of
02129   //  the signed data preceding the delimiter line.  An implementation
02130   //  which elects to adhere to the OpenPGP convention has to make sure
02131   //  it inserts a <CR><LF> pair on the last line of the data to be
02132   //  signed and transmitted (signed message and transmitted message
02133   //  MUST be identical).
02134   // So make sure that the body ends with a <LF>.
02135   if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
02136     kdDebug(5006) << "Added an <LF> on the last line" << endl;
02137     cText += "\n";
02138   }
02139   return KMail::Util::byteArrayFromQCStringNoDetach( cText );
02140 }
02141 
02142 
02143 //-----------------------------------------------------------------------------
02144 void MessageComposer::pgpSignedMsg( const QByteArray& cText, Kleo::CryptoMessageFormat format ) {
02145 
02146   assert( cText.isEmpty() || cText[cText.size()-1] != '\0' ); // I was called with a QCString !?
02147   mSignature = QByteArray();
02148 
02149   const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
02150   if ( signingKeys.empty() ) {
02151     KMessageBox::sorry( mComposeWin,
02152             i18n("This message could not be signed, "
02153                  "since no valid signing keys have been found; "
02154                  "this should actually never happen, "
02155                  "please report this bug.") );
02156     return;
02157   }
02158 
02159   // TODO: ASync call? Likely, yes :-)
02160   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02161   assert( cpf );
02162   const Kleo::CryptoBackend::Protocol * proto
02163     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02164   assert( proto ); 
02165 
02166   std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
02167                             textMode( format ) ) );
02168 
02169   if ( !job.get() ) {
02170     KMessageBox::sorry( mComposeWin,
02171             i18n("This message could not be signed, "
02172                  "since the chosen backend does not seem to support "
02173                  "signing; this should actually never happen, "
02174                  "please report this bug.") );
02175     return;
02176   }
02177 
02178   QByteArray signature;
02179   const GpgME::SigningResult res =
02180     job->exec( signingKeys, cText, signingMode( format ), signature );
02181   {
02182       std::stringstream ss;
02183       ss << res;
02184       kdDebug(5006) << ss.str().c_str() << endl;
02185   }
02186   if ( res.error().isCanceled() ) {
02187     kdDebug() << "signing was canceled by user" << endl;
02188     return;
02189   }
02190   if ( res.error() ) {
02191     kdDebug() << "signing failed: " << res.error().asString() << endl;
02192     job->showErrorDialog( mComposeWin );
02193     return;
02194   }
02195 
02196   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02197       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02198       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Signing Operation") );
02199 
02200   mSignature = signature;
02201   if ( mSignature.isEmpty() ) {
02202     KMessageBox::sorry( mComposeWin,
02203                         i18n( "The signing operation failed. "
02204                               "Please make sure that the gpg-agent program "
02205                               "is running." ) );
02206   }
02207 }
02208 
02209 //-----------------------------------------------------------------------------
02210 Kpgp::Result MessageComposer::pgpEncryptedMsg( QByteArray & encryptedBody,
02211                                                const QByteArray& cText,
02212                                                const std::vector<GpgME::Key> & encryptionKeys,
02213                            Kleo::CryptoMessageFormat format )
02214 {
02215   // TODO: ASync call? Likely, yes :-)
02216   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02217   assert( cpf );
02218   const Kleo::CryptoBackend::Protocol * proto
02219     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02220   assert( proto ); // hmmmm....?
02221 
02222   std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
02223                                                           textMode( format ) ) );
02224   if ( !job.get() ) {
02225     KMessageBox::sorry( mComposeWin,
02226                         i18n("This message could not be encrypted, "
02227                              "since the chosen backend does not seem to support "
02228                              "encryption; this should actually never happen, "
02229                              "please report this bug.") );
02230     return Kpgp::Failure;
02231   }
02232 
02233   const GpgME::EncryptionResult res =
02234     job->exec( encryptionKeys, cText, true /* we do ownertrust ourselves */, encryptedBody );
02235   {
02236       std::stringstream ss;
02237       ss << res;
02238       kdDebug(5006) << ss.str().c_str() << endl;
02239   }
02240   if ( res.error().isCanceled() ) {
02241     kdDebug() << "encryption was canceled by user" << endl;
02242     return Kpgp::Canceled;
02243   }
02244   if ( res.error() ) {
02245     kdDebug() << "encryption failed: " << res.error().asString() << endl;
02246     job->showErrorDialog( mComposeWin );
02247     return Kpgp::Failure;
02248   }
02249 
02250   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02251       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02252       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
02253 
02254   return Kpgp::Ok;
02255 }
02256 
02257 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( QByteArray & encryptedBody,
02258                             const QByteArray& cText,
02259                             const std::vector<GpgME::Key> & signingKeys,
02260                             const std::vector<GpgME::Key> & encryptionKeys,
02261                             Kleo::CryptoMessageFormat format )
02262 {
02263   // TODO: ASync call? Likely, yes :-)
02264   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02265   assert( cpf );
02266   const Kleo::CryptoBackend::Protocol * proto
02267     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02268   assert( proto ); // hmmmm....?
02269 
02270   std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
02271                                   textMode( format ) ) );
02272   if ( !job.get() ) {
02273     KMessageBox::sorry( mComposeWin,
02274             i18n("This message could not be signed and encrypted, "
02275                  "since the chosen backend does not seem to support "
02276                  "combined signing and encryption; this should actually never happen, "
02277                  "please report this bug.") );
02278     return Kpgp::Failure;
02279   }
02280 
02281   const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
02282     job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
02283   {
02284       std::stringstream ss;
02285       ss << res.first << '\n' << res.second;
02286       kdDebug(5006) << ss.str().c_str() << endl;
02287   }
02288   if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
02289     kdDebug() << "encrypt/sign was canceled by user" << endl;
02290     return Kpgp::Canceled;
02291   }
02292   if ( res.first.error() || res.second.error() ) {
02293     if ( res.first.error() )
02294       kdDebug() << "signing failed: " << res.first.error().asString() << endl;
02295     else
02296       kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
02297     job->showErrorDialog( mComposeWin );
02298     return Kpgp::Failure;
02299   }
02300 
02301   if ( GlobalSettings::showGnuPGAuditLogAfterSuccessfulSignEncrypt() )
02302       if ( Kleo::MessageBox::showAuditLogButton( job.get() ) )
02303       Kleo::MessageBox::auditLog( 0, job.get(), i18n("GnuPG Audit Log for Encryption Operation") );
02304 
02305   return Kpgp::Ok;
02306 }
02307 
02308 
02309 #include "messagecomposer.moc"