kmail

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