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 )
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" : "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 )
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 
01594     pgpSignedMsg( mEncodedBody, format );
01595 
01596     if ( mSignature.isEmpty() ) {
01597       kdDebug() << "signature was empty" << endl;
01598       mRc = false;
01599       return;
01600     }
01601     mRc = processStructuringInfo( QString::null,
01602                   mOldBodyPart.contentDescription(),
01603                   mOldBodyPart.typeStr(),
01604                   mOldBodyPart.subtypeStr(),
01605                   mOldBodyPart.contentDisposition(),
01606                   mOldBodyPart.contentTransferEncodingStr(),
01607                   mEncodedBody, "signature",
01608                   mSignature,
01609                   *mNewBodyPart, true, format );
01610     if ( mRc ) {
01611       if ( !makeMultiPartSigned( format ) ) {
01612     mNewBodyPart->setCharset( mCharset );
01613       }
01614     } else
01615       KMessageBox::sorry( mComposeWin,
01616               mErrorProcessingStructuringInfo );
01617   }
01618 
01619   if ( !mRc )
01620     return;
01621 
01622   continueComposeMessage( theMessage, doSign, doEncrypt, format );
01623 }
01624 
01625 // Do the encryption stuff
01626 void MessageComposer::continueComposeMessage( KMMessage& theMessage,
01627                                               bool doSign, bool doEncrypt,
01628                                               Kleo::CryptoMessageFormat format )
01629 {
01630 
01631   const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos
01632     = mKeyResolver->encryptionItems( format );
01633   kdWarning( splitInfos.empty() )
01634     << "MessageComposer::continueComposeMessage(): splitInfos.empty() for "
01635     << Kleo::cryptoMessageFormatToString( format ) << endl;
01636 
01637   if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) {
01638     mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) );
01639     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ),
01640                          Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign,
01641                          false, mEncodedBody,
01642                          mPreviousBoundaryLevel,
01643                          /*mOldBodyPart,*/ mNewBodyPart,
01644                          format, this ) );
01645   }
01646 
01647   for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it )
01648     mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign,
01649                          doEncrypt, mEncodedBody,
01650                          mPreviousBoundaryLevel,
01651                          /*mOldBodyPart,*/ mNewBodyPart,
01652                          format, this ) );
01653 }
01654 
01655 void MessageComposer::encryptMessage( KMMessage* msg,
01656                       const Kleo::KeyResolver::SplitInfo & splitInfo,
01657                                       bool doSign, bool doEncrypt,
01658                                       KMMessagePart newBodyPart,
01659                       Kleo::CryptoMessageFormat format )
01660 {
01661   if ( doEncrypt && splitInfo.keys.empty() ) {
01662     // the user wants to send the message unencrypted
01663     //mComposeWin->setEncryption( false, false );
01664     //FIXME why is this talkback needed? Till
01665     doEncrypt = false;
01666   }
01667 
01668   const bool doEncryptBody = doEncrypt && mEncryptBody;
01669   const bool doSignBody = doSign && mSignBody;
01670 
01671   if ( doEncryptBody ) {
01672     QByteArray innerContent;
01673     if ( doSignBody ) {
01674       // extract signed body from newBodyPart
01675       DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
01676       dwPart->Assemble();
01677       innerContent = KMail::Util::ByteArray( dwPart->AsString() );
01678       delete dwPart;
01679       dwPart = 0;
01680     } else {
01681       innerContent = mEncodedBody;
01682     }
01683 
01684     // now do the encrypting:
01685     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01686     // according to RfC 2633, 3.1.1 Canonicalization
01687     //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01688     innerContent = KMail::Util::lf2crlf( innerContent );
01689     //kdDebug(5006) << "                                                       done." << endl;
01690 
01691     QByteArray encryptedBody;
01692     Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent,
01693                                            splitInfo.keys, format );
01694     if ( result != Kpgp::Ok ) {
01695       mRc = false;
01696       return;
01697     }
01698     mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01699                   newBodyPart.contentDescription(),
01700                   newBodyPart.typeStr(),
01701                   newBodyPart.subtypeStr(),
01702                   newBodyPart.contentDisposition(),
01703                   newBodyPart.contentTransferEncodingStr(),
01704                   innerContent,
01705                   "encrypted data",
01706                   encryptedBody,
01707                   newBodyPart, false, format );
01708     if ( !mRc )
01709       KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo);
01710   }
01711 
01712   // process the attachments that are not included into the body
01713   if( mRc ) {
01714     const bool useNewBodyPart = doSignBody || doEncryptBody;
01715     addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt,
01716       useNewBodyPart ? newBodyPart : mOldBodyPart, format );
01717   }
01718 }
01719 
01720 void MessageComposer::addBodyAndAttachments( KMMessage* msg,
01721                                              const Kleo::KeyResolver::SplitInfo & splitInfo,
01722                                              bool doSign, bool doEncrypt,
01723                                              const KMMessagePart& ourFineBodyPart,
01724                                              Kleo::CryptoMessageFormat format )
01725 {
01726   const bool doEncryptBody = doEncrypt && mEncryptBody;
01727   const bool doSignBody = doSign && mSignBody;
01728 
01729   if( !mAttachments.empty()
01730       && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) {
01731     // set the content type header
01732     msg->headers().ContentType().SetType( DwMime::kTypeMultipart );
01733     msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed );
01734     msg->headers().ContentType().CreateBoundary( 0 );
01735     kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl;
01736 
01737     // add our Body Part
01738     DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart );
01739     DwHeaders& headers = tmpDwPart->Headers();
01740     DwMediaType& ct = headers.ContentType();
01741     if ( !mSaveBoundary.empty() )
01742       ct.SetBoundary(mSaveBoundary);
01743     tmpDwPart->Assemble();
01744 
01745     //KMMessagePart newPart;
01746     //newPart.setBody(tmpDwPart->AsString().c_str());
01747     msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain
01748 
01749     // add Attachments
01750     // create additional bodyparts for the attachments (if any)
01751     KMMessagePart newAttachPart;
01752     for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) {
01753 
01754       const bool cryptFlagsDifferent = ( it->encrypt != doEncryptBody || it->sign != doSignBody ) ;
01755 
01756       if ( !cryptFlagsDifferent && mEarlyAddAttachments )
01757         continue;
01758 
01759       const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ;
01760       const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ;
01761 
01762       if ( !encryptThisNow && !signThisNow ) {
01763         msg->addBodyPart( it->part );
01764         // Assemble the message. Not sure why, but this fixes the vanishing boundary parameter
01765         (void)msg->asDwMessage();
01766         continue;
01767       }
01768 
01769       KMMessagePart& rEncryptMessagePart( *it->part );
01770 
01771       DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part );
01772       innerDwPart->Assemble();
01773       QByteArray encodedAttachment = KMail::Util::ByteArray( innerDwPart->AsString() );
01774       delete innerDwPart;
01775       innerDwPart = 0;
01776 
01777       // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
01778       // according to RfC 2633, 3.1.1 Canonicalization
01779       //kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
01780       encodedAttachment = KMail::Util::lf2crlf( encodedAttachment );
01781 
01782       // sign this attachment
01783       if( signThisNow ) {
01784         pgpSignedMsg( encodedAttachment, format );
01785         mRc = !mSignature.isEmpty();
01786         if( mRc ) {
01787           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01788                                         it->part->contentDescription(),
01789                                         it->part->typeStr(),
01790                                         it->part->subtypeStr(),
01791                                         it->part->contentDisposition(),
01792                                         it->part->contentTransferEncodingStr(),
01793                                         encodedAttachment,
01794                                         "signature",
01795                                         mSignature,
01796                                         newAttachPart, true, format );
01797           if( mRc ) {
01798             if( encryptThisNow ) {
01799               rEncryptMessagePart = newAttachPart;
01800               DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
01801               dwPart->Assemble();
01802               encodedAttachment = KMail::Util::ByteArray( dwPart->AsString() );
01803               delete dwPart;
01804               dwPart = 0;
01805             }
01806           } else
01807             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01808         } else {
01809           // quit the attachments' loop
01810           break;
01811         }
01812       }
01813       if( encryptThisNow ) {
01814         QByteArray encryptedBody;
01815         Kpgp::Result result = pgpEncryptedMsg( encryptedBody,
01816                                                encodedAttachment,
01817                                                splitInfo.keys,
01818                                                format );
01819 
01820         if( Kpgp::Ok == result ) {
01821           mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/",
01822                                         rEncryptMessagePart.contentDescription(),
01823                                         rEncryptMessagePart.typeStr(),
01824                                         rEncryptMessagePart.subtypeStr(),
01825                                         rEncryptMessagePart.contentDisposition(),
01826                                         rEncryptMessagePart.contentTransferEncodingStr(),
01827                                         encodedAttachment,
01828                                         "encrypted data",
01829                                         encryptedBody,
01830                                         newAttachPart, false, format );
01831           if ( !mRc )
01832             KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo );
01833         } else
01834           mRc = false;
01835       }
01836       msg->addBodyPart( &newAttachPart );
01837       (void)msg->asDwMessage(); // Assemble the message. One gets a completely empty message otherwise :/
01838     }
01839   } else { // no attachments in the final message
01840     if( ourFineBodyPart.originalContentTypeStr() ) {
01841       msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
01842       msg->headers().ContentType().Parse();
01843       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl;
01844     } else {
01845       QCString ct = ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr();
01846       if ( ct == "multipart/mixed" )
01847         ct += ";\n\tboundary=\"" + mMultipartMixedBoundary + '"';
01848       else if ( ct == "multipart/alternative" )
01849         ct += ";\n\tboundary=\"" + QCString(mSaveBoundary.c_str()) + '"';
01850       msg->headers().ContentType().FromString( ct );
01851       msg->headers().ContentType().Parse();
01852       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ct << endl;
01853     }
01854     if ( !ourFineBodyPart.charset().isEmpty() )
01855       msg->setCharset( ourFineBodyPart.charset() );
01856     msg->setHeaderField( "Content-Transfer-Encoding",
01857                          ourFineBodyPart.contentTransferEncodingStr() );
01858     msg->setHeaderField( "Content-Description",
01859                          ourFineBodyPart.contentDescription() );
01860     msg->setHeaderField( "Content-Disposition",
01861                          ourFineBodyPart.contentDisposition() );
01862 
01863     if ( mDebugComposerCrypto )
01864       kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl;
01865 
01866     // set body content
01867     msg->setBody( ourFineBodyPart.dwBody() );
01868 
01869   }
01870 
01871   msg->setHeaderField( "X-KMail-Recipients",
01872                        splitInfo.recipients.join(", "), KMMessage::Address );
01873 
01874   if ( mDebugComposerCrypto ) {
01875     kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n      Final message:\n|||" << msg->asString() << "|||\n\n" << endl;
01876     msg->headers().Assemble();
01877     kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n      Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
01878   }
01879 }
01880 
01881 //-----------------------------------------------------------------------------
01882 // This method does not call any crypto ops, so it does not need to be async
01883 bool MessageComposer::processStructuringInfo( const QString bugURL,
01884                                               const QString contentDescClear,
01885                                               const QCString contentTypeClear,
01886                                               const QCString contentSubtypeClear,
01887                                               const QCString contentDispClear,
01888                                               const QCString contentTEncClear,
01889                                               const QByteArray& clearCStr,
01890                                               const QString /*contentDescCiph*/,
01891                                               const QByteArray& ciphertext,
01892                                               KMMessagePart& resultingPart,
01893                           bool signing, Kleo::CryptoMessageFormat format )
01894 {
01895   assert( clearCStr.isEmpty() || clearCStr[clearCStr.size()-1] != '\0' ); // I was called with a QCString !?
01896   bool bOk = true;
01897 
01898   if ( makeMimeObject( format, signing ) ) {
01899     QCString mainHeader = "Content-Type: ";
01900     const char * toplevelCT = toplevelContentType( format, signing );
01901     if ( toplevelCT )
01902       mainHeader += toplevelCT;
01903     else {
01904       if( makeMultiMime( format, signing ) )
01905         mainHeader += "text/plain";
01906       else
01907         mainHeader += contentTypeClear + '/' + contentSubtypeClear;
01908     }
01909 
01910     const QCString boundaryCStr = KMime::multiPartBoundary();
01911     // add "boundary" parameter
01912     if ( makeMultiMime( format, signing ) )
01913       mainHeader.replace( "%boundary", boundaryCStr );
01914 
01915     if ( toplevelCT ) {
01916       if ( const char * str = toplevelContentDisposition( format, signing ) ) {
01917         mainHeader += "\nContent-Disposition: ";
01918         mainHeader += str;
01919       }
01920       if ( !makeMultiMime( format, signing ) &&
01921        binaryHint( format ) )
01922         mainHeader += "\nContent-Transfer-Encoding: base64";
01923     } else {
01924       if( 0 < contentDispClear.length() ) {
01925         mainHeader += "\nContent-Disposition: ";
01926         mainHeader += contentDispClear;
01927       }
01928       if( 0 < contentTEncClear.length() ) {
01929         mainHeader += "\nContent-Transfer-Encoding: ";
01930         mainHeader += contentTEncClear;
01931       }
01932     }
01933 
01934     //kdDebug(5006) << "processStructuringInfo: mainHeader=" << mainHeader << endl;
01935 
01936     DwString mainDwStr;
01937     mainDwStr = mainHeader + "\n\n";
01938     DwBodyPart mainDwPa( mainDwStr, 0 );
01939     mainDwPa.Parse();
01940     KMMessage::bodyPart( &mainDwPa, &resultingPart );
01941     if( !makeMultiMime( format, signing ) ) {
01942       if ( signing && includeCleartextWhenSigning( format ) ) {
01943         QByteArray bodyText( clearCStr );
01944         KMail::Util::append( bodyText, "\n" );
01945         KMail::Util::append( bodyText, ciphertext );
01946         resultingPart.setBodyEncodedBinary( bodyText );
01947       } else {
01948         resultingPart.setBodyEncodedBinary( ciphertext );
01949       }
01950     } else {
01951       // Build the encapsulated MIME parts.
01952       // Build a MIME part holding the version information
01953       // taking the body contents returned in
01954       // structuring.data.bodyTextVersion.
01955       QCString versCStr, codeCStr;
01956       if ( !signing && format == Kleo::OpenPGPMIMEFormat )
01957         versCStr =
01958       "Content-Type: application/pgp-encrypted\n"
01959       "Content-Disposition: attachment\n"
01960       "\n"
01961       "Version: 1";
01962 
01963       // Build a MIME part holding the code information
01964       // taking the body contents returned in ciphertext.
01965       const char * nestedCT = nestedContentType( format, signing );
01966       assert( nestedCT );
01967       codeCStr = "Content-Type: ";
01968       codeCStr += nestedCT;
01969       codeCStr += '\n';
01970       if ( const char * str = nestedContentDisposition( format, signing ) ) {
01971     codeCStr += "Content-Disposition: ";
01972     codeCStr += str;
01973     codeCStr += '\n';
01974       }
01975       if ( binaryHint( format ) ) {
01976     codeCStr += "Content-Transfer-Encoding: base64\n\n";
01977     codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext );
01978       } else
01979     codeCStr += '\n' + QCString( ciphertext.data(), ciphertext.size() + 1 );
01980 
01981 
01982       QByteArray mainStr;
01983       KMail::Util::append( mainStr, "--" );
01984       KMail::Util::append( mainStr, boundaryCStr );
01985       if ( signing && includeCleartextWhenSigning( format ) &&
01986        !clearCStr.isEmpty() ) {
01987         KMail::Util::append( mainStr, "\n" );
01988         // clearCStr is the one that can be very big for large attachments, don't merge with the other lines
01989         KMail::Util::append( mainStr, clearCStr );
01990         KMail::Util::append( mainStr, "\n--" + boundaryCStr );
01991       }
01992       if ( !versCStr.isEmpty() )
01993         KMail::Util::append( mainStr, "\n" + versCStr + "\n--" + boundaryCStr );
01994       if( !codeCStr.isEmpty() )
01995         KMail::Util::append( mainStr, "\n" + codeCStr + "\n--" + boundaryCStr );
01996       KMail::Util::append( mainStr, "--\n" );
01997 
01998       //kdDebug(5006) << "processStructuringInfo: mainStr=" << mainStr << endl;
01999       resultingPart.setBodyEncodedBinary( mainStr );
02000     }
02001 
02002   } else { //  not making a mime object, build a plain message body.
02003 
02004     resultingPart.setContentDescription( contentDescClear );
02005     resultingPart.setTypeStr( contentTypeClear );
02006     resultingPart.setSubtypeStr( contentSubtypeClear );
02007     resultingPart.setContentDisposition( contentDispClear );
02008     resultingPart.setContentTransferEncodingStr( contentTEncClear );
02009     QByteArray resultingBody;
02010 
02011     if ( signing && includeCleartextWhenSigning( format ) ) {
02012       if( !clearCStr.isEmpty() )
02013         KMail::Util::append( resultingBody, clearCStr );
02014     }
02015     if ( !ciphertext.isEmpty() )
02016       KMail::Util::append( resultingBody, ciphertext );
02017     else {
02018       // Plugin error!
02019       KMessageBox::sorry( mComposeWin,
02020                           i18n( "<qt><p>Error: The backend did not return "
02021                                 "any encoded data.</p>"
02022                                 "<p>Please report this bug:<br>%2</p></qt>" )
02023                           .arg( bugURL ) );
02024       bOk = false;
02025     }
02026     resultingPart.setBodyEncodedBinary( resultingBody );
02027   }
02028 
02029   return bOk;
02030 }
02031 
02032 //-----------------------------------------------------------------------------
02033 QCString MessageComposer::plainTextFromMarkup( const QString& markupText )
02034 {
02035   QTextEdit *hackConspiratorTextEdit = new QTextEdit( markupText );
02036   hackConspiratorTextEdit->setTextFormat(Qt::PlainText);
02037   if ( !mDisableBreaking ) {
02038     hackConspiratorTextEdit->setWordWrap( QTextEdit::FixedColumnWidth );
02039     hackConspiratorTextEdit->setWrapColumnOrWidth( mLineBreakColumn );
02040   }
02041   QString text = hackConspiratorTextEdit->text();
02042   QCString textbody;
02043 
02044   const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02045   if( mCharset == "us-ascii" ) {
02046     textbody = KMMsgBase::toUsAscii( text );
02047   } else if( codec == 0 ) {
02048     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02049     textbody = text.local8Bit();
02050   } else {
02051     textbody = codec->fromUnicode( text );
02052   }
02053   if (textbody.isNull()) textbody = "";
02054 
02055   delete hackConspiratorTextEdit;
02056   return textbody;
02057 }
02058 
02059 //-----------------------------------------------------------------------------
02060 QByteArray MessageComposer::breakLinesAndApplyCodec()
02061 {
02062   QString text;
02063   QCString cText;
02064 
02065   if( mDisableBreaking || mIsRichText )
02066     text = mComposeWin->mEditor->text();
02067   else
02068     text = mComposeWin->mEditor->brokenText();
02069   text.truncate( text.length() ); // to ensure text.size()==text.length()+1
02070 
02071   QString newText;
02072   const QTextCodec *codec = KMMsgBase::codecForName( mCharset );
02073 
02074   if( mCharset == "us-ascii" ) {
02075     cText = KMMsgBase::toUsAscii( text );
02076     newText = QString::fromLatin1( cText );
02077   } else if( codec == 0 ) {
02078     kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
02079     cText = text.local8Bit();
02080     newText = QString::fromLocal8Bit( cText );
02081   } else {
02082     cText = codec->fromUnicode( text );
02083     newText = codec->toUnicode( cText );
02084   }
02085   if (cText.isNull()) cText = "";
02086 
02087   if( !text.isEmpty() && (newText != text) ) {
02088     QString oldText = mComposeWin->mEditor->text();
02089     mComposeWin->mEditor->setText( newText );
02090     KCursorSaver idle( KBusyPtr::idle() );
02091     bool anyway = ( KMessageBox::warningYesNo( mComposeWin,
02092                                                i18n("<qt>Not all characters fit into the chosen"
02093                                                     " encoding.<br><br>Send the message anyway?</qt>"),
02094                                                i18n("Some Characters Will Be Lost"),
02095                                                i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes );
02096     if( !anyway ) {
02097       mComposeWin->mEditor->setText(oldText);
02098       return QByteArray();
02099     }
02100   }
02101 
02102   // From RFC 3156:
02103   //  Note: The accepted OpenPGP convention is for signed data to end
02104   //  with a <CR><LF> sequence.  Note that the <CR><LF> sequence
02105   //  immediately preceding a MIME boundary delimiter line is considered
02106   //  to be part of the delimiter in [3], 5.1.  Thus, it is not part of
02107   //  the signed data preceding the delimiter line.  An implementation
02108   //  which elects to adhere to the OpenPGP convention has to make sure
02109   //  it inserts a <CR><LF> pair on the last line of the data to be
02110   //  signed and transmitted (signed message and transmitted message
02111   //  MUST be identical).
02112   // So make sure that the body ends with a <LF>.
02113   if( cText.isEmpty() || cText[cText.length()-1] != '\n' ) {
02114     kdDebug(5006) << "Added an <LF> on the last line" << endl;
02115     cText += "\n";
02116   }
02117   return KMail::Util::byteArrayFromQCStringNoDetach( cText );
02118 }
02119 
02120 
02121 //-----------------------------------------------------------------------------
02122 void MessageComposer::pgpSignedMsg( const QByteArray& cText, Kleo::CryptoMessageFormat format ) {
02123 
02124   assert( cText.isEmpty() || cText[cText.size()-1] != '\0' ); // I was called with a QCString !?
02125   mSignature = QByteArray();
02126 
02127   const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format );
02128 
02129   assert( !signingKeys.empty() );
02130 
02131   // TODO: ASync call? Likely, yes :-)
02132   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02133   assert( cpf );
02134   const Kleo::CryptoBackend::Protocol * proto
02135     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02136   assert( proto ); 
02137 
02138   std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ),
02139                             textMode( format ) ) );
02140 
02141   if ( !job.get() ) {
02142     KMessageBox::sorry( mComposeWin,
02143             i18n("This message could not be signed, "
02144                  "since the chosen backend does not seem to support "
02145                  "signing; this should actually never happen, "
02146                  "please report this bug.") );
02147     return;
02148   }
02149 
02150   QByteArray signature;
02151   const GpgME::SigningResult res =
02152     job->exec( signingKeys, cText, signingMode( format ), signature );
02153   if ( res.error().isCanceled() ) {
02154     kdDebug() << "signing was canceled by user" << endl;
02155     return;
02156   }
02157   if ( res.error() ) {
02158     kdDebug() << "signing failed: " << res.error().asString() << endl;
02159     job->showErrorDialog( mComposeWin );
02160     return;
02161   }
02162 
02163   mSignature = signature;
02164   if ( mSignature.isEmpty() ) {
02165     KMessageBox::sorry( mComposeWin,
02166                         i18n( "The signing operation failed. "
02167                               "Please make sure that the gpg-agent program "
02168                               "is running." ) );
02169   }
02170 }
02171 
02172 //-----------------------------------------------------------------------------
02173 Kpgp::Result MessageComposer::pgpEncryptedMsg( QByteArray & encryptedBody,
02174                                                const QByteArray& cText,
02175                                                const std::vector<GpgME::Key> & encryptionKeys,
02176                            Kleo::CryptoMessageFormat format )
02177 {
02178   // TODO: ASync call? Likely, yes :-)
02179   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02180   assert( cpf );
02181   const Kleo::CryptoBackend::Protocol * proto
02182     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02183   assert( proto ); // hmmmm....?
02184 
02185   std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ),
02186                               textMode( format ) ) );
02187   if ( !job.get() ) {
02188     KMessageBox::sorry( mComposeWin,
02189             i18n("This message could not be encrypted, "
02190                  "since the chosen backend does not seem to support "
02191                  "encryption; this should actually never happen, "
02192                  "please report this bug.") );
02193     return Kpgp::Failure;
02194   }
02195 
02196   const GpgME::EncryptionResult res =
02197     job->exec( encryptionKeys, cText, false, encryptedBody );
02198   if ( res.error().isCanceled() ) {
02199     kdDebug() << "encryption was canceled by user" << endl;
02200     return Kpgp::Canceled;
02201   }
02202   if ( res.error() ) {
02203     kdDebug() << "encryption failed: " << res.error().asString() << endl;
02204     job->showErrorDialog( mComposeWin );
02205     return Kpgp::Failure;
02206   }
02207   return Kpgp::Ok;
02208 }
02209 
02210 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( QByteArray & encryptedBody,
02211                             const QByteArray& cText,
02212                             const std::vector<GpgME::Key> & signingKeys,
02213                             const std::vector<GpgME::Key> & encryptionKeys,
02214                             Kleo::CryptoMessageFormat format )
02215 {
02216   // TODO: ASync call? Likely, yes :-)
02217   const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance();
02218   assert( cpf );
02219   const Kleo::CryptoBackend::Protocol * proto
02220     = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ;
02221   assert( proto ); // hmmmm....?
02222 
02223   std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ),
02224                                   textMode( format ) ) );
02225   if ( !job.get() ) {
02226     KMessageBox::sorry( mComposeWin,
02227             i18n("This message could not be signed and encrypted, "
02228                  "since the chosen backend does not seem to support "
02229                  "combined signing and encryption; this should actually never happen, "
02230                  "please report this bug.") );
02231     return Kpgp::Failure;
02232   }
02233 
02234   const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res =
02235     job->exec( signingKeys, encryptionKeys, cText, false, encryptedBody );
02236   if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) {
02237     kdDebug() << "encrypt/sign was canceled by user" << endl;
02238     return Kpgp::Canceled;
02239   }
02240   if ( res.first.error() || res.second.error() ) {
02241     if ( res.first.error() )
02242       kdDebug() << "signing failed: " << res.first.error().asString() << endl;
02243     else
02244       kdDebug() << "encryption failed: " << res.second.error().asString() << endl;
02245     job->showErrorDialog( mComposeWin );
02246     return Kpgp::Failure;
02247   }
02248   return Kpgp::Ok;
02249 }
02250 
02251 
02252 #include "messagecomposer.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys