kmail

messagecomposer.cpp

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