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