kmail

messagecomposer.cpp

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