00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "objecttreeparser.h"
00035
00036
00037 #include "kmkernel.h"
00038 #include "kmreaderwin.h"
00039 #include "partNode.h"
00040 #include <kpimutils/email.h>
00041 #include "partmetadata.h"
00042 #include "attachmentstrategy.h"
00043 #include "interfaces/htmlwriter.h"
00044 #include "htmlstatusbar.h"
00045 #include "csshelper.h"
00046 #include "bodypartformatter.h"
00047 #include "bodypartformatterfactory.h"
00048 #include "partnodebodypart.h"
00049 #include "interfaces/bodypartformatter.h"
00050 #include "globalsettings.h"
00051 #include "util.h"
00052 #include "kleojobexecutor.h"
00053
00054
00055 #include <mimelib/enum.h>
00056 #include <mimelib/bodypart.h>
00057 #include <mimelib/string.h>
00058 #include <mimelib/text.h>
00059
00060 #include <kleo/specialjob.h>
00061 #include <kleo/cryptobackendfactory.h>
00062 #include <kleo/decryptverifyjob.h>
00063 #include <kleo/verifydetachedjob.h>
00064 #include <kleo/verifyopaquejob.h>
00065 #include <kleo/keylistjob.h>
00066 #include <kleo/importjob.h>
00067 #include <kleo/dn.h>
00068
00069 #include <gpgme++/importresult.h>
00070 #include <gpgme++/decryptionresult.h>
00071 #include <gpgme++/key.h>
00072 #include <gpgme++/keylistresult.h>
00073 #include <gpgme.h>
00074
00075 #include <libkpgp/kpgpblock.h>
00076 #include <libkpgp/kpgp.h>
00077 #include <kpimutils/linklocator.h>
00078 using KPIMUtils::LinkLocator;
00079
00080 #include <ktnef/ktnefparser.h>
00081 #include <ktnef/ktnefmessage.h>
00082 #include <ktnef/ktnefattach.h>
00083
00084
00085 #include <kdebug.h>
00086 #include <klocale.h>
00087 #include <kmimetype.h>
00088 #include <kglobal.h>
00089 #include <khtml_part.h>
00090 #include <ktemporaryfile.h>
00091 #include <kstandarddirs.h>
00092 #include <kmessagebox.h>
00093 #include <kiconloader.h>
00094 #include <kcodecs.h>
00095 #include <kconfiggroup.h>
00096 #include <kstyle.h>
00097
00098
00099 #include <QApplication>
00100 #include <QDir>
00101 #include <QFile>
00102 #include <QTextCodec>
00103 #include <QByteArray>
00104 #include <QBuffer>
00105 #include <QPixmap>
00106 #include <QPainter>
00107 #include <QPointer>
00108
00109
00110 #include <sys/stat.h>
00111 #include <sys/types.h>
00112 #include <unistd.h>
00113 #include <memory>
00114 #include "chiasmuskeyselector.h"
00115
00116 namespace KMail {
00117
00118
00119 class ObjectTreeParser::CryptoProtocolSaver {
00120 ObjectTreeParser * otp;
00121 const Kleo::CryptoBackend::Protocol * protocol;
00122 public:
00123 CryptoProtocolSaver( ObjectTreeParser * _otp, const Kleo::CryptoBackend::Protocol* _w )
00124 : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
00125 {
00126 if ( otp )
00127 otp->setCryptoProtocol( _w );
00128 }
00129
00130 ~CryptoProtocolSaver() {
00131 if ( otp )
00132 otp->setCryptoProtocol( protocol );
00133 }
00134 };
00135
00136
00137 ObjectTreeParser::ObjectTreeParser( KMReaderWin * reader, const Kleo::CryptoBackend::Protocol * protocol,
00138 bool showOnlyOneMimePart, bool keepEncryptions,
00139 bool includeSignatures,
00140 const AttachmentStrategy * strategy,
00141 HtmlWriter * htmlWriter,
00142 CSSHelper * cssHelper )
00143 : mReader( reader ),
00144 mCryptoProtocol( protocol ),
00145 mShowOnlyOneMimePart( showOnlyOneMimePart ),
00146 mKeepEncryptions( keepEncryptions ),
00147 mIncludeSignatures( includeSignatures ),
00148 mAttachmentStrategy( strategy ),
00149 mHtmlWriter( htmlWriter ),
00150 mCSSHelper( cssHelper )
00151 {
00152 if ( !attachmentStrategy() )
00153 mAttachmentStrategy = reader ? reader->attachmentStrategy()
00154 : AttachmentStrategy::smart();
00155 if ( reader && !this->htmlWriter() )
00156 mHtmlWriter = reader->htmlWriter();
00157 if ( reader && !this->cssHelper() )
00158 mCSSHelper = reader->mCSSHelper;
00159 }
00160
00161 ObjectTreeParser::ObjectTreeParser( const ObjectTreeParser & other )
00162 : mReader( other.mReader ),
00163 mCryptoProtocol( other.cryptoProtocol() ),
00164 mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
00165 mKeepEncryptions( other.keepEncryptions() ),
00166 mIncludeSignatures( other.includeSignatures() ),
00167 mAttachmentStrategy( other.attachmentStrategy() ),
00168 mHtmlWriter( other.htmlWriter() ),
00169 mCSSHelper( other.cssHelper() )
00170 {
00171
00172 }
00173
00174 ObjectTreeParser::~ObjectTreeParser() {}
00175
00176 void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
00177 const char* content,
00178 const char* cntDesc,
00179 bool append )
00180 {
00181 DwBodyPart* myBody = new DwBodyPart( DwString( content ), 0 );
00182 myBody->Parse();
00183
00184 if ( ( !myBody->Body().FirstBodyPart() ||
00185 myBody->Body().AsString().length() == 0 ) &&
00186 startNode.dwPart() &&
00187 startNode.dwPart()->Body().Message() &&
00188 startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
00189 {
00190
00191
00192 myBody = new DwBodyPart( *(startNode.dwPart()->Body().Message()) );
00193 }
00194
00195 if ( myBody->hasHeaders() ) {
00196 DwText& desc = myBody->Headers().ContentDescription();
00197 desc.FromString( cntDesc );
00198 desc.SetModified();
00199 myBody->Headers().Parse();
00200 }
00201
00202 partNode* parentNode = &startNode;
00203 partNode* newNode = new partNode(false, myBody);
00204 if ( append && parentNode->firstChild() ) {
00205 parentNode = parentNode->firstChild();
00206 while( parentNode->nextSibling() )
00207 parentNode = parentNode->nextSibling();
00208 parentNode->setNext( newNode );
00209 } else
00210 parentNode->setFirstChild( newNode );
00211
00212 newNode->buildObjectTree( false );
00213
00214 if ( startNode.mimePartTreeItem() ) {
00215 kDebug() << "\n -----> Inserting items into MimePartTree";
00216 newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
00217 QString(), QString(), QString(), 0,
00218 append );
00219 kDebug() << "\n <----- Finished inserting items into MimePartTree";
00220 } else {
00221 kDebug() << "\n ------ Sorry, node.mimePartTreeItem() returns ZERO so"
00222 << "\n we cannot insert new lines into MimePartTree. :-(\n";
00223 }
00224 kDebug() << "\n -----> Now parsing the MimePartTree";
00225 ObjectTreeParser otp( mReader, cryptoProtocol() );
00226 otp.parseObjectTree( newNode );
00227 mRawReplyString += otp.rawReplyString();
00228 mTextualContent += otp.textualContent();
00229 if ( !otp.textualContentCharset().isEmpty() )
00230 mTextualContentCharset = otp.textualContentCharset();
00231 kDebug() << "\n <----- Finished parsing the MimePartTree in insertAndParseNewChildNode()";
00232 }
00233
00234
00235
00236
00237 void ObjectTreeParser::parseObjectTree( partNode * node ) {
00238 kDebug() << (node ? "node OK, " : "no node, ")
00239 << "showOnlyOneMimePart: " << (showOnlyOneMimePart() ? "TRUE" : "FALSE");
00240
00241 if ( !node )
00242 return;
00243
00244
00245 if ( showOnlyOneMimePart() ) {
00246
00247 node->setProcessed( false, false );
00248 if ( partNode * child = node->firstChild() )
00249 child->setProcessed( false, true );
00250 } else if ( mReader && !node->parentNode() ) {
00251
00252 node->setProcessed( false, true );
00253 }
00254
00255 for ( ; node ; node = node->nextSibling() ) {
00256 if ( node->processed() )
00257 continue;
00258
00259 ProcessResult processResult;
00260
00261 if ( mReader )
00262 htmlWriter()->queue( QString::fromLatin1("<a name=\"att%1\"/>").arg( node->nodeId() ) );
00263 if ( const Interface::BodyPartFormatter * formatter
00264 = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
00265 PartNodeBodyPart part( *node, codecFor( node ) );
00266
00267
00268 part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
00269 const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter() );
00270 if ( mReader && node->bodyPartMemento() )
00271 if ( Interface::Observable * obs = node->bodyPartMemento()->asObservable() )
00272 obs->attach( mReader );
00273 switch ( result ) {
00274 case Interface::BodyPartFormatter::AsIcon:
00275 processResult.setNeverDisplayInline( true );
00276
00277 case Interface::BodyPartFormatter::Failed:
00278 defaultHandling( node, processResult );
00279 break;
00280 case Interface::BodyPartFormatter::Ok:
00281 case Interface::BodyPartFormatter::NeedContent:
00282
00283 ;
00284 }
00285 } else {
00286 const BodyPartFormatter * bpf
00287 = BodyPartFormatter::createFor( node->type(), node->subType() );
00288 kFatal( !bpf, 5006 ) <<"THIS SHOULD NO LONGER HAPPEN ("
00289 << node->typeString() << '/' << node->subTypeString() << ')';
00290
00291 if ( bpf && !bpf->process( this, node, processResult ) )
00292 defaultHandling( node, processResult );
00293 }
00294 node->setProcessed( true, false );
00295
00296
00297 processResult.adjustCryptoStatesOfNode( node );
00298
00299 if ( showOnlyOneMimePart() )
00300 break;
00301 }
00302 }
00303
00304 void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
00305
00306
00307 if ( !mReader )
00308 return;
00309 if ( attachmentStrategy() == AttachmentStrategy::hidden() &&
00310 !showOnlyOneMimePart() &&
00311 node->parentNode() )
00312 return;
00313
00314 bool asIcon = true;
00315 if ( showOnlyOneMimePart() )
00316
00317
00318
00319 asIcon = !node->hasContentDispositionInline();
00320 else if ( !result.neverDisplayInline() )
00321 if ( const AttachmentStrategy * as = attachmentStrategy() )
00322 asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
00323
00324 if ( !result.isImage()
00325 && node->type() != DwMime::kTypeText )
00326 asIcon = true;
00327
00328 if ( result.isImage() && !node->msgPart().isComplete() )
00329 asIcon = true;
00330 if ( asIcon ) {
00331 if ( attachmentStrategy() != AttachmentStrategy::hidden()
00332 || showOnlyOneMimePart() )
00333 writePartIcon( &node->msgPart(), node->nodeId() );
00334 } else if ( result.isImage() )
00335 writePartIcon( &node->msgPart(), node->nodeId(), true );
00336 else
00337 writeBodyString( node->msgPart().bodyDecoded(),
00338 node->trueFromAddress(),
00339 codecFor( node ), result, false );
00340
00341 }
00342
00343 void ProcessResult::adjustCryptoStatesOfNode( partNode * node ) const {
00344 if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
00345 ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
00346 node->setSignatureState( inlineSignatureState() );
00347 node->setEncryptionState( inlineEncryptionState() );
00348 }
00349 }
00350
00354
00355 static int signatureToStatus( const GpgME::Signature &sig )
00356 {
00357 switch ( sig.status().code() ) {
00358 case GPG_ERR_NO_ERROR:
00359 return GPGME_SIG_STAT_GOOD;
00360 case GPG_ERR_BAD_SIGNATURE:
00361 return GPGME_SIG_STAT_BAD;
00362 case GPG_ERR_NO_PUBKEY:
00363 return GPGME_SIG_STAT_NOKEY;
00364 case GPG_ERR_NO_DATA:
00365 return GPGME_SIG_STAT_NOSIG;
00366 case GPG_ERR_SIG_EXPIRED:
00367 return GPGME_SIG_STAT_GOOD_EXP;
00368 case GPG_ERR_KEY_EXPIRED:
00369 return GPGME_SIG_STAT_GOOD_EXPKEY;
00370 default:
00371 return GPGME_SIG_STAT_ERROR;
00372 }
00373 }
00374
00375 bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
00376 partNode& sign,
00377 const QString& fromAddress,
00378 bool doCheck,
00379 QByteArray* cleartextData,
00380 std::vector<GpgME::Signature> paramSignatures,
00381 bool hideErrors )
00382 {
00383 bool bIsOpaqueSigned = false;
00384 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
00385 cryptPlugError = NO_PLUGIN;
00386
00387 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
00388
00389 QString cryptPlugLibName;
00390 QString cryptPlugDisplayName;
00391 if ( cryptProto ) {
00392 cryptPlugLibName = cryptProto->name();
00393 cryptPlugDisplayName = cryptProto->displayName();
00394 }
00395
00396 #ifndef NDEBUG
00397 if ( !doCheck )
00398 kDebug() << "showing OpenPGP (Encrypted+Signed) data";
00399 else
00400 if ( data )
00401 kDebug() << "processing Multipart Signed data";
00402 else
00403 kDebug() << "processing Opaque Signed data";
00404 #endif
00405
00406 if ( doCheck && cryptProto ) {
00407 kDebug() << "going to call CRYPTPLUG" << cryptPlugLibName;
00408 }
00409
00410 QByteArray cleartext;
00411 QByteArray signaturetext;
00412
00413 if ( doCheck && cryptProto ) {
00414 if ( data ) {
00415 cleartext = KMail::Util::ByteArray( data->dwPart()->AsString() );
00416
00417 dumpToFile( "dat_01_reader_signedtext_before_canonicalization",
00418 cleartext.data(), cleartext.length() );
00419
00420
00421
00422 kDebug() <<"Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)";
00423 cleartext = Util::lf2crlf( cleartext );
00424 kDebug() <<" done.";
00425 }
00426
00427 dumpToFile( "dat_02_reader_signedtext_after_canonicalization",
00428 cleartext.data(), cleartext.length() );
00429
00430 signaturetext = sign.msgPart().bodyDecodedBinary();
00431 dumpToFile( "dat_03_reader.sig", signaturetext.data(),
00432 signaturetext.size() );
00433 }
00434
00435 std::vector<GpgME::Signature> signatures;
00436 if ( doCheck )
00437 signatures = paramSignatures;
00438
00439 PartMetaData messagePart;
00440 messagePart.isSigned = true;
00441 messagePart.technicalProblem = ( cryptProto == 0 );
00442 messagePart.isGoodSignature = false;
00443 messagePart.isEncrypted = false;
00444 messagePart.isDecryptable = false;
00445 messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
00446 messagePart.status = i18n("Wrong Crypto Plug-In.");
00447 messagePart.status_code = GPGME_SIG_STAT_NONE;
00448
00449 if ( doCheck && cryptProto ) {
00450 GpgME::VerificationResult result;
00451 if ( data ) {
00452 if ( Kleo::VerifyDetachedJob * const job = cryptProto->verifyDetachedJob() ) {
00453 KleoJobExecutor executor;
00454 result = executor.exec( job, signaturetext, cleartext );
00455 messagePart.auditLog = executor.auditLogAsHtml();
00456 } else {
00457 cryptPlugError = CANT_VERIFY_SIGNATURES;
00458 }
00459 } else {
00460 if ( Kleo::VerifyOpaqueJob * const job = cryptProto->verifyOpaqueJob() ) {
00461 KleoJobExecutor executor;
00462 result = executor.exec( job, signaturetext, cleartext );
00463 messagePart.auditLog = executor.auditLogAsHtml();
00464 } else {
00465 cryptPlugError = CANT_VERIFY_SIGNATURES;
00466 }
00467 }
00468 signatures = result.signatures();
00469 }
00470
00471 if ( doCheck )
00472 kDebug() << "returned from CRYPTPLUG";
00473
00474
00475 if ( !signatures.empty() ) {
00476 kDebug() << "\nFound signature";
00477 GpgME::Signature signature = signatures.front();
00478
00479 messagePart.status_code = signatureToStatus( signature );
00480 messagePart.status = QString::fromLocal8Bit( signature.status().asString() );
00481 for ( uint i = 1; i < signatures.size(); ++i ) {
00482 if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
00483 messagePart.status_code = GPGME_SIG_STAT_DIFF;
00484 messagePart.status = i18n("Different results for signatures");
00485 }
00486 }
00487 if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
00488 messagePart.isGoodSignature = true;
00489
00490
00491 Kleo::KeyListJob *job = cryptProto->keyListJob();
00492 std::vector<GpgME::Key> keys;
00493 if ( signature.fingerprint() )
00494 GpgME::KeyListResult keyListRes = job->exec( QStringList( QString::fromLatin1( signature.fingerprint() ) ),
00495 false, keys );
00496 GpgME::Key key;
00497 if ( keys.size() == 1 )
00498 key = keys[0];
00499 else if ( keys.size() > 1 )
00500 assert( false );
00501
00502
00503 messagePart.sigSummary = signature.summary();
00504
00505 if ( key.keyID() )
00506 messagePart.keyId = key.keyID();
00507 if ( messagePart.keyId.isEmpty() )
00508 messagePart.keyId = signature.fingerprint();
00509
00510 messagePart.keyTrust = (Kpgp::Validity)signature.validity();
00511 if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
00512 messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
00513 for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
00514
00515
00516 if ( key.userID( iMail ).email() ) {
00517 QString email = QString::fromUtf8( key.userID( iMail ).email() );
00518
00519
00520 if ( email.startsWith( '<' ) && email.endsWith( '>' ) )
00521 email = email.mid( 1, email.length() - 2 );
00522 if ( !email.isEmpty() )
00523 messagePart.signerMailAddresses.append( email );
00524 }
00525 }
00526
00527 if ( signature.creationTime() )
00528 messagePart.creationTime.setTime_t( signature.creationTime() );
00529 else
00530 messagePart.creationTime = QDateTime();
00531 if ( messagePart.signer.isEmpty() ) {
00532 if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
00533 messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
00534 if ( !messagePart.signerMailAddresses.empty() ) {
00535 if ( messagePart.signer.isEmpty() )
00536 messagePart.signer = messagePart.signerMailAddresses.front();
00537 else
00538 messagePart.signer += " <" + messagePart.signerMailAddresses.front() + '>';
00539 }
00540 }
00541
00542 kDebug() << "\n key id:" << messagePart.keyId
00543 << "\n key trust:" << messagePart.keyTrust
00544 << "\n signer:" << messagePart.signer;
00545
00546 } else {
00547 messagePart.creationTime = QDateTime();
00548 }
00549
00550 if ( !doCheck || !data ){
00551 if ( cleartextData || !cleartext.isEmpty() ) {
00552 if ( mReader )
00553 htmlWriter()->queue( writeSigstatHeader( messagePart,
00554 cryptProto,
00555 fromAddress ) );
00556 bIsOpaqueSigned = true;
00557
00558 CryptoProtocolSaver cpws( this, cryptProto );
00559 insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
00560 "opaqued signed data" );
00561
00562 if ( mReader )
00563 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00564
00565 }
00566 else if ( !hideErrors ) {
00567 QString txt;
00568 txt = "<hr><b><h2>";
00569 txt.append( i18n( "The crypto engine returned no cleartext data." ) );
00570 txt.append( "</h2></b>" );
00571 txt.append( "<br> <br>" );
00572 txt.append( i18n( "Status: " ) );
00573 if ( !messagePart.status.isEmpty() ) {
00574 txt.append( "<i>" );
00575 txt.append( messagePart.status );
00576 txt.append( "</i>" );
00577 }
00578 else
00579 txt.append( i18n("(unknown)") );
00580 if ( mReader )
00581 htmlWriter()->queue(txt);
00582 }
00583 }
00584 else {
00585 if ( mReader ) {
00586 if ( !cryptProto ) {
00587 QString errorMsg;
00588 switch ( cryptPlugError ) {
00589 case NOT_INITIALIZED:
00590 errorMsg = i18n( "Crypto plug-in \"%1\" is not initialized.",
00591 cryptPlugLibName );
00592 break;
00593 case CANT_VERIFY_SIGNATURES:
00594 errorMsg = i18n( "Crypto plug-in \"%1\" cannot verify signatures.",
00595 cryptPlugLibName );
00596 break;
00597 case NO_PLUGIN:
00598 if ( cryptPlugDisplayName.isEmpty() )
00599 errorMsg = i18n( "No appropriate crypto plug-in was found." );
00600 else
00601 errorMsg = i18nc( "%1 is either 'OpenPGP' or 'S/MIME'",
00602 "No %1 plug-in was found.",
00603 cryptPlugDisplayName );
00604 break;
00605 }
00606 messagePart.errorText = i18n( "The message is signed, but the "
00607 "validity of the signature cannot be "
00608 "verified.<br />"
00609 "Reason: %1",
00610 errorMsg );
00611 }
00612
00613 if ( mReader )
00614 htmlWriter()->queue( writeSigstatHeader( messagePart,
00615 cryptProto,
00616 fromAddress ) );
00617 }
00618
00619 ObjectTreeParser otp( mReader, cryptProto, true );
00620 otp.parseObjectTree( data );
00621 mRawReplyString += otp.rawReplyString();
00622 mTextualContent += otp.textualContent();
00623 if ( !otp.textualContentCharset().isEmpty() )
00624 mTextualContentCharset = otp.textualContentCharset();
00625
00626 if ( mReader )
00627 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
00628 }
00629
00630 kDebug() << "done, returning" << ( bIsOpaqueSigned ? "TRUE" : "FALSE" );
00631 return bIsOpaqueSigned;
00632 }
00633
00634
00635 bool ObjectTreeParser::okDecryptMIME( partNode& data,
00636 QByteArray& decryptedData,
00637 bool& signatureFound,
00638 std::vector<GpgME::Signature> &signatures,
00639 bool showWarning,
00640 bool& passphraseError,
00641 bool& actuallyEncrypted,
00642 QString& aErrorText,
00643 QString& auditLog )
00644 {
00645 passphraseError = false;
00646 aErrorText.clear();
00647 auditLog.clear();
00648 bool bDecryptionOk = false;
00649 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
00650 cryptPlugError = NO_PLUGIN;
00651
00652 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
00653
00654 QString cryptPlugLibName;
00655 if ( cryptProto )
00656 cryptPlugLibName = cryptProto->name();
00657
00658 if ( mReader && !mReader->decryptMessage() ) {
00659 QString iconName = KIconLoader::global()->iconPath( "document-decrypt",
00660 KIconLoader::Small );
00661 decryptedData = "<div style=\"font-size:large; text-align:center;"
00662 "padding-top:20pt;\">"
00663 + i18n("This message is encrypted.").toUtf8()
00664 + "</div>"
00665 "<div style=\"text-align:center; padding-bottom:20pt;\">"
00666 "<a href=\"kmail:decryptMessage\">"
00667 "<img src=\"" + iconName.toUtf8() + "\"/>"
00668 + i18n("Decrypt Message").toUtf8()
00669 + "</a></div>";
00670 return false;
00671 }
00672
00673 if ( cryptProto && !kmkernel->contextMenuShown() ) {
00674 QByteArray ciphertext = data.msgPart().bodyDecodedBinary();
00675 #ifdef MARCS_DEBUG
00676 QString cipherStr = QString::fromLatin1( ciphertext );
00677 bool cipherIsBinary = ( !cipherStr.contains("BEGIN ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
00678 ( !cipherStr.contains("BEGIN PGP ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
00679 ( !cipherStr.contains("BEGIN PGP MESSAGE", Qt::CaseInsensitive ) );
00680
00681 dumpToFile( "dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
00682
00683 QByteArray deb;
00684 deb = "\n\nE N C R Y P T E D D A T A = ";
00685 if ( cipherIsBinary )
00686 deb += "[binary data]";
00687 else {
00688 deb += "\"";
00689 deb += cipherStr;
00690 deb += "\"";
00691 }
00692 deb += "\n\n";
00693 kDebug() << deb;
00694 #endif
00695
00696
00697 kDebug() << "going to call CRYPTPLUG" << cryptPlugLibName;
00698 if ( mReader )
00699 emit mReader->noDrag();
00700
00701 Kleo::DecryptVerifyJob* job = cryptProto->decryptVerifyJob();
00702 if ( !job ) {
00703 cryptPlugError = CANT_DECRYPT;
00704 cryptProto = 0;
00705 } else {
00706 QByteArray plainText;
00707 KleoJobExecutor executor;
00708 const std::pair<GpgME::DecryptionResult,GpgME::VerificationResult> res = executor.exec( job, ciphertext, plainText );
00709 const GpgME::DecryptionResult decryptResult = res.first;
00710 const GpgME::VerificationResult verifyResult = res.second;
00711 signatureFound = verifyResult.signatures().size() > 0;
00712 signatures = verifyResult.signatures();
00713 bDecryptionOk = !decryptResult.error();
00714 passphraseError = decryptResult.error().isCanceled()
00715 || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
00716 actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
00717 aErrorText = QString::fromLocal8Bit( decryptResult.error().asString() );
00718 auditLog = executor.auditLogAsHtml();
00719
00720 kDebug() << "ObjectTreeParser::decryptMIME: returned from CRYPTPLUG";
00721 if ( bDecryptionOk )
00722 decryptedData = plainText;
00723 else if ( mReader && showWarning ) {
00724 decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00725 "padding:20pt;\">"
00726 + i18n("Encrypted data not shown.").toUtf8()
00727 + "</div>";
00728 if ( !passphraseError )
00729 aErrorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName )
00730 + "<br />"
00731 + i18n("Error: %1", aErrorText );
00732 }
00733 }
00734 }
00735
00736 if ( !cryptProto ) {
00737 decryptedData = "<div style=\"text-align:center; padding:20pt;\">"
00738 + i18n("Encrypted data not shown.").toUtf8()
00739 + "</div>";
00740 switch ( cryptPlugError ) {
00741 case NOT_INITIALIZED:
00742 aErrorText = i18n( "Crypto plug-in \"%1\" is not initialized.",
00743 cryptPlugLibName );
00744 break;
00745 case CANT_DECRYPT:
00746 aErrorText = i18n( "Crypto plug-in \"%1\" cannot decrypt messages.",
00747 cryptPlugLibName );
00748 break;
00749 case NO_PLUGIN:
00750 aErrorText = i18n( "No appropriate crypto plug-in was found." );
00751 break;
00752 }
00753 } else if ( kmkernel->contextMenuShown() ) {
00754
00755
00756 QByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
00757 QString cipherStr = QString::fromLatin1( ciphertext );
00758 bool cipherIsBinary = ( !cipherStr.contains("BEGIN ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
00759 ( !cipherStr.contains("BEGIN PGP ENCRYPTED MESSAGE", Qt::CaseInsensitive ) ) &&
00760 ( !cipherStr.contains("BEGIN PGP MESSAGE", Qt::CaseInsensitive ) );
00761 if ( !cipherIsBinary ) {
00762 decryptedData = ciphertext;
00763 }
00764 else {
00765 decryptedData = "<div style=\"font-size:x-large; text-align:center;"
00766 "padding:20pt;\">"
00767 + i18n("Encrypted data not shown.").toUtf8()
00768 + "</div>";
00769 }
00770 }
00771
00772 dumpToFile( "dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
00773
00774 return bDecryptionOk;
00775 }
00776
00777
00778 bool ObjectTreeParser::containsExternalReferences( const QString & str )
00779 {
00780 int httpPos = str.indexOf( "\"http:", Qt::CaseInsensitive );
00781 int httpsPos = str.indexOf( "\"https:", Qt::CaseInsensitive );
00782
00783 while ( httpPos >= 0 || httpsPos >= 0 ) {
00784
00785 int pos = ( httpPos < httpsPos )
00786 ? ( ( httpPos >= 0 ) ? httpPos : httpsPos )
00787 : ( ( httpsPos >= 0 ) ? httpsPos : httpPos );
00788
00789 if ( pos > 5 ) {
00790 int hrefPos = str.lastIndexOf( "href", pos - 5, Qt::CaseInsensitive );
00791
00792
00793
00794 if ( ( hrefPos == -1 ) || ( pos - hrefPos > 7 ) ) {
00795
00796
00797
00798
00799 int dtdPos = str.indexOf( "http://www.w3.org/TR/REC-html40/strict.dtd", pos + 1 );
00800 if ( dtdPos != ( pos + 1 ) )
00801 return true;
00802 }
00803 }
00804
00805 if ( pos == httpPos ) {
00806 httpPos = str.indexOf( "\"http:", httpPos + 6, Qt::CaseInsensitive );
00807 }
00808 else {
00809 httpsPos = str.indexOf( "\"https:", httpsPos + 7, Qt::CaseInsensitive );
00810 }
00811 }
00812 return false;
00813 }
00814
00815 bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
00816 const QByteArray partBody( curNode->msgPart().bodyDecoded() );
00817
00818 mRawReplyString = partBody;
00819 if ( curNode->isFirstTextPart() ) {
00820 mTextualContent += curNode->msgPart().bodyToUnicode();
00821 mTextualContentCharset = curNode->msgPart().charset();
00822 }
00823
00824 if ( !mReader )
00825 return true;
00826
00827 QString bodyText;
00828 if ( mReader->htmlMail() )
00829 bodyText = codecFor( curNode )->toUnicode( partBody );
00830 else
00831 bodyText = KMMessage::html2source( partBody );
00832
00833 if ( curNode->isFirstTextPart() ||
00834 attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
00835 showOnlyOneMimePart() )
00836 {
00837 if ( mReader->htmlMail() ) {
00838
00839
00840
00841
00842
00843 bodyText = bodyText.replace( "<html>", QString(), Qt::CaseInsensitive );
00844 bodyText = bodyText.replace( "<head>", QString(), Qt::CaseInsensitive );
00845 bodyText = bodyText.replace( "</head>", QString(), Qt::CaseInsensitive );
00846 QRegExp bodyRegExp( "<body.*>" );
00847 bodyRegExp.setMinimal( true );
00848 bodyRegExp.setCaseSensitivity( Qt::CaseInsensitive );
00849 bodyText = bodyText.replace( bodyRegExp, QString() );
00850
00851
00852
00853
00854
00855 int i = bodyText.lastIndexOf( "</body>", -1, Qt::CaseInsensitive );
00856 if ( 0 <= i )
00857 bodyText.truncate(i);
00858 else {
00859 i = bodyText.lastIndexOf( "</html>", -1, Qt::CaseInsensitive );
00860 if ( 0 <= i )
00861 bodyText.truncate(i);
00862 }
00863
00864
00865
00866
00867
00868
00869
00870 if ( !mReader->htmlLoadExternal() &&
00871 containsExternalReferences( bodyText ) ) {
00872 htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
00873 htmlWriter()->queue( i18n("<b>Note:</b> This HTML message may contain external "
00874 "references to images etc. For security/privacy reasons "
00875 "external references are not loaded. If you trust the "
00876 "sender of this message then you can load the external "
00877 "references for this message "
00878 "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
00879 htmlWriter()->queue( "</div><br><br>" );
00880 }
00881 } else {
00882 htmlWriter()->queue( "<div class=\"htmlWarn\">\n" );
00883 htmlWriter()->queue( i18n("<b>Note:</b> This is an HTML message. For "
00884 "security reasons, only the raw HTML code "
00885 "is shown. If you trust the sender of this "
00886 "message then you can activate formatted "
00887 "HTML display for this message "
00888 "<a href=\"kmail:showHTML\">by clicking here</a>.") );
00889 htmlWriter()->queue( "</div><br><br>" );
00890 }
00891 htmlWriter()->queue( bodyText );
00892 mReader->mColorBar->setHtmlMode();
00893 return true;
00894 }
00895 return false;
00896 }
00897 }
00898
00899 static bool isMailmanMessage( partNode * curNode ) {
00900 if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
00901 return false;
00902 DwHeaders & headers = curNode->dwPart()->Headers();
00903 if ( headers.HasField("X-Mailman-Version") )
00904 return true;
00905 if ( headers.HasField("X-Mailer") &&
00906 0 == QString::fromLatin1( headers.FieldBody("X-Mailer").AsString().c_str() )
00907 .contains("MAILMAN", Qt::CaseInsensitive ) )
00908 return true;
00909 return false;
00910 }
00911
00912 namespace KMail {
00913
00914 bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
00915 const QString str = QString::fromLatin1( curNode->msgPart().bodyDecoded() );
00916
00917
00918 const QLatin1String delim1( "--__--__--\n\nMessage:");
00919 const QLatin1String delim2( "--__--__--\r\n\r\nMessage:");
00920 const QLatin1String delimZ2("--__--__--\n\n_____________");
00921 const QLatin1String delimZ1("--__--__--\r\n\r\n_____________");
00922 QString partStr, digestHeaderStr;
00923 int thisDelim = str.indexOf(delim1, Qt::CaseInsensitive );
00924 if ( thisDelim == -1 )
00925 thisDelim = str.indexOf(delim2, Qt::CaseInsensitive );
00926 if ( thisDelim == -1 ) {
00927 kDebug() << " Sorry: Old style Mailman message but no delimiter found.";
00928 return false;
00929 }
00930
00931 int nextDelim = str.indexOf( delim1, thisDelim+1, Qt::CaseInsensitive );
00932 if ( -1 == nextDelim )
00933 nextDelim = str.indexOf( delim2, thisDelim+1, Qt::CaseInsensitive );
00934 if ( -1 == nextDelim )
00935 nextDelim = str.indexOf( delimZ1, thisDelim+1, Qt::CaseInsensitive );
00936 if ( -1 == nextDelim )
00937 nextDelim = str.indexOf( delimZ2, thisDelim+1, Qt::CaseInsensitive );
00938 if ( nextDelim < 0)
00939 return false;
00940
00941 kDebug() << " processing old style Mailman digest";
00942
00943
00944
00945
00946 digestHeaderStr = "Content-Type=text/plain\nContent-Description=digest header\n\n";
00947 digestHeaderStr += str.mid( 0, thisDelim );
00948 insertAndParseNewChildNode( *curNode,
00949 digestHeaderStr.toLatin1(),
00950 "Digest Header", true );
00951
00952
00953
00954 curNode->setType( DwMime::kTypeMultipart );
00955 curNode->setSubType( DwMime::kSubtypeDigest );
00956 while( -1 < nextDelim ){
00957 int thisEoL = str.indexOf("\nMessage:", thisDelim, Qt::CaseInsensitive );
00958 if ( -1 < thisEoL )
00959 thisDelim = thisEoL+1;
00960 else{
00961 thisEoL = str.indexOf("\n_____________", thisDelim, Qt::CaseInsensitive );
00962 if ( -1 < thisEoL )
00963 thisDelim = thisEoL+1;
00964 }
00965 thisEoL = str.indexOf( '\n', thisDelim );
00966 if ( -1 < thisEoL )
00967 thisDelim = thisEoL+1;
00968 else
00969 thisDelim = thisDelim+1;
00970
00971
00972
00973 partStr = "Content-Type=message/rfc822\nContent-Description=embedded message\n";
00974 partStr += str.mid( thisDelim, nextDelim-thisDelim );
00975 QString subject = QString::fromLatin1("embedded message");
00976 QString subSearch = QString::fromLatin1("\nSubject:");
00977 int subPos = partStr.indexOf(subSearch, 0, Qt::CaseInsensitive );
00978 if ( -1 < subPos ){
00979 subject = partStr.mid(subPos+subSearch.length());
00980 thisEoL = subject.indexOf('\n');
00981 if ( -1 < thisEoL )
00982 subject.truncate( thisEoL );
00983 }
00984 kDebug() << " embedded message found: \"" << subject <<"\"";
00985 insertAndParseNewChildNode( *curNode,
00986 partStr.toLatin1(),
00987 subject.toLatin1(), true );
00988
00989 thisDelim = nextDelim+1;
00990 nextDelim = str.indexOf(delim1, thisDelim, Qt::CaseInsensitive );
00991 if ( -1 == nextDelim )
00992 nextDelim = str.indexOf(delim2, thisDelim, Qt::CaseInsensitive);
00993 if ( -1 == nextDelim )
00994 nextDelim = str.indexOf(delimZ1, thisDelim, Qt::CaseInsensitive);
00995 if ( -1 == nextDelim )
00996 nextDelim = str.indexOf(delimZ2, thisDelim, Qt::CaseInsensitive);
00997 }
00998
00999 curNode->setType( DwMime::kTypeText );
01000 curNode->setSubType( DwMime::kSubtypePlain );
01001 int thisEoL = str.indexOf( "_____________", thisDelim );
01002 if ( -1 < thisEoL ){
01003 thisDelim = thisEoL;
01004 thisEoL = str.indexOf( '\n', thisDelim );
01005 if ( -1 < thisEoL )
01006 thisDelim = thisEoL+1;
01007 }
01008 else
01009 thisDelim = thisDelim+1;
01010 partStr = "Content-Type=text/plain\nContent-Description=digest footer\n\n";
01011 partStr += str.mid( thisDelim );
01012 insertAndParseNewChildNode( *curNode,
01013 partStr.toLatin1(),
01014 "Digest Footer", true );
01015 return true;
01016 }
01017
01018 bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
01019 if ( !mReader ) {
01020 mRawReplyString = curNode->msgPart().bodyDecoded();
01021 if ( curNode->isFirstTextPart() ) {
01022 mTextualContent += curNode->msgPart().bodyToUnicode();
01023 mTextualContentCharset = curNode->msgPart().charset();
01024 }
01025 return true;
01026 }
01027
01028 if ( !curNode->isFirstTextPart() &&
01029 attachmentStrategy()->defaultDisplay( curNode ) !=