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