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