kmail

messagecomposer.cpp

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