kmail Library API Documentation

messagecomposer.cpp

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