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) 00431 mBcc = mReferenceMessage->bcc(); 00432 mTo = KPIM::splitEmailAddrList( mComposeWin->to().stripWhiteSpace() ); 00433 mCc = KPIM::splitEmailAddrList( mComposeWin->cc().stripWhiteSpace() ); 00434 mBccList = KPIM::splitEmailAddrList( mBcc.stripWhiteSpace() ); 00435 00436 for ( unsigned int i = 0 ; i < mComposeWin->mAtmList.count() ; ++i ) 00437 mAttachments.push_back( Attachment( mComposeWin->mAtmList.at(i), 00438 mComposeWin->signFlagOfAttachment( i ), 00439 mComposeWin->encryptFlagOfAttachment( i ) ) ); 00440 } 00441 00442 void MessageComposer::adjustCryptFlags() 00443 { 00444 if ( !mDisableCrypto && 00445 mAllowedCryptoMessageFormats & Kleo::InlineOpenPGPFormat && 00446 !mAttachments.empty() && 00447 ( mSigningRequested || mEncryptionRequested ) ) 00448 { 00449 int ret; 00450 if ( mAllowedCryptoMessageFormats == Kleo::InlineOpenPGPFormat ) { 00451 ret = KMessageBox::warningYesNoCancel( mComposeWin, 00452 i18n("The inline OpenPGP crypto message format " 00453 "does not support encryption or signing " 00454 "of attachments.\n" 00455 "Really use deprecated inline OpenPGP?"), 00456 i18n("Insecure Message Format"), 00457 KStdGuiItem::yes(), 00458 i18n("&No, Use OpenPGP/MIME") ); 00459 } 00460 else { 00461 // if other crypto message formats are allowed then simply don't use 00462 // inline OpenPGP 00463 ret = KMessageBox::No; 00464 } 00465 00466 if ( ret == KMessageBox::Cancel ) { 00467 mRc = false; 00468 return; 00469 } else if ( ret == KMessageBox::No ) { 00470 mAllowedCryptoMessageFormats &= ~Kleo::InlineOpenPGPFormat; 00471 mAllowedCryptoMessageFormats |= Kleo::OpenPGPMIMEFormat; 00472 if ( mSigningRequested ) { 00473 // The composer window disabled signing on the attachments, re-enable it 00474 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) 00475 mAttachments[idx].sign = true; 00476 } 00477 if ( mEncryptionRequested ) { 00478 // The composer window disabled encrypting on the attachments, re-enable it 00479 // We assume this is what the user wants - after all he chose OpenPGP/MIME for this. 00480 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) 00481 mAttachments[idx].encrypt = true; 00482 } 00483 } 00484 } 00485 00486 mKeyResolver = 00487 new Kleo::KeyResolver( encryptToSelf(), showKeyApprovalDialog(), 00488 mUseOpportunisticEncryption, mAllowedCryptoMessageFormats, 00489 encryptKeyNearExpiryWarningThresholdInDays(), 00490 signingKeyNearExpiryWarningThresholdInDays(), 00491 encryptRootCertNearExpiryWarningThresholdInDays(), 00492 signingRootCertNearExpiryWarningThresholdInDays(), 00493 encryptChainCertNearExpiryWarningThresholdInDays(), 00494 signingChainCertNearExpiryWarningThresholdInDays() ); 00495 00496 if ( !mDisableCrypto ) { 00497 const KPIM::Identity & id = mComposeWin->identity(); 00498 00499 QStringList encryptToSelfKeys; 00500 if ( !id.pgpEncryptionKey().isEmpty() ) 00501 encryptToSelfKeys.push_back( id.pgpEncryptionKey() ); 00502 if ( !id.smimeEncryptionKey().isEmpty() ) 00503 encryptToSelfKeys.push_back( id.smimeEncryptionKey() ); 00504 if ( mKeyResolver->setEncryptToSelfKeys( encryptToSelfKeys ) != Kpgp::Ok ) { 00505 mRc = false; 00506 return; 00507 } 00508 00509 QStringList signKeys; 00510 if ( !id.pgpSigningKey().isEmpty() ) 00511 signKeys.push_back( mPGPSigningKey = id.pgpSigningKey() ); 00512 if ( !id.smimeSigningKey().isEmpty() ) 00513 signKeys.push_back( mSMIMESigningKey = id.smimeSigningKey() ); 00514 if ( mKeyResolver->setSigningKeys( signKeys ) != Kpgp::Ok ) { 00515 mRc = false; 00516 return; 00517 } 00518 } 00519 00520 mKeyResolver->setPrimaryRecipients( mTo + mCc ); 00521 mKeyResolver->setSecondaryRecipients( mBccList ); 00522 00523 // check settings of composer buttons *and* attachment check boxes 00524 bool doSignCompletely = mSigningRequested; 00525 bool doEncryptCompletely = mEncryptionRequested; 00526 for ( unsigned int idx = 0 ; idx < mAttachments.size() ; ++idx ) { 00527 if ( mAttachments[idx].encrypt ) 00528 mEncryptionRequested = true; 00529 else 00530 doEncryptCompletely = false; 00531 if ( mAttachments[idx].sign ) 00532 mSigningRequested = true; 00533 else 00534 doSignCompletely = false; 00535 } 00536 00537 mDoSign = !mDisableCrypto && determineWhetherToSign( doSignCompletely ); 00538 00539 if ( !mRc ) 00540 return; 00541 00542 mDoEncrypt = !mDisableCrypto && determineWhetherToEncrypt( doEncryptCompletely ); 00543 00544 if ( !mRc ) 00545 return; 00546 00547 // resolveAllKeys needs to run even if mDisableCrypto == true, since 00548 // we depend on it collecting all recipients into one dummy 00549 // SplitInfo to avoid special-casing all over the place: 00550 if ( mKeyResolver->resolveAllKeys( mDoSign, mDoEncrypt ) != Kpgp::Ok ) 00551 mRc = false; 00552 } 00553 00554 bool MessageComposer::determineWhetherToSign( bool doSignCompletely ) { 00555 bool sign = false; 00556 switch ( mKeyResolver->checkSigningPreferences( mSigningRequested ) ) { 00557 case Kleo::DoIt: 00558 if ( !mSigningRequested ) { 00559 markAllAttachmentsForSigning( true ); 00560 return true; 00561 } 00562 sign = true; 00563 break; 00564 case Kleo::DontDoIt: 00565 sign = false; 00566 break; 00567 case Kleo::AskOpportunistic: 00568 assert( 0 ); 00569 case Kleo::Ask: 00570 { 00571 // the user wants to be asked or has to be asked 00572 const KCursorSaver idle( KBusyPtr::idle() ); 00573 const QString msg = i18n("Examination of the recipient's signing preferences " 00574 "yielded that you be asked whether or not to sign " 00575 "this message.\n" 00576 "Sign this message?"); 00577 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg, 00578 i18n("Sign Message?"), 00579 i18n("to sign","&Sign"), 00580 i18n("Do &Not Sign") ) ) { 00581 case KMessageBox::Cancel: 00582 mRc = false; 00583 return false; 00584 case KMessageBox::Yes: 00585 markAllAttachmentsForSigning( true ); 00586 return true; 00587 case KMessageBox::No: 00588 markAllAttachmentsForSigning( false ); 00589 return false; 00590 } 00591 } 00592 break; 00593 case Kleo::Conflict: 00594 { 00595 // warn the user that there are conflicting signing preferences 00596 const KCursorSaver idle( KBusyPtr::idle() ); 00597 const QString msg = i18n("There are conflicting signing preferences " 00598 "for these recipients.\n" 00599 "Sign this message?"); 00600 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg, 00601 i18n("Sign Message?"), 00602 i18n("to sign","&Sign"), 00603 i18n("Do &Not Sign") ) ) { 00604 case KMessageBox::Cancel: 00605 mRc = false; 00606 return false; 00607 case KMessageBox::Yes: 00608 markAllAttachmentsForSigning( true ); 00609 return true; 00610 case KMessageBox::No: 00611 markAllAttachmentsForSigning( false ); 00612 return false; 00613 } 00614 } 00615 break; 00616 case Kleo::Impossible: 00617 { 00618 const KCursorSaver idle( KBusyPtr::idle() ); 00619 const QString msg = i18n("You have requested to sign this message, " 00620 "but no valid signing keys have been configured " 00621 "for this identity.\n" 00622 "If you choose to continue, " 00623 "no signing will be performed."); 00624 if ( KMessageBox::warningContinueCancel( mComposeWin, msg, 00625 i18n("Send &Unsigned") ) 00626 == KMessageBox::Cancel ) { 00627 mRc = false; 00628 return false; 00629 } else { 00630 markAllAttachmentsForSigning( false ); 00631 return false; 00632 } 00633 } 00634 } 00635 00636 if ( !sign || !doSignCompletely ) { 00637 if ( warnSendUnsigned() ) { 00638 const KCursorSaver idle( KBusyPtr::idle() ); 00639 const QString msg = sign && !doSignCompletely 00640 ? i18n("Some parts of this message will not be signed.\n" 00641 "Sending only partially signed messages might violate site policy.\n" 00642 "Sign all parts instead?") // oh, I hate this... 00643 : i18n("This message will not be signed.\n" 00644 "Sending unsigned message might violate site policy.\n" 00645 "Sign message instead?") ; // oh, I hate this... 00646 const QString buttonText = sign && !doSignCompletely 00647 ? i18n("&Sign All Parts") : i18n("&Sign") ; 00648 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg, 00649 i18n("Unsigned-Message Warning"), 00650 buttonText, 00651 i18n("Send &As Is") ) ) { 00652 case KMessageBox::Cancel: 00653 mRc = false; 00654 return false; 00655 case KMessageBox::Yes: 00656 markAllAttachmentsForSigning( true ); 00657 return true; 00658 case KMessageBox::No: 00659 return sign || doSignCompletely; 00660 } 00661 } 00662 } 00663 00664 return sign || doSignCompletely ; 00665 } 00666 00667 bool MessageComposer::determineWhetherToEncrypt( bool doEncryptCompletely ) { 00668 bool encrypt = false; 00669 bool opportunistic = false; 00670 switch ( mKeyResolver->checkEncryptionPreferences( mEncryptionRequested ) ) { 00671 case Kleo::DoIt: 00672 if ( !mEncryptionRequested ) { 00673 markAllAttachmentsForEncryption( true ); 00674 return true; 00675 } 00676 encrypt = true; 00677 break; 00678 case Kleo::DontDoIt: 00679 encrypt = false; 00680 break; 00681 case Kleo::AskOpportunistic: 00682 opportunistic = true; 00683 // fall through... 00684 case Kleo::Ask: 00685 { 00686 // the user wants to be asked or has to be asked 00687 const KCursorSaver idle( KBusyPtr::idle() ); 00688 const QString msg = opportunistic 00689 ? i18n("Valid trusted encryption keys were found for all recipients.\n" 00690 "Encrypt this message?") 00691 : i18n("Examination of the recipient's encryption preferences " 00692 "yielded that you be asked whether or not to encrypt " 00693 "this message.\n" 00694 "Encrypt this message?"); 00695 switch ( KMessageBox::questionYesNoCancel( mComposeWin, msg, 00696 i18n("Encrypt Message?"), 00697 mDoSign 00698 ? i18n("Sign && &Encrypt") 00699 : i18n("&Encrypt"), 00700 mDoSign 00701 ? i18n("&Sign Only") 00702 : i18n("&Send As-Is") ) ) { 00703 case KMessageBox::Cancel: 00704 mRc = false; 00705 return false; 00706 case KMessageBox::Yes: 00707 markAllAttachmentsForEncryption( true ); 00708 return true; 00709 case KMessageBox::No: 00710 markAllAttachmentsForEncryption( false ); 00711 return false; 00712 } 00713 } 00714 break; 00715 case Kleo::Conflict: 00716 { 00717 // warn the user that there are conflicting encryption preferences 00718 const KCursorSaver idle( KBusyPtr::idle() ); 00719 const QString msg = i18n("There are conflicting encryption preferences " 00720 "for these recipients.\n" 00721 "Encrypt this message?"); 00722 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg, 00723 i18n("Encrypt Message?"), 00724 i18n("&Encrypt"), 00725 i18n("Do &Not Encrypt") ) ) { 00726 case KMessageBox::Cancel: 00727 mRc = false; 00728 return false; 00729 case KMessageBox::Yes: 00730 markAllAttachmentsForEncryption( true ); 00731 return true; 00732 case KMessageBox::No: 00733 markAllAttachmentsForEncryption( false ); 00734 return false; 00735 } 00736 } 00737 break; 00738 case Kleo::Impossible: 00739 { 00740 const KCursorSaver idle( KBusyPtr::idle() ); 00741 const QString msg = i18n("You have requested to encrypt this message, " 00742 "and to encrypt a copy to yourself, " 00743 "but no valid trusted encryption keys have been " 00744 "configured for this identity."); 00745 if ( KMessageBox::warningContinueCancel( mComposeWin, msg, 00746 i18n("Send &Unencrypted") ) 00747 == KMessageBox::Cancel ) { 00748 mRc = false; 00749 return false; 00750 } else { 00751 markAllAttachmentsForEncryption( false ); 00752 return false; 00753 } 00754 } 00755 } 00756 00757 if ( !encrypt || !doEncryptCompletely ) { 00758 if ( warnSendUnencrypted() ) { 00759 const KCursorSaver idle( KBusyPtr::idle() ); 00760 const QString msg = !doEncryptCompletely 00761 ? i18n("Some parts of this message will not be encrypted.\n" 00762 "Sending only partially encrypted messages might violate site policy " 00763 "and/or leak sensitive information.\n" 00764 "Encrypt all parts instead?") // oh, I hate this... 00765 : i18n("This message will not be encrypted.\n" 00766 "Sending unencrypted messages might violate site policy and/or " 00767 "leak sensitive information.\n" 00768 "Encrypt messages instead?") ; // oh, I hate this... 00769 const QString buttonText = !doEncryptCompletely 00770 ? i18n("&Encrypt All Parts") : i18n("&Encrypt") ; 00771 switch ( KMessageBox::warningYesNoCancel( mComposeWin, msg, 00772 i18n("Unencrypted Message Warning"), 00773 buttonText, 00774 mDoSign 00775 ? i18n("&Sign Only") 00776 : i18n("&Send As-Is") ) ) { 00777 case KMessageBox::Cancel: 00778 mRc = false; 00779 return false; 00780 case KMessageBox::Yes: 00781 markAllAttachmentsForEncryption( true ); 00782 return true; 00783 case KMessageBox::No: 00784 return encrypt || doEncryptCompletely; 00785 } 00786 } 00787 } 00788 00789 return encrypt || doEncryptCompletely ; 00790 } 00791 00792 void MessageComposer::markAllAttachmentsForSigning( bool sign ) { 00793 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) 00794 it->sign = sign; 00795 } 00796 00797 void MessageComposer::markAllAttachmentsForEncryption( bool enc ) { 00798 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) 00799 it->encrypt = enc; 00800 } 00801 00802 00803 void MessageComposer::composeMessage() 00804 { 00805 for ( unsigned int i = 0 ; i < numFormats ; ++i ) { 00806 if ( mKeyResolver->encryptionItems( formats[i] ).empty() ) 00807 continue; 00808 KMMessage * msg = new KMMessage( *mReferenceMessage ); 00809 composeMessage( *msg, mDoSign, mDoEncrypt, formats[i] ); 00810 if ( !mRc ) 00811 return; 00812 } 00813 } 00814 00815 // 00816 // These are replacements for StructuringInfo(Wrapper): 00817 // 00818 00819 // check whether to use multipart/{signed,encrypted} 00820 static inline bool makeMultiMime( Kleo::CryptoMessageFormat f, bool sign ) { 00821 switch ( f ) { 00822 default: 00823 case Kleo::InlineOpenPGPFormat: 00824 case Kleo::SMIMEOpaqueFormat: return false; 00825 case Kleo::OpenPGPMIMEFormat: return true; 00826 case Kleo::SMIMEFormat: return sign; // only on sign - there's no mp/encrypted for S/MIME 00827 } 00828 } 00829 static inline bool makeMultiPartSigned( Kleo::CryptoMessageFormat f ) { 00830 return makeMultiMime( f, true ); 00831 } 00832 static inline bool makeMultiPartEncrypted( Kleo::CryptoMessageFormat f ) { 00833 return makeMultiMime( f, false ); 00834 } 00835 00836 static inline bool makeMimeObject( Kleo::CryptoMessageFormat f, bool /*signing*/ ) { 00837 return f != Kleo::InlineOpenPGPFormat; 00838 } 00839 00840 static inline const char * toplevelContentType( Kleo::CryptoMessageFormat f, bool signing ) { 00841 switch ( f ) { 00842 default: 00843 case Kleo::InlineOpenPGPFormat: return 0; 00844 case Kleo::OpenPGPMIMEFormat: 00845 return signing ? 00846 "multipart/signed;\n\t" 00847 "boundary=\"%boundary\";\n\t" 00848 "protocol=\"application/pgp-signature\";\n\t" 00849 "micalg=pgp-sha1" // FIXME: obtain this parameter from gpgme! 00850 : 00851 "multipart/encrypted;\n\t" 00852 "boundary=\"%boundary\";\n\t" 00853 "protocol=\"application/pgp-encrypted\"" 00854 ; 00855 case Kleo::SMIMEFormat: 00856 if ( signing ) 00857 return 00858 "multipart/signed;\n\t" 00859 "boundary=\"%boundary\";\n\t" 00860 "protocol=\"application/pkcs7-signature\";\n\t" 00861 "micalg=sha1"; // FIXME: obtain this parameter from gpgme! 00862 // fall through (for encryption, there's no difference between 00863 // SMIME and SMIMEOpaque, since there is no mp/encrypted for 00864 // S/MIME): 00865 case Kleo::SMIMEOpaqueFormat: 00866 return signing ? 00867 "application/pkcs7-mime;\n\t" 00868 "smime-type=signed-data;\n\t" 00869 "name=\"smime.p7m\";\n\t" 00870 : 00871 "application/pkcs7-mime;\n\t" 00872 "smime-type=enveloped-data;\n\t" 00873 "name=\"smime.p7m\";\n\t" 00874 ; 00875 } 00876 } 00877 00878 static inline const char * toplevelContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) { 00879 switch ( f ) { 00880 default: 00881 case Kleo::InlineOpenPGPFormat: 00882 case Kleo::OpenPGPMIMEFormat: 00883 return 0; 00884 case Kleo::SMIMEFormat: 00885 if ( signing ) 00886 return 0; 00887 case Kleo::SMIMEOpaqueFormat: 00888 return "attachment; filename=\"smime.p7m\""; 00889 } 00890 } 00891 00892 static inline bool includeCleartextWhenSigning( Kleo::CryptoMessageFormat f ) { 00893 return makeMultiPartSigned( f ); 00894 } 00895 00896 static inline const char * nestedContentType( Kleo::CryptoMessageFormat f, bool signing ) { 00897 switch ( f ) { 00898 case Kleo::OpenPGPMIMEFormat: 00899 return signing ? "application/pgp-signature" : "application/octet-stream" ; 00900 case Kleo::SMIMEFormat: 00901 if ( signing ) 00902 return "application/pkcs7-signature; name=\"smime.p7s\""; 00903 // fall through: 00904 default: 00905 case Kleo::InlineOpenPGPFormat: 00906 case Kleo::SMIMEOpaqueFormat: 00907 return 0; 00908 } 00909 } 00910 00911 static inline const char * nestedContentDisposition( Kleo::CryptoMessageFormat f, bool signing ) { 00912 if ( !signing && f == Kleo::OpenPGPMIMEFormat ) 00913 return "inline; filename=\"msg.asc\""; 00914 if ( signing && f == Kleo::SMIMEFormat ) 00915 return "attachment; filename=\"smime.p7s\""; 00916 return 0; 00917 } 00918 00919 static inline bool binaryHint( Kleo::CryptoMessageFormat f ) { 00920 switch ( f ) { 00921 case Kleo::SMIMEFormat: 00922 case Kleo::SMIMEOpaqueFormat: 00923 return true; 00924 default: 00925 case Kleo::OpenPGPMIMEFormat: 00926 case Kleo::InlineOpenPGPFormat: 00927 return false; 00928 } 00929 } 00930 00931 static inline bool isSMIME( Kleo::CryptoMessageFormat f ) { 00932 return f == Kleo::SMIMEFormat || f == Kleo::SMIMEOpaqueFormat ; 00933 } 00934 00935 static inline bool armor( Kleo::CryptoMessageFormat f ) { 00936 return !binaryHint( f ); 00937 } 00938 00939 static inline bool textMode( Kleo::CryptoMessageFormat f ) { 00940 return f == Kleo::InlineOpenPGPFormat; 00941 } 00942 00943 static inline GpgME::Context::SignatureMode signingMode( Kleo::CryptoMessageFormat f ) { 00944 switch ( f ) { 00945 case Kleo::SMIMEOpaqueFormat: 00946 return GpgME::Context::Normal; 00947 case Kleo::InlineOpenPGPFormat: 00948 return GpgME::Context::Clearsigned; 00949 default: 00950 case Kleo::SMIMEFormat: 00951 case Kleo::OpenPGPMIMEFormat: 00952 return GpgME::Context::Detached; 00953 } 00954 } 00955 00956 // 00957 // END replacements for StructuringInfo(Wrapper) 00958 // 00959 00960 class EncryptMessageJob : public MessageComposerJob { 00961 public: 00962 EncryptMessageJob( KMMessage* msg, const Kleo::KeyResolver::SplitInfo & si, 00963 bool doSign, bool doEncrypt, const QCString& encodedBody, 00964 int boundaryLevel, const KMMessagePart& oldBodyPart, 00965 KMMessagePart* newBodyPart, Kleo::CryptoMessageFormat format, 00966 MessageComposer* composer ) 00967 : MessageComposerJob( composer ), mMsg( msg ), mSplitInfo( si ), 00968 mDoSign( doSign ), mDoEncrypt( doEncrypt ), mEncodedBody( encodedBody ), 00969 mBoundaryLevel( boundaryLevel ), mOldBodyPart( oldBodyPart ), 00970 mNewBodyPart( newBodyPart ), mFormat( format ) {} 00971 00972 void execute() { 00973 KMMessagePart tmpNewBodyPart = *mNewBodyPart; 00974 00975 // TODO: Async call 00976 00977 mComposer->encryptMessage( mMsg, mSplitInfo, mDoSign, mDoEncrypt, 00978 tmpNewBodyPart, mFormat ); 00979 if ( !mComposer->mRc ) { 00980 delete mMsg; mMsg = 0; 00981 return; 00982 } 00983 mComposer->mMessageList.push_back( mMsg ); 00984 } 00985 00986 private: 00987 KMMessage* mMsg; 00988 Kleo::KeyResolver::SplitInfo mSplitInfo; 00989 bool mDoSign, mDoEncrypt; 00990 QCString mEncodedBody; 00991 int mBoundaryLevel; 00992 KMMessagePart mOldBodyPart; 00993 KMMessagePart* mNewBodyPart; 00994 Kleo::CryptoMessageFormat mFormat; 00995 }; 00996 00997 class SetLastMessageAsUnencryptedVersionOfLastButOne : public MessageComposerJob { 00998 public: 00999 SetLastMessageAsUnencryptedVersionOfLastButOne( MessageComposer * composer ) 01000 : MessageComposerJob( composer ) {} 01001 01002 void execute() { 01003 KMMessage * last = mComposer->mMessageList.back(); 01004 mComposer->mMessageList.pop_back(); 01005 mComposer->mMessageList.back()->setUnencryptedMsg( last ); 01006 } 01007 }; 01008 01009 QCString MessageComposer::bodyText() 01010 { 01011 QCString body = breakLinesAndApplyCodec(); 01012 01013 if (body.isNull()) return body; 01014 01015 if (body.isEmpty()) body = "\n"; // don't crash 01016 01017 // From RFC 3156: 01018 // Note: The accepted OpenPGP convention is for signed data to end 01019 // with a <CR><LF> sequence. Note that the <CR><LF> sequence 01020 // immediately preceding a MIME boundary delimiter line is considered 01021 // to be part of the delimiter in [3], 5.1. Thus, it is not part of 01022 // the signed data preceding the delimiter line. An implementation 01023 // which elects to adhere to the OpenPGP convention has to make sure 01024 // it inserts a <CR><LF> pair on the last line of the data to be 01025 // signed and transmitted (signed message and transmitted message 01026 // MUST be identical). 01027 // So make sure that the body ends with a <LF>. 01028 if( body[body.length()-1] != '\n' ) { 01029 kdDebug(5006) << "Added an <LF> on the last line" << endl; 01030 body += "\n"; 01031 } 01032 return body; 01033 } 01034 01035 void MessageComposer::composeInlineOpenPGPMessage( KMMessage& theMessage, 01036 bool doSign, bool doEncrypt ) 01037 { 01038 // preprocess the body text 01039 QCString body = bodyText(); 01040 if (body.isNull()) { 01041 mRc = false; 01042 return; 01043 } 01044 01045 mNewBodyPart = 0; // unused 01046 mEarlyAddAttachments = false; 01047 mAllAttachmentsAreInBody = false; 01048 01049 // set the main headers 01050 theMessage.deleteBodyParts(); 01051 QString oldContentType = theMessage.headerField( "Content-Type" ); 01052 theMessage.removeHeaderField("Content-Type"); 01053 theMessage.removeHeaderField("Content-Transfer-Encoding"); 01054 01055 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos 01056 = mKeyResolver->encryptionItems( Kleo::InlineOpenPGPFormat ); 01057 kdWarning( splitInfos.empty() ) 01058 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for InlineOpenPGPFormat" 01059 << endl; 01060 std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it; 01061 for ( it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) { 01062 const Kleo::KeyResolver::SplitInfo& splitInfo = *it; 01063 KMMessage* msg = new KMMessage( theMessage ); 01064 if ( doEncrypt ) { 01065 Kpgp::Result result; 01066 QByteArray encryptedBody; 01067 if ( doSign ) { // Sign and encrypt 01068 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( Kleo::InlineOpenPGPFormat ); 01069 result = pgpSignedAndEncryptedMsg( encryptedBody, body, signingKeys, 01070 splitInfo.keys, Kleo::InlineOpenPGPFormat ); 01071 } else { // Encrypt but don't sign 01072 result = pgpEncryptedMsg( encryptedBody, body, 01073 splitInfo.keys, Kleo::InlineOpenPGPFormat ); 01074 } 01075 if ( result != Kpgp::Ok ) { 01076 mRc = false; 01077 return; 01078 } 01079 assert( !encryptedBody.isNull() ); // if you hit this, check gpg-agent is running, then blame gpgme. 01080 mOldBodyPart.setBodyEncodedBinary( encryptedBody ); 01081 } else { 01082 if ( doSign ) { // Sign but don't encrypt 01083 pgpSignedMsg( body, Kleo::InlineOpenPGPFormat ); 01084 if ( mSignature.isNull() ) { 01085 mRc = false; 01086 return; 01087 } 01088 mOldBodyPart.setBodyEncodedBinary( mSignature ); 01089 } else { // don't sign nor encrypt -> nothing to do 01090 assert( !body.isNull() ); 01091 mOldBodyPart.setBodyEncoded( body ); 01092 } 01093 } 01094 mOldBodyPart.setContentDisposition( "inline" ); 01095 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() ); 01096 mOldBodyPart.setCharset(mCharset); 01097 addBodyAndAttachments( msg, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat ); 01098 mMessageList.push_back( msg ); 01099 if ( it == splitInfos.begin() ) { 01100 if ( doEncrypt && !saveMessagesEncrypted() ) { 01101 mOldBodyPart.setBodyEncoded( body ); 01102 KMMessage* msgUnenc = new KMMessage( theMessage ); 01103 addBodyAndAttachments( msgUnenc, splitInfo, false, false, mOldBodyPart, Kleo::InlineOpenPGPFormat ); 01104 msg->setUnencryptedMsg( msgUnenc ); 01105 } 01106 } 01107 } // end for 01108 } 01109 01110 void MessageComposer::composeMessage( KMMessage& theMessage, 01111 bool doSign, bool doEncrypt, 01112 Kleo::CryptoMessageFormat format ) 01113 { 01114 #ifdef DEBUG 01115 kdDebug(5006) << "entering KMComposeWin::composeMessage" << endl; 01116 #endif 01117 if ( format == Kleo::InlineOpenPGPFormat ) { 01118 composeInlineOpenPGPMessage( theMessage, doSign, doEncrypt ); 01119 return; 01120 } 01121 01122 // create informative header for those that have no mime-capable 01123 // email client 01124 theMessage.setBody( "This message is in MIME format." ); 01125 01126 // preprocess the body text 01127 QCString body = bodyText(); 01128 if (body.isNull()) { 01129 mRc = false; 01130 return; 01131 } 01132 01133 // set the main headers 01134 QString oldContentType = theMessage.headerField( "Content-Type" ); 01135 theMessage.deleteBodyParts(); 01136 theMessage.removeHeaderField("Content-Type"); 01137 theMessage.removeHeaderField("Content-Transfer-Encoding"); 01138 theMessage.setAutomaticFields(TRUE); // == multipart/mixed 01139 01140 // this is our *final* body part 01141 mNewBodyPart = new KMMessagePart; 01142 01143 // this is the boundary depth of the surrounding MIME part 01144 mPreviousBoundaryLevel = 0; 01145 01146 // create temporary bodyPart for editor text 01147 // (and for all attachments, if mail is to be signed and/or encrypted) 01148 mEarlyAddAttachments = !mAttachments.empty() && ( doSign || doEncrypt ); 01149 01150 mAllAttachmentsAreInBody = mEarlyAddAttachments; 01151 01152 // test whether there ARE attachments that can be included into the body 01153 if( mEarlyAddAttachments ) { 01154 bool someOk = false; 01155 for ( QValueVector<Attachment>::const_iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) { 01156 if ( it->encrypt == doEncrypt && it->sign == doSign ) 01157 someOk = true; 01158 else 01159 mAllAttachmentsAreInBody = false; 01160 } 01161 if( !mAllAttachmentsAreInBody && !someOk ) 01162 mEarlyAddAttachments = false; 01163 } 01164 01165 // if an html message is to be generated, make a text/plain and text/html part 01166 if ( mComposeWin->mEditor->textFormat() == Qt::RichText ) { 01167 mOldBodyPart.setTypeStr( "multipart"); 01168 mOldBodyPart.setSubtypeStr(mEarlyAddAttachments ? "mixed" : "alternative"); 01169 } 01170 else if( mEarlyAddAttachments ) { 01171 mOldBodyPart.setTypeStr( "multipart" ); 01172 mOldBodyPart.setSubtypeStr( "mixed" ); 01173 } else 01174 mOldBodyPart.setOriginalContentTypeStr( oldContentType.utf8() ); 01175 01176 mOldBodyPart.setContentDisposition( "inline" ); 01177 01178 QCString boundaryCStr; 01179 if ( mComposeWin->mEditor->textFormat() == Qt::RichText ) { // create a multipart body 01180 // calculate a boundary string 01181 QCString boundaryCStr; // storing boundary string data 01182 QCString newbody; 01183 DwMediaType tmpCT; 01184 tmpCT.CreateBoundary( mPreviousBoundaryLevel++ ); // was 0 01185 boundaryCStr = tmpCT.Boundary().c_str(); 01186 QValueList<int> allowedCTEs; 01187 01188 KMMessagePart textBodyPart; 01189 textBodyPart.setTypeStr("text"); 01190 textBodyPart.setSubtypeStr("plain"); 01191 mComposeWin->mEditor->setTextFormat(Qt::PlainText); 01192 QCString textbody = breakLinesAndApplyCodec(); 01193 mComposeWin->mEditor->setTextFormat(Qt::RichText); 01194 // the signed body must not be 8bit encoded 01195 textBodyPart.setBodyAndGuessCte( textbody, allowedCTEs, 01196 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign, 01197 doSign ); 01198 textBodyPart.setCharset( mCharset ); 01199 textBodyPart.setBodyEncoded( textbody ); 01200 DwBodyPart* textDwPart = theMessage.createDWBodyPart( &textBodyPart ); 01201 textDwPart->Assemble(); 01202 newbody += "--"; 01203 newbody += boundaryCStr; 01204 newbody += "\n"; 01205 newbody += textDwPart->AsString().c_str(); 01206 delete textDwPart; 01207 textDwPart = 0; 01208 01209 KMMessagePart htmlBodyPart; 01210 htmlBodyPart.setTypeStr("text"); 01211 htmlBodyPart.setSubtypeStr("html"); 01212 QCString htmlbody = breakLinesAndApplyCodec(); 01213 // the signed body must not be 8bit encoded 01214 htmlBodyPart.setBodyAndGuessCte( htmlbody, allowedCTEs, 01215 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign, 01216 doSign ); 01217 htmlBodyPart.setCharset( mCharset ); 01218 htmlBodyPart.setBodyEncoded( htmlbody ); 01219 DwBodyPart* htmlDwPart = theMessage.createDWBodyPart( &htmlBodyPart ); 01220 htmlDwPart->Assemble(); 01221 newbody += "\n--"; 01222 newbody += boundaryCStr; 01223 newbody += "\n"; 01224 newbody += htmlDwPart->AsString().c_str(); 01225 delete htmlDwPart; 01226 htmlDwPart = 0; 01227 01228 newbody += "--"; 01229 newbody += boundaryCStr; 01230 newbody += "--\n"; 01231 body = newbody; 01232 mOldBodyPart.setBodyEncoded( newbody ); 01233 01234 mSaveBoundary = tmpCT.Boundary(); 01235 } 01236 01237 if( mEarlyAddAttachments ) { 01238 // calculate a boundary string 01239 ++mPreviousBoundaryLevel; 01240 DwMediaType tmpCT; 01241 tmpCT.CreateBoundary( mPreviousBoundaryLevel ); 01242 boundaryCStr = tmpCT.Boundary().c_str(); 01243 // add the normal body text 01244 KMMessagePart innerBodyPart; 01245 if ( mComposeWin->mEditor->textFormat() == Qt::RichText ) { 01246 innerBodyPart.setTypeStr( "multipart");//text" ); 01247 innerBodyPart.setSubtypeStr("alternative");//html"); 01248 } 01249 else { 01250 innerBodyPart.setOriginalContentTypeStr( oldContentType.utf8() ); 01251 } 01252 innerBodyPart.setContentDisposition( "inline" ); 01253 QValueList<int> allowedCTEs; 01254 // the signed body must not be 8bit encoded 01255 innerBodyPart.setBodyAndGuessCte( body, allowedCTEs, 01256 !kmkernel->msgSender()->sendQuotedPrintable() && !doSign, 01257 doSign ); 01258 if ( mComposeWin->mEditor->textFormat() != Qt::RichText ) 01259 innerBodyPart.setCharset( mCharset ); 01260 innerBodyPart.setBodyEncoded( body ); // do we need this, since setBodyAndGuessCte does this already? 01261 DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart ); 01262 innerDwPart->Assemble(); 01263 if ( mComposeWin->mEditor->textFormat() == Qt::RichText ) { // and add our mp/a boundary 01264 QCString tmpbody = innerDwPart->AsString().c_str(); 01265 int boundPos = tmpbody.find( '\n' ); 01266 if( -1 < boundPos ) { 01267 QCString bStr( ";\n boundary=\"" ); 01268 bStr += mSaveBoundary.c_str(); 01269 bStr += "\""; 01270 body = innerDwPart->AsString().c_str(); 01271 body.insert( boundPos, bStr ); 01272 body = "--" + boundaryCStr + "\n" + body; 01273 } 01274 } 01275 else 01276 body = "--" + boundaryCStr + "\n" + innerDwPart->AsString().c_str(); 01277 delete innerDwPart; 01278 innerDwPart = 0; 01279 // add all matching Attachments 01280 // NOTE: This code will be changed when KMime is complete. 01281 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) { 01282 if ( it->encrypt == doEncrypt && it->sign == doSign ) { 01283 // signed/encrypted body parts must be either QP or base64 encoded 01284 // Why not 7 bit? Because the LF->CRLF canonicalization would render 01285 // e.g. 7 bit encoded shell scripts unusable because of the CRs. 01286 // 01287 // (marc) this is a workaround for the KMail bug that doesn't 01288 // respect the CRLF->LF de-canonicalisation. We should 01289 // eventually get rid of this: 01290 if( it->sign || it->encrypt ) { 01291 QCString cte = it->part->cteStr().lower(); 01292 if( ( "8bit" == cte ) 01293 || ( ( it->part->type() == DwMime::kTypeText ) 01294 && ( "7bit" == cte ) ) ) { 01295 const QByteArray body = it->part->bodyDecodedBinary(); 01296 QValueList<int> dummy; 01297 it->part->setBodyAndGuessCte(body, dummy, false, it->sign); 01298 kdDebug(5006) << "Changed encoding of message part from " 01299 << cte << " to " << it->part->cteStr() << endl; 01300 } 01301 } 01302 innerDwPart = theMessage.createDWBodyPart( it->part ); 01303 innerDwPart->Assemble(); 01304 body += "\n--" + boundaryCStr + "\n" + innerDwPart->AsString().c_str(); 01305 delete innerDwPart; 01306 innerDwPart = 0; 01307 } 01308 } 01309 body += "\n--" + boundaryCStr + "--\n"; 01310 } else { // !earlyAddAttachments 01311 QValueList<int> allowedCTEs; 01312 // the signed body must not be 8bit encoded 01313 mOldBodyPart.setBodyAndGuessCte(body, allowedCTEs, !kmkernel->msgSender()->sendQuotedPrintable() && !doSign, 01314 doSign); 01315 if ( mComposeWin->mEditor->textFormat() != Qt::RichText ) 01316 mOldBodyPart.setCharset(mCharset); 01317 } 01318 // create S/MIME body part for signing and/or encrypting 01319 mOldBodyPart.setBodyEncoded( body ); 01320 01321 if( doSign || doEncrypt ) { 01322 // get string representation of body part (including the attachments) 01323 01324 DwBodyPart* dwPart; 01325 if ( mComposeWin->mEditor->textFormat() == Qt::RichText && !mEarlyAddAttachments ) { 01326 // if we are using richtext and not already have a mp/a body 01327 // make the body a mp/a body 01328 dwPart = theMessage.createDWBodyPart( &mOldBodyPart ); 01329 DwHeaders& headers = dwPart->Headers(); 01330 DwMediaType& ct = headers.ContentType(); 01331 ct.SetBoundary(mSaveBoundary); 01332 dwPart->Assemble(); 01333 mEncodedBody = dwPart->AsString().c_str(); 01334 } 01335 else { 01336 dwPart = theMessage.createDWBodyPart( &mOldBodyPart ); 01337 dwPart->Assemble(); 01338 mEncodedBody = dwPart->AsString().c_str(); 01339 } 01340 delete dwPart; 01341 dwPart = 0; 01342 01343 // manually add a boundary definition to the Content-Type header 01344 if( !boundaryCStr.isEmpty() ) { 01345 int boundPos = mEncodedBody.find( '\n' ); 01346 if( -1 < boundPos ) { 01347 // insert new "boundary" parameter 01348 QCString bStr( ";\n boundary=\"" ); 01349 bStr += boundaryCStr; 01350 bStr += "\""; 01351 mEncodedBody.insert( boundPos, bStr ); 01352 } 01353 } 01354 01355 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs 01356 // according to RfC 2633, 3.1.1 Canonicalization 01357 kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; 01358 mEncodedBody = KMMessage::lf2crlf( mEncodedBody ); 01359 } 01360 01361 if ( doSign ) { 01362 pgpSignedMsg( mEncodedBody, format ); 01363 01364 if ( mSignature.isEmpty() ) { 01365 kdDebug() << "signature was empty" << endl; 01366 mRc = false; 01367 return; 01368 } 01369 mRc = processStructuringInfo( QString::null, 01370 mOldBodyPart.contentDescription(), 01371 mOldBodyPart.typeStr(), 01372 mOldBodyPart.subtypeStr(), 01373 mOldBodyPart.contentDisposition(), 01374 mOldBodyPart.contentTransferEncodingStr(), 01375 mEncodedBody, "signature", 01376 mSignature, 01377 *mNewBodyPart, true, format ); 01378 if ( mRc ) { 01379 if ( !makeMultiPartSigned( format ) ) { 01380 mNewBodyPart->setCharset( mCharset ); 01381 } 01382 } else 01383 KMessageBox::sorry( mComposeWin, 01384 mErrorProcessingStructuringInfo ); 01385 } 01386 01387 if ( !mRc ) 01388 return; 01389 01390 continueComposeMessage( theMessage, doSign, doEncrypt, format ); 01391 } 01392 01393 // Do the encryption stuff 01394 void MessageComposer::continueComposeMessage( KMMessage& theMessage, 01395 bool doSign, bool doEncrypt, 01396 Kleo::CryptoMessageFormat format ) 01397 { 01398 01399 const std::vector<Kleo::KeyResolver::SplitInfo> splitInfos 01400 = mKeyResolver->encryptionItems( format ); 01401 kdWarning( splitInfos.empty() ) 01402 << "MessageComposer::continueComposeMessage(): splitInfos.empty() for " 01403 << Kleo::cryptoMessageFormatToString( format ) << endl; 01404 01405 if ( !splitInfos.empty() && doEncrypt && !saveMessagesEncrypted() ) { 01406 mJobs.push_front( new SetLastMessageAsUnencryptedVersionOfLastButOne( this ) ); 01407 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), 01408 Kleo::KeyResolver::SplitInfo( splitInfos.front().recipients ), doSign, 01409 false, mEncodedBody, 01410 mPreviousBoundaryLevel, 01411 mOldBodyPart, mNewBodyPart, 01412 format, this ) ); 01413 } 01414 01415 for ( std::vector<Kleo::KeyResolver::SplitInfo>::const_iterator it = splitInfos.begin() ; it != splitInfos.end() ; ++it ) 01416 mJobs.push_front( new EncryptMessageJob( new KMMessage( theMessage ), *it, doSign, 01417 doEncrypt, mEncodedBody, 01418 mPreviousBoundaryLevel, 01419 mOldBodyPart, mNewBodyPart, 01420 format, this ) ); 01421 } 01422 01423 void MessageComposer::encryptMessage( KMMessage* msg, 01424 const Kleo::KeyResolver::SplitInfo & splitInfo, 01425 bool doSign, bool doEncrypt, 01426 KMMessagePart newBodyPart, 01427 Kleo::CryptoMessageFormat format ) 01428 { 01429 if ( doEncrypt && splitInfo.keys.empty() ) { 01430 // the user wants to send the message unencrypted 01431 mComposeWin->setEncryption( false, false ); 01432 doEncrypt = false; 01433 } 01434 01435 // encrypt message 01436 if ( doEncrypt ) { 01437 QCString innerContent; 01438 if ( doSign ) { 01439 DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart ); 01440 dwPart->Assemble(); 01441 innerContent = dwPart->AsString().c_str(); 01442 delete dwPart; 01443 dwPart = 0; 01444 } else 01445 innerContent = mEncodedBody; 01446 01447 // now do the encrypting: 01448 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs 01449 // according to RfC 2633, 3.1.1 Canonicalization 01450 kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; 01451 innerContent = KMMessage::lf2crlf( innerContent ); 01452 kdDebug(5006) << " done." << endl; 01453 01454 QByteArray encryptedBody; 01455 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, innerContent, 01456 splitInfo.keys, format ); 01457 if ( result != Kpgp::Ok ) { 01458 mRc = false; 01459 return; 01460 } 01461 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/", 01462 newBodyPart.contentDescription(), 01463 newBodyPart.typeStr(), 01464 newBodyPart.subtypeStr(), 01465 newBodyPart.contentDisposition(), 01466 newBodyPart.contentTransferEncodingStr(), 01467 innerContent, 01468 "encrypted data", 01469 encryptedBody, 01470 newBodyPart, false, format ); 01471 if ( !mRc ) 01472 KMessageBox::sorry(mComposeWin, mErrorProcessingStructuringInfo); 01473 } 01474 01475 // process the attachments that are not included into the body 01476 if( mRc ) { 01477 addBodyAndAttachments( msg, splitInfo, doSign, doEncrypt, 01478 (doSign || doEncrypt) ? newBodyPart : mOldBodyPart, format ); 01479 } 01480 } 01481 01482 void MessageComposer::addBodyAndAttachments( KMMessage* msg, 01483 const Kleo::KeyResolver::SplitInfo & splitInfo, 01484 bool doSign, bool doEncrypt, 01485 const KMMessagePart& ourFineBodyPart, 01486 Kleo::CryptoMessageFormat format ) 01487 { 01488 if( !mAttachments.empty() 01489 && ( !mEarlyAddAttachments || !mAllAttachmentsAreInBody ) ) { 01490 // set the content type header 01491 msg->headers().ContentType().SetType( DwMime::kTypeMultipart ); 01492 msg->headers().ContentType().SetSubtype( DwMime::kSubtypeMixed ); 01493 msg->headers().ContentType().CreateBoundary( 0 ); 01494 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to Multipart/Mixed" << endl; 01495 // msg->setBody( "This message is in MIME format.\n" 01496 // "Since your mail reader does not understand this format,\n" 01497 // "some or all parts of this message may not be legible." ); 01498 // add our Body Part 01499 DwBodyPart* tmpDwPart = msg->createDWBodyPart( &ourFineBodyPart ); 01500 DwHeaders& headers = tmpDwPart->Headers(); 01501 DwMediaType& ct = headers.ContentType(); 01502 if ( !mSaveBoundary.empty() ) 01503 ct.SetBoundary(mSaveBoundary); 01504 tmpDwPart->Assemble(); 01505 01506 //KMMessagePart newPart; 01507 //newPart.setBody(tmpDwPart->AsString().c_str()); 01508 msg->addDwBodyPart(tmpDwPart); // only this method doesn't add it as text/plain 01509 01510 // add Attachments 01511 // create additional bodyparts for the attachments (if any) 01512 KMMessagePart newAttachPart; 01513 for ( QValueVector<Attachment>::iterator it = mAttachments.begin() ; it != mAttachments.end() ; ++it ) { 01514 01515 const bool cryptFlagsDifferent = format != Kleo::InlineOpenPGPFormat 01516 && ( it->encrypt != doEncrypt || it->sign != doSign ) ; 01517 01518 // CONTROVERSIAL 01519 const bool encryptThisNow = doEncrypt && cryptFlagsDifferent && it->encrypt ; 01520 const bool signThisNow = doSign && cryptFlagsDifferent && it->sign ; 01521 01522 if ( !cryptFlagsDifferent && mEarlyAddAttachments ) 01523 continue; 01524 01525 if ( !encryptThisNow && !signThisNow ) { 01526 msg->addBodyPart( it->part ); 01527 // I DON'T KNOW WHY, BUT THIS FIXES THE VANISHING BOUNDARY PARAMTER 01528 (void)msg->asString(); 01529 continue; 01530 } 01531 01532 KMMessagePart& rEncryptMessagePart( *it->part ); 01533 01534 // prepare the attachment's content 01535 // signed/encrypted body parts must be either QP or base64 encoded 01536 QCString cte = it->part->cteStr().lower(); 01537 if( ( "8bit" == cte ) 01538 || ( ( it->part->type() == DwMime::kTypeText ) 01539 && ( "7bit" == cte ) ) ) { 01540 QByteArray body = it->part->bodyDecodedBinary(); 01541 QValueList<int> dummy; 01542 it->part->setBodyAndGuessCte(body, dummy, false, true); 01543 kdDebug(5006) << "Changed encoding of message part from " 01544 << cte << " to " << it->part->cteStr() << endl; 01545 } 01546 DwBodyPart* innerDwPart = msg->createDWBodyPart( it->part ); 01547 innerDwPart->Assemble(); 01548 QCString encodedAttachment = innerDwPart->AsString().c_str(); 01549 delete innerDwPart; 01550 innerDwPart = 0; 01551 01552 // replace simple LFs by CRLFs for all MIME supporting CryptPlugs 01553 // according to RfC 2633, 3.1.1 Canonicalization 01554 kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl; 01555 encodedAttachment = KMMessage::lf2crlf( encodedAttachment ); 01556 01557 // sign this attachment 01558 if( signThisNow ) { 01559 01560 pgpSignedMsg( encodedAttachment, format ); 01561 QByteArray signature = mSignature; 01562 mRc = !signature.isEmpty(); 01563 if( mRc ) { 01564 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/", 01565 it->part->contentDescription(), 01566 it->part->typeStr(), 01567 it->part->subtypeStr(), 01568 it->part->contentDisposition(), 01569 it->part->contentTransferEncodingStr(), 01570 encodedAttachment, 01571 "signature", 01572 signature, 01573 newAttachPart, true, format ); 01574 if( mRc ) { 01575 if( encryptThisNow ) { 01576 rEncryptMessagePart = newAttachPart; 01577 DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart ); 01578 dwPart->Assemble(); 01579 encodedAttachment = dwPart->AsString().c_str(); 01580 delete dwPart; 01581 dwPart = 0; 01582 } 01583 } else 01584 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo ); 01585 } else { 01586 // quit the attachments' loop 01587 break; 01588 } 01589 } 01590 if( encryptThisNow ) { 01591 QByteArray encryptedBody; 01592 Kpgp::Result result = pgpEncryptedMsg( encryptedBody, 01593 encodedAttachment, 01594 splitInfo.keys, 01595 format ); 01596 01597 if( Kpgp::Ok == result ) { 01598 mRc = processStructuringInfo( "http://www.gnupg.org/aegypten/", 01599 rEncryptMessagePart.contentDescription(), 01600 rEncryptMessagePart.typeStr(), 01601 rEncryptMessagePart.subtypeStr(), 01602 rEncryptMessagePart.contentDisposition(), 01603 rEncryptMessagePart.contentTransferEncodingStr(), 01604 encodedAttachment, 01605 "encrypted data", 01606 encryptedBody, 01607 newAttachPart, false, format ); 01608 if ( !mRc ) 01609 KMessageBox::sorry( mComposeWin, mErrorProcessingStructuringInfo ); 01610 } else 01611 mRc = false; 01612 } 01613 msg->addBodyPart( &newAttachPart ); 01614 } 01615 } else { // no attachments in the final message 01616 if( ourFineBodyPart.originalContentTypeStr() ) { 01617 msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() ); 01618 msg->headers().ContentType().Parse(); 01619 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type from originalContentTypeStr()=" << ourFineBodyPart.originalContentTypeStr() << endl; 01620 } else { 01621 msg->headers().ContentType().FromString( ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr() ); 01622 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : set top level Content-Type to " << ourFineBodyPart.typeStr() << "/" << ourFineBodyPart.subtypeStr() << endl; 01623 } 01624 if ( !ourFineBodyPart.charset().isEmpty() ) 01625 msg->setCharset( ourFineBodyPart.charset() ); 01626 msg->setHeaderField( "Content-Transfer-Encoding", 01627 ourFineBodyPart.contentTransferEncodingStr() ); 01628 msg->setHeaderField( "Content-Description", 01629 ourFineBodyPart.contentDescription() ); 01630 msg->setHeaderField( "Content-Disposition", 01631 ourFineBodyPart.contentDisposition() ); 01632 01633 if ( mDebugComposerCrypto ) 01634 kdDebug(5006) << "MessageComposer::addBodyAndAttachments() : top level headers and body adjusted" << endl; 01635 01636 // set body content 01637 if ( mComposeWin->mEditor->textFormat() == Qt::RichText && !(doSign || doEncrypt) ) { // add the boundary to the header 01638 msg->headers().ContentType().SetBoundary( mSaveBoundary ); 01639 msg->headers().ContentType().Assemble(); 01640 } 01641 msg->setBody(ourFineBodyPart.body() ); 01642 01643 if ( mDebugComposerCrypto ) { 01644 kdDebug(5006) << "MessageComposer::addBodyAndAttachments():\n Final message:\n|||" << msg->asString() << "|||\n\n" << endl; 01645 msg->headers().Assemble(); 01646 kdDebug(5006) << "\n\n\nMessageComposer::addBodyAndAttachments():\n Final headers:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl; 01647 } 01648 } 01649 01650 msg->setHeaderField( "X-KMail-Recipients", 01651 splitInfo.recipients.join(", ") ); 01652 } 01653 01654 //----------------------------------------------------------------------------- 01655 // This method does not call any crypto ops, so it does not need to be async 01656 bool MessageComposer::processStructuringInfo( const QString bugURL, 01657 const QString contentDescClear, 01658 const QCString contentTypeClear, 01659 const QCString contentSubtypeClear, 01660 const QCString contentDispClear, 01661 const QCString contentTEncClear, 01662 const QCString& clearCStr, 01663 const QString /*contentDescCiph*/, 01664 const QByteArray& ciphertext, 01665 KMMessagePart& resultingPart, 01666 bool signing, Kleo::CryptoMessageFormat format ) 01667 { 01668 bool bOk = true; 01669 01670 if ( makeMimeObject( format, signing ) ) { 01671 QCString mainHeader = "Content-Type: "; 01672 const char * toplevelCT = toplevelContentType( format, signing ); 01673 if ( toplevelCT ) 01674 mainHeader += toplevelCT; 01675 else { 01676 if( makeMultiMime( format, signing ) ) 01677 mainHeader += "text/plain"; 01678 else 01679 mainHeader += contentTypeClear + '/' + contentSubtypeClear; 01680 } 01681 01682 const QCString boundaryCStr = KMime::multiPartBoundary(); 01683 // add "boundary" parameter 01684 if ( makeMultiMime( format, signing ) ) 01685 mainHeader.replace( "%boundary", boundaryCStr ); 01686 01687 if ( toplevelCT ) { 01688 if ( const char * str = toplevelContentDisposition( format, signing ) ) { 01689 mainHeader += "\nContent-Disposition: "; 01690 mainHeader += str; 01691 } 01692 if ( !makeMultiMime( format, signing ) && 01693 binaryHint( format ) ) 01694 mainHeader += "\nContent-Transfer-Encoding: base64"; 01695 } else { 01696 if( 0 < contentDispClear.length() ) { 01697 mainHeader += "\nContent-Disposition: "; 01698 mainHeader += contentDispClear; 01699 } 01700 if( 0 < contentTEncClear.length() ) { 01701 mainHeader += "\nContent-Transfer-Encoding: "; 01702 mainHeader += contentTEncClear; 01703 } 01704 } 01705 01706 DwString mainDwStr; 01707 mainDwStr = mainHeader + "\n\n"; 01708 DwBodyPart mainDwPa( mainDwStr, 0 ); 01709 mainDwPa.Parse(); 01710 KMMessage::bodyPart( &mainDwPa, &resultingPart ); 01711 if( !makeMultiMime( format, signing ) ) { 01712 if ( signing && includeCleartextWhenSigning( format ) ) { 01713 QCString bodyText( clearCStr ); 01714 bodyText += '\n'; 01715 bodyText += QCString( ciphertext.data(), ciphertext.size() + 1 ); 01716 resultingPart.setBodyEncoded( bodyText ); 01717 } else 01718 resultingPart.setBodyEncodedBinary( ciphertext ); 01719 } else { // OF if( !makeMultiMime ) 01720 // Build the encapsulated MIME parts. 01721 // Build a MIME part holding the version information 01722 // taking the body contents returned in 01723 // structuring.data.bodyTextVersion. 01724 QCString versCStr, codeCStr; 01725 if ( !signing && format == Kleo::OpenPGPMIMEFormat ) 01726 versCStr = 01727 "Content-Type: application/pgp-encrypted\n" 01728 "Content-Disposition: attachment\n" 01729 "\n" 01730 "Version: 1"; 01731 01732 // Build a MIME part holding the code information 01733 // taking the body contents returned in ciphertext. 01734 const char * nestedCT = nestedContentType( format, signing ); 01735 assert( nestedCT ); 01736 codeCStr = "Content-Type: "; 01737 codeCStr += nestedCT; 01738 codeCStr += '\n'; 01739 if ( const char * str = nestedContentDisposition( format, signing ) ) { 01740 codeCStr += "Content-Disposition: "; 01741 codeCStr += str; 01742 codeCStr += '\n'; 01743 } 01744 if ( binaryHint( format ) ) { 01745 codeCStr += "Content-Transfer-Encoding: base64\n\n"; 01746 codeCStr += KMime::Codec::codecForName( "base64" )->encodeToQCString( ciphertext ); 01747 } else 01748 codeCStr += '\n' + QCString( ciphertext.data(), ciphertext.size() + 1 ); 01749 01750 01751 QCString mainStr = "--" + boundaryCStr; 01752 if ( signing && includeCleartextWhenSigning( format ) && 01753 !clearCStr.isEmpty() ) 01754 mainStr += "\n" + clearCStr + "\n--" + boundaryCStr; 01755 if ( !versCStr.isEmpty() ) 01756 mainStr += "\n" + versCStr + "\n--" + boundaryCStr; 01757 if( !codeCStr.isEmpty() ) 01758 mainStr += "\n" + codeCStr + "\n--" + boundaryCStr; 01759 mainStr += "--\n"; 01760 01761 resultingPart.setBodyEncoded( mainStr ); 01762 } // OF if( !makeMultiMime ) .. else 01763 } else { // OF makeMimeObject 01764 // Build a plain message body 01765 // based on the values returned in structInf. 01766 // Note: We do _not_ insert line breaks between the parts since 01767 // it is the plugin job to provide us with ready-to-use 01768 // texts containing all necessary line breaks. 01769 resultingPart.setContentDescription( contentDescClear ); 01770 resultingPart.setTypeStr( contentTypeClear ); 01771 resultingPart.setSubtypeStr( contentSubtypeClear ); 01772 resultingPart.setContentDisposition( contentDispClear ); 01773 resultingPart.setContentTransferEncodingStr( contentTEncClear ); 01774 QCString resultingBody; 01775 01776 #ifdef NULL_ANYWAY // these are never set currently (although they'll be used for InlineOpenPGP) 01777 if( structuring.data.flatTextPrefix 01778 && strlen( structuring.data.flatTextPrefix ) ) 01779 resultingBody += structuring.data.flatTextPrefix; 01780 #endif 01781 if ( signing && includeCleartextWhenSigning( format ) ) { 01782 if( !clearCStr.isEmpty() ) 01783 resultingBody += clearCStr; 01784 #ifdef NULL_ANYWAY 01785 if( structuring.data.flatTextSeparator 01786 && strlen( structuring.data.flatTextSeparator ) ) 01787 resultingBody += structuring.data.flatTextSeparator; 01788 #endif 01789 } 01790 if ( !ciphertext.isEmpty() ) 01791 resultingBody += QCString( ciphertext.data(), ciphertext.size() + 1 ); // what's that supposed to do ???? 01792 else { 01793 // Plugin error! 01794 KMessageBox::sorry( mComposeWin, 01795 i18n( "<qt><p>Error: The backend did not return " 01796 "any encoded data.</p>" 01797 "<p>Please report this bug:<br>%2</p></qt>" ) 01798 .arg( bugURL ) ); 01799 bOk = false; 01800 } 01801 #ifdef NULL_ANYWAY 01802 if( structuring.data.flatTextPostfix 01803 && strlen( structuring.data.flatTextPostfix ) ) 01804 resultingBody += structuring.data.flatTextPostfix; 01805 #endif 01806 resultingPart.setBodyEncoded( resultingBody ); 01807 } // OF if( structuring.data.makeMimeObject ) .. else 01808 01809 // No need to free the memory that was allocated for the ciphertext 01810 // since this memory is freed by it's QCString destructor. 01811 01812 // Neither do we free the memory that was allocated 01813 // for our structuring info data's char* members since we are using 01814 // not the pure cryptplug's StructuringInfo struct 01815 // but the convenient CryptPlugWrapper's StructuringInfoWrapper class. 01816 01817 return bOk; 01818 } 01819 01820 //----------------------------------------------------------------------------- 01821 QCString MessageComposer::breakLinesAndApplyCodec() 01822 { 01823 QString text; 01824 QCString cText; 01825 01826 if( mDisableBreaking || mComposeWin->mEditor->textFormat() == Qt::RichText) 01827 text = mComposeWin->mEditor->text(); 01828 else 01829 text = mComposeWin->mEditor->brokenText(); 01830 text.truncate( text.length() ); // to ensure text.size()==text.length()+1 01831 01832 QString newText; 01833 const QTextCodec *codec = KMMsgBase::codecForName( mCharset ); 01834 01835 if( mCharset == "us-ascii" ) { 01836 cText = KMMsgBase::toUsAscii( text ); 01837 newText = QString::fromLatin1( cText ); 01838 } else if( codec == 0 ) { 01839 kdDebug(5006) << "Something is wrong and I can not get a codec." << endl; 01840 cText = text.local8Bit(); 01841 newText = QString::fromLocal8Bit( cText ); 01842 } else { 01843 cText = codec->fromUnicode( text ); 01844 newText = codec->toUnicode( cText ); 01845 } 01846 if (cText.isNull()) cText = ""; 01847 01848 if( !text.isEmpty() && (newText != text) ) { 01849 QString oldText = mComposeWin->mEditor->text(); 01850 mComposeWin->mEditor->setText( newText ); 01851 KCursorSaver idle( KBusyPtr::idle() ); 01852 bool anyway = ( KMessageBox::warningYesNo( mComposeWin, 01853 i18n("<qt>Not all characters fit into the chosen" 01854 " encoding.<br><br>Send the message anyway?</qt>"), 01855 i18n("Some characters will be lost"), 01856 i18n("Lose Characters"), i18n("Change Encoding") ) == KMessageBox::Yes ); 01857 if( !anyway ) { 01858 mComposeWin->mEditor->setText(oldText); 01859 return QCString(); 01860 } 01861 } 01862 01863 return cText; 01864 } 01865 01866 01867 //----------------------------------------------------------------------------- 01868 void MessageComposer::pgpSignedMsg( const QCString & cText, Kleo::CryptoMessageFormat format ) { 01869 01870 mSignature = QByteArray(); 01871 01872 const std::vector<GpgME::Key> signingKeys = mKeyResolver->signingKeys( format ); 01873 01874 assert( !signingKeys.empty() ); 01875 01876 // TODO: ASync call? Likely, yes :-) 01877 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance(); 01878 assert( cpf ); 01879 const Kleo::CryptoBackend::Protocol * proto 01880 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ; 01881 assert( proto ); 01882 01883 std::auto_ptr<Kleo::SignJob> job( proto->signJob( armor( format ), 01884 textMode( format ) ) ); 01885 01886 if ( !job.get() ) { 01887 KMessageBox::sorry( mComposeWin, 01888 i18n("This message could not be signed, " 01889 "since the chosen backend does not seem to support " 01890 "signing; this should actually never happen, " 01891 "please report this bug.") ); 01892 return; 01893 } 01894 01895 QByteArray plainText; 01896 plainText.duplicate( cText.data(), cText.length() ); // hrmpf... 01897 QByteArray signature; 01898 const GpgME::SigningResult res = 01899 job->exec( signingKeys, plainText, signingMode( format ), signature ); 01900 if ( res.error().isCanceled() ) { 01901 kdDebug() << "signing was canceled by user" << endl; 01902 return; 01903 } 01904 if ( res.error() ) { 01905 kdDebug() << "signing failed: " << res.error().asString() << endl; 01906 job->showErrorDialog( mComposeWin ); 01907 return; 01908 } 01909 01910 mSignature = signature; 01911 Q_ASSERT( !mSignature.isNull() ); // if you hit this, check gpg-agent is running, then blame gpgme. 01912 if ( mSignature.isNull() ) { 01913 KMessageBox::error( mComposeWin, i18n( "The signing operation failed for an unknown reason." ) ); 01914 } 01915 } 01916 01917 //----------------------------------------------------------------------------- 01918 Kpgp::Result MessageComposer::pgpEncryptedMsg( QByteArray & encryptedBody, 01919 const QCString & cText, 01920 const std::vector<GpgME::Key> & encryptionKeys, 01921 Kleo::CryptoMessageFormat format ) 01922 { 01923 // TODO: ASync call? Likely, yes :-) 01924 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance(); 01925 assert( cpf ); 01926 const Kleo::CryptoBackend::Protocol * proto 01927 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ; 01928 assert( proto ); // hmmmm....? 01929 01930 std::auto_ptr<Kleo::EncryptJob> job( proto->encryptJob( armor( format ), 01931 textMode( format ) ) ); 01932 if ( !job.get() ) { 01933 KMessageBox::sorry( mComposeWin, 01934 i18n("This message could not be encrypted, " 01935 "since the chosen backend does not seem to support " 01936 "encryption; this should actually never happen, " 01937 "please report this bug.") ); 01938 return Kpgp::Failure; 01939 } 01940 01941 QByteArray plainText; 01942 plainText.duplicate( cText.data(), cText.length() ); // hrmpf... 01943 01944 const GpgME::EncryptionResult res = 01945 job->exec( encryptionKeys, plainText, false, encryptedBody ); 01946 if ( res.error().isCanceled() ) { 01947 kdDebug() << "encryption was canceled by user" << endl; 01948 return Kpgp::Canceled; 01949 } 01950 if ( res.error() ) { 01951 kdDebug() << "encryption failed: " << res.error().asString() << endl; 01952 job->showErrorDialog( mComposeWin ); 01953 return Kpgp::Failure; 01954 } 01955 return Kpgp::Ok; 01956 } 01957 01958 Kpgp::Result MessageComposer::pgpSignedAndEncryptedMsg( QByteArray & encryptedBody, 01959 const QCString & cText, 01960 const std::vector<GpgME::Key> & signingKeys, 01961 const std::vector<GpgME::Key> & encryptionKeys, 01962 Kleo::CryptoMessageFormat format ) 01963 { 01964 // TODO: ASync call? Likely, yes :-) 01965 const Kleo::CryptoBackendFactory * cpf = Kleo::CryptoBackendFactory::instance(); 01966 assert( cpf ); 01967 const Kleo::CryptoBackend::Protocol * proto 01968 = isSMIME( format ) ? cpf->smime() : cpf->openpgp() ; 01969 assert( proto ); // hmmmm....? 01970 01971 std::auto_ptr<Kleo::SignEncryptJob> job( proto->signEncryptJob( armor( format ), 01972 textMode( format ) ) ); 01973 if ( !job.get() ) { 01974 KMessageBox::sorry( mComposeWin, 01975 i18n("This message could not be signed and encrypted, " 01976 "since the chosen backend does not seem to support " 01977 "combined signing and encryption; this should actually never happen, " 01978 "please report this bug.") ); 01979 return Kpgp::Failure; 01980 } 01981 01982 QByteArray plainText; 01983 plainText.duplicate( cText.data(), cText.length() ); // hrmpf... 01984 01985 const std::pair<GpgME::SigningResult,GpgME::EncryptionResult> res = 01986 job->exec( signingKeys, encryptionKeys, plainText, false, encryptedBody ); 01987 if ( res.first.error().isCanceled() || res.second.error().isCanceled() ) { 01988 kdDebug() << "encrypt/sign was canceled by user" << endl; 01989 return Kpgp::Canceled; 01990 } 01991 if ( res.first.error() || res.second.error() ) { 01992 if ( res.first.error() ) 01993 kdDebug() << "signing failed: " << res.first.error().asString() << endl; 01994 else 01995 kdDebug() << "encryption failed: " << res.second.error().asString() << endl; 01996 job->showErrorDialog( mComposeWin ); 01997 return Kpgp::Failure; 01998 } 01999 return Kpgp::Ok; 02000 } 02001 02002 02003 #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 Aug 27 12:52:51 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003