kmail Library API Documentation

messagecomposer.cpp

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