36#include "objecttreeparser.h"
37#include "objecttreeparser_p.h"
41#include "kmreaderwin.h"
43#include <libtdepim/tdefileio.h>
44#include <libemailfunctions/email.h>
45#include "partmetadata.h"
46#include "attachmentstrategy.h"
47#include "interfaces/htmlwriter.h"
48#include "htmlstatusbar.h"
50#include "bodypartformatter.h"
51#include "bodypartformatterfactory.h"
52#include "partnodebodypart.h"
53#include "interfaces/bodypartformatter.h"
54#include "globalsettings.h"
59#include <mimelib/enum.h>
60#include <mimelib/bodypart.h>
61#include <mimelib/string.h>
62#include <mimelib/text.h>
64#include <kleo/specialjob.h>
65#include <kleo/cryptobackendfactory.h>
66#include <kleo/decryptverifyjob.h>
67#include <kleo/verifydetachedjob.h>
68#include <kleo/verifyopaquejob.h>
69#include <kleo/keylistjob.h>
70#include <kleo/importjob.h>
73#include <gpgmepp/importresult.h>
74#include <gpgmepp/decryptionresult.h>
75#include <gpgmepp/key.h>
76#include <gpgmepp/keylistresult.h>
81#include <linklocator.h>
83#include <ktnef/ktnefparser.h>
84#include <ktnef/ktnefmessage.h>
85#include <ktnef/ktnefattach.h>
92#include <tdehtml_part.h>
93#include <tdetempfile.h>
94#include <tdestandarddirs.h>
95#include <tdeapplication.h>
96#include <tdemessagebox.h>
97#include <kiconloader.h>
101#include <tqtextcodec.h>
104#include <tqapplication.h>
108#include <tqpainter.h>
115#include <sys/types.h>
118#include "chiasmuskeyselector.h"
123 class ObjectTreeParser::CryptoProtocolSaver {
124 ObjectTreeParser * otp;
125 const Kleo::CryptoBackend::Protocol * protocol;
127 CryptoProtocolSaver( ObjectTreeParser * _otp,
const Kleo::CryptoBackend::Protocol* _w )
128 : otp( _otp ), protocol( _otp ? _otp->cryptoProtocol() : 0 )
131 otp->setCryptoProtocol( _w );
134 ~CryptoProtocolSaver() {
136 otp->setCryptoProtocol( protocol );
141 ObjectTreeParser::ObjectTreeParser(
KMReaderWin * reader,
const Kleo::CryptoBackend::Protocol * protocol,
142 bool showOnlyOneMimePart,
bool keepEncryptions,
143 bool includeSignatures,
144 const AttachmentStrategy * strategy,
145 HtmlWriter * htmlWriter,
146 CSSHelper * cssHelper )
148 mCryptoProtocol( protocol ),
149 mShowOnlyOneMimePart( showOnlyOneMimePart ),
150 mKeepEncryptions( keepEncryptions ),
151 mIncludeSignatures( includeSignatures ),
152 mHasPendingAsyncJobs( false ),
153 mAllowAsync( false ),
154 mShowRawToltecMail( false ),
155 mAttachmentStrategy( strategy ),
156 mHtmlWriter( htmlWriter ),
157 mCSSHelper( cssHelper )
159 if ( !attachmentStrategy() )
161 : AttachmentStrategy::smart();
162 if ( reader && !this->htmlWriter() )
164 if ( reader && !this->cssHelper() )
165 mCSSHelper = reader->mCSSHelper;
168 ObjectTreeParser::ObjectTreeParser(
const ObjectTreeParser & other )
169 : mReader( other.mReader ),
170 mCryptoProtocol( other.cryptoProtocol() ),
171 mShowOnlyOneMimePart( other.showOnlyOneMimePart() ),
172 mKeepEncryptions( other.keepEncryptions() ),
173 mIncludeSignatures( other.includeSignatures() ),
174 mHasPendingAsyncJobs( other.hasPendingAsyncJobs() ),
175 mAllowAsync( other.allowAsync() ),
176 mAttachmentStrategy( other.attachmentStrategy() ),
177 mHtmlWriter( other.htmlWriter() ),
178 mCSSHelper( other.cssHelper() )
183 ObjectTreeParser::~ObjectTreeParser() {}
185 void ObjectTreeParser::insertAndParseNewChildNode( partNode& startNode,
188 bool append,
bool addToTextualContent )
190 DwBodyPart* myBody =
new DwBodyPart( DwString( content ), 0 );
193 if ( ( !myBody->Body().FirstBodyPart() ||
194 myBody->Body().AsString().length() == 0 ) &&
195 startNode.dwPart() &&
196 startNode.dwPart()->Body().Message() &&
197 startNode.dwPart()->Body().Message()->Body().FirstBodyPart() )
201 myBody =
new DwBodyPart( *(startNode.dwPart()->Body().Message()) );
204 if ( myBody->hasHeaders() ) {
205 DwText& desc = myBody->Headers().ContentDescription();
206 desc.FromString( cntDesc );
208 myBody->Headers().Parse();
211 partNode* parentNode = &startNode;
212 partNode* newNode =
new partNode(
false, myBody);
216 newNode->buildObjectTree(
false );
218 if ( append && parentNode->firstChild() ) {
219 parentNode = parentNode->firstChild();
220 while( parentNode->nextSibling() )
221 parentNode = parentNode->nextSibling();
222 parentNode->setNext( newNode );
224 parentNode->setFirstChild( newNode );
226 if ( startNode.mimePartTreeItem() ) {
227 newNode->fillMimePartTree( startNode.mimePartTreeItem(), 0,
228 TQString(), TQString(), TQString(), 0,
232 ObjectTreeParser otp( mReader, cryptoProtocol() );
233 otp.parseObjectTree( newNode );
234 if ( addToTextualContent ) {
235 mRawReplyString += otp.rawReplyString();
236 mTextualContent += otp.textualContent();
237 if ( !otp.textualContentCharset().isEmpty() )
238 mTextualContentCharset = otp.textualContentCharset();
245 void ObjectTreeParser::parseObjectTree( partNode * node ) {
255 mHasPendingAsyncJobs =
false;
258 if ( showOnlyOneMimePart() ) {
260 node->setProcessed(
false,
false );
261 if ( partNode * child = node->firstChild() )
262 child->setProcessed(
false,
true );
263 }
else if ( mReader && !node->parentNode() ) {
265 node->setProcessed(
false,
true );
268 for ( ; node ; node = node->nextSibling() ) {
269 if ( node->processed() )
272 ProcessResult processResult;
275 htmlWriter()->queue( TQString::fromLatin1(
"<a name=\"att%1\"/>").arg( node->nodeId() ) );
278 if (
const Interface::BodyPartFormatter * formatter
279 = BodyPartFormatterFactory::instance()->createFor( node->typeString(), node->subTypeString() ) ) {
284 PartNodeBodyPart part( *node, codecFor( node ) );
287 part.setDefaultDisplay( (KMail::Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay( node ) );
289 writeAttachmentMarkHeader( node );
290 node->setDisplayedEmbedded(
true );
291 Callback callback( mReader->message(), mReader );
292 const Interface::BodyPartFormatter::Result result = formatter->format( &part, htmlWriter(), callback );
293 writeAttachmentMarkFooter();
295 case Interface::BodyPartFormatter::AsIcon:
296 processResult.setNeverDisplayInline(
true );
298 case Interface::BodyPartFormatter::Failed:
299 defaultHandling( node, processResult );
301 case Interface::BodyPartFormatter::Ok:
302 case Interface::BodyPartFormatter::NeedContent:
308 const BodyPartFormatter * bpf
309 = BodyPartFormatter::createFor( node->type(), node->subType() );
310 kdFatal( !bpf, 5006 ) <<
"THIS SHOULD NO LONGER HAPPEN ("
311 << node->typeString() <<
'/' << node->subTypeString()
314 writeAttachmentMarkHeader( node );
315 if ( bpf && !bpf->process(
this, node, processResult ) ) {
316 defaultHandling( node, processResult );
318 writeAttachmentMarkFooter();
321 node->setProcessed(
true,
false );
324 processResult.adjustCryptoStatesOfNode( node );
326 if ( showOnlyOneMimePart() )
331 void ObjectTreeParser::defaultHandling( partNode * node, ProcessResult & result ) {
338 const AttachmentStrategy * as = attachmentStrategy();
339 if ( as && as->defaultDisplay( node ) == AttachmentStrategy::None &&
340 !showOnlyOneMimePart() &&
341 node->parentNode() ) {
342 node->setDisplayedHidden(
true );
347 if ( showOnlyOneMimePart() )
351 asIcon = !node->hasContentDispositionInline();
352 else if ( !result.neverDisplayInline() )
354 asIcon = as->defaultDisplay( node ) == AttachmentStrategy::AsIcon;
356 if ( !result.isImage() && node->type() != DwMime::kTypeText )
359 if ( result.isImage() && !node->msgPart().isComplete() )
362 if ( !( as && as->defaultDisplay( node ) == AttachmentStrategy::None ) ||
363 showOnlyOneMimePart() ) {
364 writePartIcon( &node->msgPart(), node->nodeId() );
367 node->setDisplayedHidden(
true );
369 }
else if ( result.isImage() ) {
370 node->setDisplayedEmbedded(
true );
371 writePartIcon( &node->msgPart(), node->nodeId(),
true );
374 node->setDisplayedEmbedded(
true );
375 writeBodyString( node->msgPart().bodyDecoded(),
376 node->trueFromAddress(),
377 codecFor( node ), result,
false );
382 void ProcessResult::adjustCryptoStatesOfNode( partNode * node )
const {
383 if ( ( inlineSignatureState() != KMMsgNotSigned ) ||
384 ( inlineEncryptionState() != KMMsgNotEncrypted ) ) {
385 node->setSignatureState( inlineSignatureState() );
386 node->setEncryptionState( inlineEncryptionState() );
394 static int signatureToStatus(
const GpgME::Signature &sig )
396 switch ( sig.status().code() ) {
397 case GPG_ERR_NO_ERROR:
398 return GPGME_SIG_STAT_GOOD;
399 case GPG_ERR_BAD_SIGNATURE:
400 return GPGME_SIG_STAT_BAD;
401 case GPG_ERR_NO_PUBKEY:
402 return GPGME_SIG_STAT_NOKEY;
403 case GPG_ERR_NO_DATA:
404 return GPGME_SIG_STAT_NOSIG;
405 case GPG_ERR_SIG_EXPIRED:
406 return GPGME_SIG_STAT_GOOD_EXP;
407 case GPG_ERR_KEY_EXPIRED:
408 return GPGME_SIG_STAT_GOOD_EXPKEY;
410 return GPGME_SIG_STAT_ERROR;
414 bool ObjectTreeParser::writeOpaqueOrMultipartSignedData( partNode* data,
416 const TQString& fromAddress,
419 TQCString* cleartextData,
420 const std::vector<GpgME::Signature> & paramSignatures,
421 const GpgME::Key & paramKey)
423 bool bIsOpaqueSigned =
false;
424 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
425 cryptPlugError = NO_PLUGIN;
427 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
429 TQString cryptPlugLibName;
430 TQString cryptPlugDisplayName;
432 cryptPlugLibName = cryptProto->name();
433 cryptPlugDisplayName = cryptProto->displayName();
450 if ( doCheck && cryptProto ) {
456 TQByteArray signaturetext;
458 if ( doCheck && cryptProto ) {
462 dumpToFile(
"dat_01_reader_signedtext_before_canonicalization",
463 cleartext.data(), cleartext.length() );
468 cleartext = Util::lf2crlf( cleartext );
472 dumpToFile(
"dat_02_reader_signedtext_after_canonicalization",
473 cleartext.data(), cleartext.length() );
475 signaturetext = sign.msgPart().bodyDecodedBinary();
476 dumpToFile(
"dat_03_reader.sig", signaturetext.data(),
477 signaturetext.size() );
480 std::vector<GpgME::Signature> signatures;
484 signatures = paramSignatures;
488 PartMetaData messagePart;
489 messagePart.isSigned =
true;
490 messagePart.technicalProblem = ( cryptProto == 0 );
491 messagePart.isGoodSignature =
false;
492 messagePart.isEncrypted =
false;
493 messagePart.isDecryptable =
false;
494 messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
495 messagePart.status = i18n(
"Wrong Crypto Plug-In.");
496 messagePart.status_code = GPGME_SIG_STAT_NONE;
498 if ( doCheck && cryptProto ) {
499 GpgME::VerificationResult result;
501 const VerifyDetachedBodyPartMemento * m
502 =
dynamic_cast<VerifyDetachedBodyPartMemento*
>( sign.bodyPartMemento(
"verifydetached" ) );
504 Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
506 cryptPlugError = CANT_VERIFY_SIGNATURES;
509 TQByteArray plainData = cleartext;
510 plainData.resize( cleartext.size() - 1 );
511 VerifyDetachedBodyPartMemento * newM
512 =
new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
513 if ( allowAsync() ) {
514 if ( newM->start() ) {
515 messagePart.inProgress =
true;
516 mHasPendingAsyncJobs =
true;
524 sign.setBodyPartMemento(
"verifydetached", newM );
526 }
else if ( m->isRunning() ) {
527 messagePart.inProgress =
true;
528 mHasPendingAsyncJobs =
true;
533 result = m->verifyResult();
534 messagePart.auditLogError = m->auditLogError();
535 messagePart.auditLog = m->auditLogAsHtml();
536 key = m->signingKey();
539 const VerifyOpaqueBodyPartMemento * m
540 =
dynamic_cast<VerifyOpaqueBodyPartMemento*
>( sign.bodyPartMemento(
"verifyopaque" ) );
542 Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
544 cryptPlugError = CANT_VERIFY_SIGNATURES;
547 VerifyOpaqueBodyPartMemento * newM
548 =
new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
549 if ( allowAsync() ) {
550 if ( newM->start() ) {
551 messagePart.inProgress =
true;
552 mHasPendingAsyncJobs =
true;
560 sign.setBodyPartMemento(
"verifyopaque", newM );
562 }
else if ( m->isRunning() ) {
563 messagePart.inProgress =
true;
564 mHasPendingAsyncJobs =
true;
569 result = m->verifyResult();
570 const TQByteArray & plainData = m->plainText();
571 cleartext = TQCString( plainData.data(), plainData.size() + 1 );
572 messagePart.auditLogError = m->auditLogError();
573 messagePart.auditLog = m->auditLogAsHtml();
574 key = m->signingKey();
577 std::stringstream ss;
580 signatures = result.signatures();
588 if ( signatures.size() > 0 ) {
590 GpgME::Signature signature = signatures[0];
592 messagePart.status_code = signatureToStatus( signature );
593 messagePart.status = TQString::fromUtf8( signature.status().asString() );
594 for ( uint i = 1; i < signatures.size(); ++i ) {
595 if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
596 messagePart.status_code = GPGME_SIG_STAT_DIFF;
597 messagePart.status = i18n(
"Different results for signatures");
600 if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
601 messagePart.isGoodSignature =
true;
604 messagePart.sigSummary = signature.summary();
607 messagePart.keyId = key.keyID();
608 if ( messagePart.keyId.isEmpty() )
609 messagePart.keyId = signature.fingerprint();
611 messagePart.keyTrust = (Kpgp::Validity)signature.validity();
612 if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
613 messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
614 for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
617 if ( key.userID( iMail ).email() ) {
618 TQString email = TQString::fromUtf8( key.userID( iMail ).email() );
621 if ( email.startsWith(
"<" ) && email.endsWith(
">" ) )
622 email = email.mid( 1, email.length() - 2 );
623 if ( !email.isEmpty() )
624 messagePart.signerMailAddresses.append( email );
628 if ( signature.creationTime() )
629 messagePart.creationTime.setTime_t( signature.creationTime() );
631 messagePart.creationTime = TQDateTime();
632 if ( messagePart.signer.isEmpty() ) {
633 if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
634 messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
635 if ( !messagePart.signerMailAddresses.empty() ) {
636 if ( messagePart.signer.isEmpty() )
637 messagePart.signer = messagePart.signerMailAddresses.front();
639 messagePart.signer +=
" <" + messagePart.signerMailAddresses.front() +
'>';
648 messagePart.creationTime = TQDateTime();
651 if ( !doCheck || !data ){
652 if ( cleartextData || !cleartext.isEmpty() ) {
654 htmlWriter()->queue( writeSigstatHeader( messagePart,
657 bIsOpaqueSigned =
true;
659 CryptoProtocolSaver cpws(
this, cryptProto );
660 insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
661 "opaqued signed data" );
664 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
667 else if ( !hideErrors ) {
670 txt.append( i18n(
"The crypto engine returned no cleartext data." ) );
671 txt.append(
"</h2></b>" );
672 txt.append(
"<br> <br>" );
673 txt.append( i18n(
"Status: " ) );
674 if ( !messagePart.status.isEmpty() ) {
676 txt.append( messagePart.status );
677 txt.append(
"</i>" );
680 txt.append( i18n(
"(unknown)") );
682 htmlWriter()->queue(txt);
689 switch ( cryptPlugError ) {
690 case NOT_INITIALIZED:
691 errorMsg = i18n(
"Crypto plug-in \"%1\" is not initialized." )
692 .arg( cryptPlugLibName );
694 case CANT_VERIFY_SIGNATURES:
695 errorMsg = i18n(
"Crypto plug-in \"%1\" cannot verify signatures." )
696 .arg( cryptPlugLibName );
699 if ( cryptPlugDisplayName.isEmpty() )
700 errorMsg = i18n(
"No appropriate crypto plug-in was found." );
702 errorMsg = i18n(
"%1 is either 'OpenPGP' or 'S/MIME'",
703 "No %1 plug-in was found." )
704 .arg( cryptPlugDisplayName );
707 messagePart.errorText = i18n(
"The message is signed, but the "
708 "validity of the signature cannot be "
715 htmlWriter()->queue( writeSigstatHeader( messagePart,
720 ObjectTreeParser otp( mReader, cryptProto,
true );
721 otp.parseObjectTree( data );
722 mRawReplyString += otp.rawReplyString();
723 mTextualContent += otp.textualContent();
724 if ( !otp.textualContentCharset().isEmpty() )
725 mTextualContentCharset = otp.textualContentCharset();
728 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
733 return bIsOpaqueSigned;
736void ObjectTreeParser::writeDecryptionInProgressBlock() {
740 const TQString decryptedData = i18n(
"Encrypted data not shown");
741 PartMetaData messagePart;
742 messagePart.isDecryptable =
true;
743 messagePart.isEncrypted =
true;
744 messagePart.isSigned =
false;
745 messagePart.inProgress =
true;
746 htmlWriter()->queue( writeSigstatHeader( messagePart,
750 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
753void ObjectTreeParser::writeDeferredDecryptionBlock() {
755 const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath(
"decrypted", TDEIcon::Small );
756 const TQString decryptedData =
757 "<div style=\"font-size:large; text-align:center;padding-top:20pt;\">" +
758 i18n(
"This message is encrypted.") +
760 "<div style=\"text-align:center; padding-bottom:20pt;\">"
761 "<a href=\"kmail:decryptMessage\">"
762 "<img src=\"" + iconName +
"\"/>" +
763 i18n(
"Decrypt Message") +
765 PartMetaData messagePart;
766 messagePart.isDecryptable =
true;
767 messagePart.isEncrypted =
true;
768 messagePart.isSigned =
false;
769 mRawReplyString += decryptedData.utf8();
770 htmlWriter()->queue( writeSigstatHeader( messagePart,
773 htmlWriter()->queue( decryptedData );
774 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
777bool ObjectTreeParser::okDecryptMIME( partNode& data,
778 TQCString& decryptedData,
779 bool& signatureFound,
780 std::vector<GpgME::Signature> &signatures,
783 bool& passphraseError,
784 bool& actuallyEncrypted,
785 bool& decryptionStarted,
786 TQString& aErrorText,
787 GpgME::Error & auditLogError,
790 passphraseError =
false;
791 decryptionStarted =
false;
792 aErrorText = TQString();
793 auditLogError = GpgME::Error();
794 auditLog = TQString();
795 bool bDecryptionOk =
false;
796 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
797 cryptPlugError = NO_PLUGIN;
799 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
801 TQString cryptPlugLibName;
803 cryptPlugLibName = cryptProto->name();
805 assert( !mReader || mReader->decryptMessage() );
807 if ( cryptProto && !kmkernel->contextMenuShown() ) {
808 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
810 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
811 bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
812 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
813 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
815 dumpToFile(
"dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
818 deb =
"\n\nE N C R Y P T E D D A T A = ";
819 if ( cipherIsBinary )
820 deb +=
"[binary data]";
827 kdDebug(5006) << deb << endl;
834 emit mReader->noDrag();
837 const DecryptVerifyBodyPartMemento * m
838 =
dynamic_cast<DecryptVerifyBodyPartMemento*
>( data.bodyPartMemento(
"decryptverify" ) );
840 Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
842 cryptPlugError = CANT_DECRYPT;
845 DecryptVerifyBodyPartMemento * newM
846 =
new DecryptVerifyBodyPartMemento( job, cryptProto->keyListJob(), ciphertext );
847 if ( allowAsync() ) {
848 if ( newM->start() ) {
849 decryptionStarted =
true;
850 mHasPendingAsyncJobs =
true;
858 data.setBodyPartMemento(
"decryptverify", newM );
860 }
else if ( m->isRunning() ) {
861 decryptionStarted =
true;
862 mHasPendingAsyncJobs =
true;
867 const TQByteArray & plainText = m->plainText();
868 const GpgME::DecryptionResult & decryptResult = m->decryptResult();
869 const GpgME::VerificationResult & verifyResult = m->verifyResult();
870 std::stringstream ss;
871 ss << decryptResult <<
'\n' << verifyResult;
873 signatureFound = verifyResult.signatures().size() > 0;
874 signatures = verifyResult.signatures();
875 key = m->signingKey();
876 bDecryptionOk = !decryptResult.error();
877 passphraseError = decryptResult.error().isCanceled()
878 || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
879 actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
880 aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() );
881 auditLogError = m->auditLogError();
882 auditLog = m->auditLogAsHtml();
887 decryptedData = TQCString( plainText.data(), plainText.size() + 1 );
888 else if ( mReader && showWarning ) {
889 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
891 + i18n(
"Encrypted data not shown.").utf8()
893 if ( !passphraseError )
894 aErrorText = i18n(
"Crypto plug-in \"%1\" could not decrypt the data.")
895 .arg( cryptPlugLibName )
897 + i18n(
"Error: %1").arg( aErrorText );
903 decryptedData =
"<div style=\"text-align:center; padding:20pt;\">"
904 + i18n(
"Encrypted data not shown.").utf8()
906 switch ( cryptPlugError ) {
907 case NOT_INITIALIZED:
908 aErrorText = i18n(
"Crypto plug-in \"%1\" is not initialized." )
909 .arg( cryptPlugLibName );
912 aErrorText = i18n(
"Crypto plug-in \"%1\" cannot decrypt messages." )
913 .arg( cryptPlugLibName );
916 aErrorText = i18n(
"No appropriate crypto plug-in was found." );
919 }
else if ( kmkernel->contextMenuShown() ) {
922 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
923 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
924 bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
925 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
926 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
927 if ( !cipherIsBinary ) {
928 decryptedData = cipherStr;
931 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
933 + i18n(
"Encrypted data not shown.").utf8()
938 dumpToFile(
"dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
940 return bDecryptionOk;
944 bool ObjectTreeParser::containsExternalReferences(
const TQCString & str )
946 TQRegExp httpRegExp(
"(\\\"|\\\'|url\\s*\\(\\s*)http[s]?:");
947 int httpPos = str.find( httpRegExp, 0 );
949 while ( httpPos >= 0 ) {
952 int hrefPos = str.findRev(
"href", httpPos - 5,
true );
956 if ( ( hrefPos == -1 ) || ( httpPos - hrefPos > 7 ) )
960 httpPos = str.find( httpRegExp, httpPos + 6 );
965 bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
966 TQCString cstr( curNode->msgPart().bodyDecoded() );
968 mRawReplyString = cstr;
969 if ( curNode->isFirstTextPart() ) {
970 mTextualContent += curNode->msgPart().bodyToUnicode();
971 mTextualContentCharset = curNode->msgPart().charset();
977 if ( curNode->isFirstTextPart() ||
978 attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
979 showOnlyOneMimePart() )
981 if ( mReader->htmlMail() ) {
982 curNode->setDisplayedEmbedded(
true );
987 int i = cstr.findRev(
"</body>", -1,
false);
992 i = cstr.findRev(
"</html>", -1,
false);
993 if ( 0 <= i ) cstr.truncate(i);
1002 if ( !mReader->htmlLoadExternal() &&
1003 containsExternalReferences( cstr ) ) {
1004 htmlWriter()->queue(
"<div class=\"htmlWarn\">\n" );
1005 htmlWriter()->queue( i18n(
"<b>Note:</b> This HTML message may contain external "
1006 "references to images etc. For security/privacy reasons "
1007 "external references are not loaded. If you trust the "
1008 "sender of this message then you can load the external "
1009 "references for this message "
1010 "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
1011 htmlWriter()->queue(
"</div><br><br>" );
1014 htmlWriter()->queue(
"<div class=\"htmlWarn\">\n" );
1015 htmlWriter()->queue( i18n(
"<b>Note:</b> This is an HTML message. For "
1016 "security reasons, only the raw HTML code "
1017 "is shown. If you trust the sender of this "
1018 "message then you can activate formatted "
1019 "HTML display for this message "
1020 "<a href=\"kmail:showHTML\">by clicking here</a>.") );
1021 htmlWriter()->queue(
"</div><br><br>" );
1023 htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr :
KMMessage::html2source( cstr )));
1024 mReader->mColorBar->setHtmlMode();
1031static bool isMailmanMessage( partNode * curNode ) {
1032 if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
1034 DwHeaders & headers = curNode->dwPart()->Headers();
1035 if ( headers.HasField(
"X-Mailman-Version") )
1037 if ( headers.HasField(
"X-Mailer") &&
1038 0 == TQCString( headers.FieldBody(
"X-Mailer").AsString().c_str() )
1039 .find(
"MAILMAN", 0,
false) )
1046 bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
1047 const TQCString cstr = curNode->msgPart().bodyDecoded();
1050 const TQCString delim1(
"--__--__--\n\nMessage:");
1051 const TQCString delim2(
"--__--__--\r\n\r\nMessage:");
1052 const TQCString delimZ2(
"--__--__--\n\n_____________");
1053 const TQCString delimZ1(
"--__--__--\r\n\r\n_____________");
1054 TQCString partStr, digestHeaderStr;
1055 int thisDelim = cstr.find(delim1.data(), 0,
false);
1056 if ( thisDelim == -1 )
1057 thisDelim = cstr.find(delim2.data(), 0,
false);
1058 if ( thisDelim == -1 ) {
1059 kdDebug(5006) <<
" Sorry: Old style Mailman message but no delimiter found." << endl;
1063 int nextDelim = cstr.find(delim1.data(), thisDelim+1,
false);
1064 if ( -1 == nextDelim )
1065 nextDelim = cstr.find(delim2.data(), thisDelim+1,
false);
1066 if ( -1 == nextDelim )
1067 nextDelim = cstr.find(delimZ1.data(), thisDelim+1,
false);
1068 if ( -1 == nextDelim )
1069 nextDelim = cstr.find(delimZ2.data(), thisDelim+1,
false);
1078 digestHeaderStr =
"Content-Type=text/plain\nContent-Description=digest header\n\n";
1079 digestHeaderStr += cstr.mid( 0, thisDelim );
1080 insertAndParseNewChildNode( *curNode,
1082 "Digest Header",
true );
1086 curNode->setType( DwMime::kTypeMultipart );
1087 curNode->setSubType( DwMime::kSubtypeDigest );
1088 while( -1 < nextDelim ){
1089 int thisEoL = cstr.find(
"\nMessage:", thisDelim,
false);
1091 thisDelim = thisEoL+1;
1093 thisEoL = cstr.find(
"\n_____________", thisDelim,
false);
1095 thisDelim = thisEoL+1;
1097 thisEoL = cstr.find(
'\n', thisDelim);
1099 thisDelim = thisEoL+1;
1101 thisDelim = thisDelim+1;
1105 partStr =
"Content-Type=message/rfc822\nContent-Description=embedded message\n";
1106 partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
1107 TQCString subject(
"embedded message");
1108 TQCString subSearch(
"\nSubject:");
1109 int subPos = partStr.find(subSearch.data(), 0,
false);
1111 subject = partStr.mid(subPos+subSearch.length());
1112 thisEoL = subject.find(
'\n');
1114 subject.truncate( thisEoL );
1117 insertAndParseNewChildNode( *curNode,
1121 thisDelim = nextDelim+1;
1122 nextDelim = cstr.find(delim1.data(), thisDelim,
false);
1123 if ( -1 == nextDelim )
1124 nextDelim = cstr.find(delim2.data(), thisDelim,
false);
1125 if ( -1 == nextDelim )
1126 nextDelim = cstr.find(delimZ1.data(), thisDelim,
false);
1127 if ( -1 == nextDelim )
1128 nextDelim = cstr.find(delimZ2.data(), thisDelim,
false);
1131 curNode->setType( DwMime::kTypeText );
1132 curNode->setSubType( DwMime::kSubtypePlain );
1133 int thisEoL = cstr.find(
"_____________", thisDelim);
1134 if ( -1 < thisEoL ){
1135 thisDelim = thisEoL;
1136 thisEoL = cstr.find(
'\n', thisDelim);
1138 thisDelim = thisEoL+1;
1141 thisDelim = thisDelim+1;
1142 partStr =
"Content-Type=text/plain\nContent-Description=digest footer\n\n";
1143 partStr += cstr.mid( thisDelim );
1144 insertAndParseNewChildNode( *curNode,
1146 "Digest Footer",
true );
1150 bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
1152 mRawReplyString = curNode->msgPart().bodyDecoded();
1153 if ( curNode->isFirstTextPart() ) {
1154 mTextualContent += curNode->msgPart().bodyToUnicode();
1155 mTextualContentCharset = curNode->msgPart().charset();
1160 if ( !curNode->isFirstTextPart() &&
1161 attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
1162 !showOnlyOneMimePart() )
1165 mRawReplyString = curNode->msgPart().bodyDecoded();
1166 if ( curNode->isFirstTextPart() ) {
1167 mTextualContent += curNode->msgPart().bodyToUnicode();
1168 mTextualContentCharset = curNode->msgPart().charset();
1171 TQString label = curNode->msgPart().fileName().stripWhiteSpace();
1172 if ( label.isEmpty() )
1173 label = curNode->msgPart().name().stripWhiteSpace();
1175 const bool bDrawFrame = !curNode->isFirstTextPart()
1176 && !showOnlyOneMimePart()
1177 && !label.isEmpty();
1181 const TQString comment =
1184 const TQString fileName =
1185 mReader->writeMessagePartToTempFile( &curNode->msgPart(),
1186 curNode->nodeId() );
1188 const TQString dir = TQApplication::reverseLayout() ?
"rtl" :
"ltr" ;
1190 TQString htmlStr =
"<table cellspacing=\"1\" class=\"textAtm\">"
1191 "<tr class=\"textAtmH\"><td dir=\"" + dir +
"\">";
1192 if ( !fileName.isEmpty() )
1193 htmlStr +=
"<a href=\"" + curNode->asHREF(
"body" ) +
"\">"
1197 if ( !comment.isEmpty() )
1198 htmlStr +=
"<br>" + comment;
1199 htmlStr +=
"</td></tr><tr class=\"textAtmB\"><td>";
1201 htmlWriter()->queue( htmlStr );
1205 if ( !isMailmanMessage( curNode ) ||
1206 !processMailmanMessage( curNode ) ) {
1207 writeBodyString( mRawReplyString, curNode->trueFromAddress(),
1208 codecFor( curNode ), result, !bDrawFrame );
1209 curNode->setDisplayedEmbedded(
true );
1212 htmlWriter()->queue(
"</td></tr></table>" );
1217 void ObjectTreeParser::stdChildHandling( partNode * child ) {
1221 ObjectTreeParser otp( *
this );
1222 otp.setShowOnlyOneMimePart(
false );
1223 otp.parseObjectTree( child );
1224 mRawReplyString += otp.rawReplyString();
1225 mTextualContent += otp.textualContent();
1226 if ( !otp.textualContentCharset().isEmpty() )
1227 mTextualContentCharset = otp.textualContentCharset();
1230 TQString ObjectTreeParser::defaultToltecReplacementText()
1232 return i18n(
"This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
1233 "Microsoft Outlook in combination with the Toltec connector." );
1236 bool ObjectTreeParser::processToltecMail( partNode *node )
1238 if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
1239 !node->isToltecMessage() || mShowRawToltecMail )
1242 htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
1243 htmlWriter()->queue(
"<br><br><a href=\"kmail:showRawToltecMail\">" +
1244 i18n(
"Show Raw Message" ) +
"</a>" );
1248 bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
1250 if ( processToltecMail( node ) ) {
1254 partNode * child = node->firstChild();
1259 stdChildHandling( child );
1263 bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
1264 partNode * child = node->firstChild();
1268 partNode * dataHtml = child->findType( DwMime::kTypeText,
1269 DwMime::kSubtypeHtml,
false,
true );
1270 partNode * dataPlain = child->findType( DwMime::kTypeText,
1271 DwMime::kSubtypePlain,
false,
true );
1273 if ( (mReader && mReader->htmlMail() && dataHtml) ||
1274 (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
1276 dataPlain->setProcessed(
true,
false );
1277 stdChildHandling( dataHtml );
1281 if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
1283 dataHtml->setProcessed(
true,
false );
1284 stdChildHandling( dataPlain );
1288 stdChildHandling( child );
1292 bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
1293 return processMultiPartMixedSubtype( node, result );
1296 bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
1297 return processMultiPartMixedSubtype( node, result );
1300 bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
1301 if ( node->childCount() != 2 ) {
1302 kdDebug(5006) <<
"mulitpart/signed must have exactly two child parts!" << endl
1303 <<
"processing as multipart/mixed" << endl;
1304 if ( node->firstChild() )
1305 stdChildHandling( node->firstChild() );
1306 return node->firstChild();
1309 partNode * signedData = node->firstChild();
1310 assert( signedData );
1312 partNode * signature = signedData->nextSibling();
1313 assert( signature );
1315 signature->setProcessed(
true,
true );
1317 if ( !includeSignatures() ) {
1318 stdChildHandling( signedData );
1326 const TQString contentType = node->contentTypeParameter(
"protocol" ).lower();
1327 const Kleo::CryptoBackend::Protocol *protocol = 0;
1328 if ( contentType ==
"application/pkcs7-signature" || contentType ==
"application/x-pkcs7-signature" )
1329 protocol = Kleo::CryptoBackendFactory::instance()->smime();
1330 else if ( contentType ==
"application/pgp-signature" || contentType ==
"application/x-pgp-signature" )
1331 protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
1334 signature->setProcessed(
true,
true );
1335 stdChildHandling( signedData );
1339 CryptoProtocolSaver saver(
this, protocol );
1341 node->setSignatureState( KMMsgFullySigned );
1342 writeOpaqueOrMultipartSignedData( signedData, *signature,
1343 node->trueFromAddress() );
1347 bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
1348 partNode * child = node->firstChild();
1352 if ( keepEncryptions() ) {
1353 node->setEncryptionState( KMMsgFullyEncrypted );
1354 const TQCString cstr = node->msgPart().bodyDecoded();
1356 writeBodyString( cstr, node->trueFromAddress(),
1357 codecFor( node ), result,
false );
1358 mRawReplyString += cstr;
1362 const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
1367 partNode * data = child->findType( DwMime::kTypeApplication,
1368 DwMime::kSubtypeOctetStream,
false,
true );
1370 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
1373 data = child->findType( DwMime::kTypeApplication,
1374 DwMime::kSubtypePkcs7Mime,
false,
true );
1376 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
1384 stdChildHandling( child );
1388 CryptoProtocolSaver cpws(
this, useThisCryptProto );
1390 if ( partNode * dataChild = data->firstChild() ) {
1392 stdChildHandling( dataChild );
1397 node->setEncryptionState( KMMsgFullyEncrypted );
1399 if ( mReader && !mReader->decryptMessage() ) {
1400 writeDeferredDecryptionBlock();
1401 data->setProcessed(
true,
false );
1406 PartMetaData messagePart;
1407 TQCString decryptedData;
1408 bool signatureFound;
1409 std::vector<GpgME::Signature> signatures;
1410 GpgME::Key signingKey;
1411 bool passphraseError;
1412 bool actuallyEncrypted =
true;
1413 bool decryptionStarted;
1415 bool bOkDecrypt = okDecryptMIME( *data,
1424 messagePart.errorText,
1425 messagePart.auditLogError,
1426 messagePart.auditLog );
1428 if ( decryptionStarted ) {
1429 writeDecryptionInProgressBlock();
1435 messagePart.isDecryptable = bOkDecrypt;
1436 messagePart.isEncrypted =
true;
1437 messagePart.isSigned =
false;
1438 htmlWriter()->queue( writeSigstatHeader( messagePart,
1440 node->trueFromAddress() ) );
1455 if ( signatureFound ) {
1456 writeOpaqueOrMultipartSignedData( 0,
1458 node->trueFromAddress(),
1464 node->setSignatureState( KMMsgFullySigned );
1466 insertAndParseNewChildNode( *node,
1471 mRawReplyString += decryptedData;
1475 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1480 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1481 data->setProcessed(
true,
false );
1486 bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
1488 && !attachmentStrategy()->inlineNestedMessages()
1489 && !showOnlyOneMimePart() )
1492 if ( partNode * child = node->firstChild() ) {
1494 ObjectTreeParser otp( mReader, cryptoProtocol() );
1495 otp.parseObjectTree( child );
1496 mRawReplyString += otp.rawReplyString();
1497 mTextualContent += otp.textualContent();
1498 if ( !otp.textualContentCharset().isEmpty() )
1499 mTextualContentCharset = otp.textualContentCharset();
1505 PartMetaData messagePart;
1507 messagePart.isEncrypted =
false;
1508 messagePart.isSigned =
false;
1509 messagePart.isEncapsulatedRfc822Message =
true;
1511 mReader->writeMessagePartToTempFile( &node->msgPart(),
1513 htmlWriter()->queue( writeSigstatHeader( messagePart,
1515 node->trueFromAddress(),
1518 TQCString rfc822messageStr( node->msgPart().bodyDecoded() );
1520 DwMessage* rfc822DwMessage =
new DwMessage();
1521 rfc822DwMessage->FromString( rfc822messageStr );
1522 rfc822DwMessage->Parse();
1523 KMMessage rfc822message( rfc822DwMessage );
1524 node->setFromAddress( rfc822message.from() );
1527 htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
1530 insertAndParseNewChildNode( *node,
1532 "encapsulated message",
false ,
1534 node->setDisplayedEmbedded(
true );
1536 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1541 bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
1542 if ( partNode * child = node->firstChild() ) {
1544 ObjectTreeParser otp( mReader, cryptoProtocol() );
1545 otp.parseObjectTree( child );
1546 mRawReplyString += otp.rawReplyString();
1547 mTextualContent += otp.textualContent();
1548 if ( !otp.textualContentCharset().isEmpty() )
1549 mTextualContentCharset = otp.textualContentCharset();
1554 const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
1555 if ( node->parentNode()
1556 && DwMime::kTypeMultipart == node->parentNode()->type()
1557 && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
1559 node->setEncryptionState( KMMsgFullyEncrypted );
1560 if ( keepEncryptions() ) {
1561 const TQCString cstr = node->msgPart().bodyDecoded();
1563 writeBodyString( cstr, node->trueFromAddress(),
1564 codecFor( node ), result,
false );
1565 mRawReplyString += cstr;
1566 }
else if ( mReader && !mReader->decryptMessage() ) {
1567 writeDeferredDecryptionBlock();
1572 PartMetaData messagePart;
1573 setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
1574 TQCString decryptedData;
1575 bool signatureFound;
1576 std::vector<GpgME::Signature> signatures;
1577 GpgME::Key signingKey;
1578 bool passphraseError;
1579 bool actuallyEncrypted =
true;
1580 bool decryptionStarted;
1582 bool bOkDecrypt = okDecryptMIME( *node,
1591 messagePart.errorText,
1592 messagePart.auditLogError,
1593 messagePart.auditLog );
1595 if ( decryptionStarted ) {
1596 writeDecryptionInProgressBlock();
1602 messagePart.isDecryptable = bOkDecrypt;
1603 messagePart.isEncrypted =
true;
1604 messagePart.isSigned =
false;
1605 htmlWriter()->queue( writeSigstatHeader( messagePart,
1607 node->trueFromAddress() ) );
1612 insertAndParseNewChildNode( *node,
1616 mRawReplyString += decryptedData;
1620 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1625 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1629 setCryptoProtocol( oldUseThisCryptPlug );
1633 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
1634 if ( partNode * child = node->firstChild() ) {
1636 ObjectTreeParser otp( mReader, cryptoProtocol() );
1637 otp.parseObjectTree( child );
1638 mRawReplyString += otp.rawReplyString();
1639 mTextualContent += otp.textualContent();
1640 if ( !otp.textualContentCharset().isEmpty() )
1641 mTextualContentCharset = otp.textualContentCharset();
1647 if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
1650 const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
1652 const TQString smimeType = node->contentTypeParameter(
"smime-type").lower();
1654 if ( smimeType ==
"certs-only" ) {
1655 result.setNeverDisplayInline(
true );
1656 if ( !smimeCrypto || !mReader )
1659 const TDEConfigGroup reader( KMKernel::config(),
"Reader" );
1660 if ( !reader.readBoolEntry(
"AutoImportKeys",
false ) )
1663 const TQByteArray certData = node->msgPart().bodyDecodedBinary();
1665 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::ImportJob>
import( smimeCrypto->importJob() );
1666 const GpgME::ImportResult res =
import->exec( certData );
1667 if ( res.error() ) {
1668 htmlWriter()->queue( i18n(
"Sorry, certificate could not be imported.<br>"
1669 "Reason: %1").arg( TQString::fromLocal8Bit( res.error().asString() ) ) );
1673 const int nImp = res.numImported();
1674 const int nUnc = res.numUnchanged();
1675 const int nSKImp = res.numSecretKeysImported();
1676 const int nSKUnc = res.numSecretKeysUnchanged();
1677 if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
1678 htmlWriter()->queue( i18n(
"Sorry, no certificates were found in this message." ) );
1681 TQString comment =
"<b>" + i18n(
"Certificate import status:" ) +
"</b><br> <br>";
1683 comment += i18n(
"1 new certificate was imported.",
1684 "%n new certificates were imported.", nImp ) +
"<br>";
1686 comment += i18n(
"1 certificate was unchanged.",
1687 "%n certificates were unchanged.", nUnc ) +
"<br>";
1689 comment += i18n(
"1 new secret key was imported.",
1690 "%n new secret keys were imported.", nSKImp ) +
"<br>";
1692 comment += i18n(
"1 secret key was unchanged.",
1693 "%n secret keys were unchanged.", nSKUnc ) +
"<br>";
1694 comment +=
" <br>";
1695 htmlWriter()->queue( comment );
1696 if ( !nImp && !nSKImp ) {
1697 htmlWriter()->queue(
"<hr>" );
1700 const std::vector<GpgME::Import> imports = res.imports();
1701 if ( imports.empty() ) {
1702 htmlWriter()->queue( i18n(
"Sorry, no details on certificate import available." ) +
"<hr>" );
1705 htmlWriter()->queue(
"<b>" + i18n(
"Certificate import details:" ) +
"</b><br>" );
1706 for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
1707 if ( (*it).error() )
1708 htmlWriter()->queue( i18n(
"Failed: %1 (%2)" )
1709 .arg( (*it).fingerprint(),
1710 TQString::fromLocal8Bit( (*it).error().asString() ) ) );
1711 else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
1712 if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
1713 htmlWriter()->queue( i18n(
"New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
1716 htmlWriter()->queue( i18n(
"New or changed: %1" ).arg( (*it).fingerprint() ) );
1719 htmlWriter()->queue(
"<br>" );
1722 htmlWriter()->queue(
"<hr>" );
1728 CryptoProtocolSaver cpws(
this, smimeCrypto );
1730 bool isSigned = smimeType ==
"signed-data";
1731 bool isEncrypted = smimeType ==
"enveloped-data";
1736 partNode* signTestNode = isEncrypted ? 0 : node;
1743 if ( isEncrypted ) {
1749 TQCString decryptedData;
1750 PartMetaData messagePart;
1751 messagePart.isEncrypted =
true;
1752 messagePart.isSigned =
false;
1753 bool signatureFound;
1754 std::vector<GpgME::Signature> signatures;
1755 GpgME::Key signingKey;
1756 bool passphraseError;
1757 bool actuallyEncrypted =
true;
1758 bool decryptionStarted;
1760 if ( mReader && !mReader->decryptMessage() ) {
1761 writeDeferredDecryptionBlock();
1765 const bool bOkDecrypt = okDecryptMIME( *node,
1774 messagePart.errorText,
1775 messagePart.auditLogError,
1776 messagePart.auditLog );
1777 if ( decryptionStarted ) {
1778 writeDecryptionInProgressBlock();
1784 node->setEncryptionState( KMMsgFullyEncrypted );
1787 messagePart.isDecryptable =
true;
1789 htmlWriter()->queue( writeSigstatHeader( messagePart,
1791 node->trueFromAddress() ) );
1792 insertAndParseNewChildNode( *node,
1796 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1802 if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
1807 if ( isEncrypted ) {
1810 messagePart.isDecryptable =
false;
1812 htmlWriter()->queue( writeSigstatHeader( messagePart,
1814 node->trueFromAddress() ) );
1815 assert( mReader->decryptMessage() );
1816 writePartIcon( &node->msgPart(), node->nodeId() );
1817 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1825 node->setEncryptionState( KMMsgFullyEncrypted );
1829 if ( signTestNode ) {
1837 bool sigFound = writeOpaqueOrMultipartSignedData( 0,
1839 node->trueFromAddress(),
1847 signTestNode->setSignatureState( KMMsgFullySigned );
1848 if ( signTestNode != node )
1849 node->setSignatureState( KMMsgFullySigned );
1855 return isSigned || isEncrypted;
1858bool ObjectTreeParser::decryptChiasmus(
const TQByteArray& data, TQByteArray& bodyDecoded, TQString& errorText )
1860 const Kleo::CryptoBackend::Protocol * chiasmus =
1861 Kleo::CryptoBackendFactory::instance()->protocol(
"Chiasmus" );
1862 Q_ASSERT( chiasmus );
1866 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob(
"x-obtain-keys", TQStringVariantMap() ) );
1868 errorText = i18n(
"Chiasmus backend does not offer the "
1869 "\"x-obtain-keys\" function. Please report this bug." );
1873 if ( listjob->exec() ) {
1874 errorText = i18n(
"Chiasmus Backend Error" );
1878 const TQVariant result = listjob->property(
"result" );
1879 if ( result.type() != TQVariant::StringList ) {
1880 errorText = i18n(
"Unexpected return value from Chiasmus backend: "
1881 "The \"x-obtain-keys\" function did not return a "
1882 "string list. Please report this bug." );
1886 const TQStringList keys = result.toStringList();
1887 if ( keys.empty() ) {
1888 errorText = i18n(
"No keys have been found. Please check that a "
1889 "valid key path has been set in the Chiasmus "
1894 emit mReader->noDrag();
1895 ChiasmusKeySelector selectorDlg( mReader, i18n(
"Chiasmus Decryption Key Selection" ),
1896 keys, GlobalSettings::chiasmusDecryptionKey(),
1897 GlobalSettings::chiasmusDecryptionOptions() );
1898 if ( selectorDlg.exec() != TQDialog::Accepted )
1901 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
1902 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
1903 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
1905 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob(
"x-decrypt", TQStringVariantMap() ) );
1907 errorText = i18n(
"Chiasmus backend does not offer the "
1908 "\"x-decrypt\" function. Please report this bug." );
1912 if ( !job->setProperty(
"key", GlobalSettings::chiasmusDecryptionKey() ) ||
1913 !job->setProperty(
"options", GlobalSettings::chiasmusDecryptionOptions() ) ||
1914 !job->setProperty(
"input", data ) ) {
1915 errorText = i18n(
"The \"x-decrypt\" function does not accept "
1916 "the expected parameters. Please report this bug." );
1920 if ( job->exec() ) {
1921 errorText = i18n(
"Chiasmus Decryption Error" );
1925 const TQVariant resultData = job->property(
"result" );
1926 if ( resultData.type() != TQVariant::ByteArray ) {
1927 errorText = i18n(
"Unexpected return value from Chiasmus backend: "
1928 "The \"x-decrypt\" function did not return a "
1929 "byte array. Please report this bug." );
1932 bodyDecoded = resultData.toByteArray();
1936bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
1939 mRawReplyString = curNode->msgPart().bodyDecoded();
1940 mTextualContent += curNode->msgPart().bodyToUnicode();
1941 mTextualContentCharset = curNode->msgPart().charset();
1945 TQByteArray decryptedBody;
1947 const TQByteArray data = curNode->msgPart().bodyDecodedBinary();
1948 bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
1949 PartMetaData messagePart;
1950 messagePart.isDecryptable = bOkDecrypt;
1951 messagePart.isEncrypted =
true;
1952 messagePart.isSigned =
false;
1953 messagePart.errorText = errorText;
1955 htmlWriter()->queue( writeSigstatHeader( messagePart,
1957 curNode->trueFromAddress() ) );
1958 const TQByteArray body = bOkDecrypt ? decryptedBody : data;
1959 const TQString chiasmusCharset = curNode->contentTypeParameter(
"chiasmus-charset");
1960 const TQTextCodec* aCodec = chiasmusCharset.isEmpty()
1961 ? codecFor( curNode )
1962 : KMMsgBase::codecForName( chiasmusCharset.ascii() );
1963 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ),
false ) );
1964 result.setInlineEncryptionState( KMMsgFullyEncrypted );
1966 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1970bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
1976 const TQString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
1978 if ( !parser.openFile( fileName ) || !parser.message()) {
1979 kdDebug() << k_funcinfo <<
"Could not parse " << fileName << endl;
1983 TQPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
1984 if ( tnefatts.isEmpty() ) {
1985 kdDebug() << k_funcinfo <<
"No attachments found in " << fileName << endl;
1989 if ( !showOnlyOneMimePart() ) {
1990 TQString label = node->msgPart().fileName().stripWhiteSpace();
1991 if ( label.isEmpty() )
1992 label = node->msgPart().name().stripWhiteSpace();
1995 const TQString dir = TQApplication::reverseLayout() ?
"rtl" :
"ltr" ;
1997 TQString htmlStr =
"<table cellspacing=\"1\" class=\"textAtm\">"
1998 "<tr class=\"textAtmH\"><td dir=\"" + dir +
"\">";
1999 if ( !fileName.isEmpty() )
2000 htmlStr +=
"<a href=\"" + node->asHREF(
"body" ) +
"\">"
2004 if ( !comment.isEmpty() )
2005 htmlStr +=
"<br>" + comment;
2006 htmlStr +=
"</td></tr><tr class=\"textAtmB\"><td>";
2007 htmlWriter()->queue( htmlStr );
2010 for ( uint i = 0; i < tnefatts.count(); ++i ) {
2011 KTNEFAttach *att = tnefatts.at( i );
2012 TQString label = att->displayName();
2013 if( label.isEmpty() )
2014 label = att->name();
2017 TQString dir = mReader->createTempDir(
"ktnef-" + TQString::number( i ) );
2018 parser.extractFileTo( att->name(), dir );
2019 mReader->mTempFiles.append( dir + TQDir::separator() + att->name() );
2020 TQString href =
"file:" + KURL::encode_string( dir + TQDir::separator() + att->name() );
2022 KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
2023 TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( mimeType->icon( TQString(),
false ), TDEIcon::Desktop );
2025 htmlWriter()->queue(
"<div><a href=\"" + href +
"\"><img src=\"" +
2026 iconName +
"\" border=\"0\" style=\"max-width: 100%\">" + label +
2030 if ( !showOnlyOneMimePart() )
2031 htmlWriter()->queue(
"</td></tr></table>" );
2036 void ObjectTreeParser::writeBodyString(
const TQCString & bodyString,
2037 const TQString & fromAddress,
2038 const TQTextCodec * codec,
2039 ProcessResult & result,
2041 assert( mReader ); assert( codec );
2042 KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
2043 KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
2044 writeBodyStr( bodyString, codec, fromAddress,
2045 inlineSignatureState, inlineEncryptionState, decorate );
2046 result.setInlineSignatureState( inlineSignatureState );
2047 result.setInlineEncryptionState( inlineEncryptionState );
2050 void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart,
int partNum,
bool inlineImage ) {
2051 if ( !mReader || !msgPart )
2054 TQString label = msgPart->fileName();
2055 if( label.isEmpty() )
2056 label = msgPart->name();
2057 if( label.isEmpty() )
2061 TQString comment = msgPart->contentDescription();
2063 if ( label == comment ) comment = TQString();
2065 TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
2067 TQString href = TQString(
"attachment:%1?place=body" ).arg( partNum );
2073 iconName = msgPart->iconName();
2074 if( iconName.right( 14 ) ==
"mime_empty.png" ) {
2075 msgPart->magicSetType();
2076 iconName = msgPart->iconName();
2080 TQCString contentId = msgPart->contentId();
2081 if ( !contentId.isEmpty() ) {
2082 htmlWriter()->embedPart( contentId, href );
2087 htmlWriter()->queue(
"<div><a href=\"" + href +
"\">"
2088 "<img src=\"" + fileName +
"\" border=\"0\" style=\"max-width: 100%\"></a>"
2090 "<div><a href=\"" + href +
"\">" + label +
"</a>"
2092 "<div>" + comment +
"</div><br>" );
2095 htmlWriter()->queue(
"<div><a href=\"" + href +
"\"><img src=\"" +
2096 iconName +
"\" border=\"0\" style=\"max-width: 100%\">" + label +
2098 "<div>" + comment +
"</div><br>" );
2101#define SIG_FRAME_COL_UNDEF 99
2102#define SIG_FRAME_COL_RED -1
2103#define SIG_FRAME_COL_YELLOW 0
2104#define SIG_FRAME_COL_GREEN 1
2105TQString ObjectTreeParser::sigStatusToString(
const Kleo::CryptoBackend::Protocol* cryptProto,
2107 GpgME::Signature::Summary summary,
2109 bool& showKeyInfos )
2114 showKeyInfos =
true;
2117 if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
2120 switch( status_code ) {
2122 result = i18n(
"Error: Signature not verified");
2125 result = i18n(
"Good signature");
2128 result = i18n(
"<b>Bad</b> signature");
2131 result = i18n(
"No public key to verify the signature");
2134 result = i18n(
"No signature found");
2137 result = i18n(
"Error verifying the signature");
2140 result = i18n(
"Different results for signatures");
2155 else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
2159 if( summary == GpgME::Signature::None ) {
2160 result = i18n(
"No status information available.");
2161 frameColor = SIG_FRAME_COL_YELLOW;
2162 showKeyInfos =
false;
2166 if( summary & GpgME::Signature::Valid ) {
2167 result = i18n(
"Good signature.");
2176 frameColor = SIG_FRAME_COL_GREEN;
2177 showKeyInfos =
false;
2184 frameColor = SIG_FRAME_COL_GREEN;
2186 if( summary & GpgME::Signature::KeyExpired ){
2188 result2 += i18n(
"One key has expired.");
2190 if( summary & GpgME::Signature::SigExpired ){
2192 result2 += i18n(
"The signature has expired.");
2196 if( summary & GpgME::Signature::KeyMissing ) {
2197 result2 += i18n(
"Unable to verify: key missing.");
2200 showKeyInfos =
false;
2201 frameColor = SIG_FRAME_COL_YELLOW;
2203 if( summary & GpgME::Signature::CrlMissing ){
2204 result2 += i18n(
"CRL not available.");
2205 frameColor = SIG_FRAME_COL_YELLOW;
2207 if( summary & GpgME::Signature::CrlTooOld ){
2208 result2 += i18n(
"Available CRL is too old.");
2209 frameColor = SIG_FRAME_COL_YELLOW;
2211 if( summary & GpgME::Signature::BadPolicy ){
2212 result2 += i18n(
"A policy was not met.");
2213 frameColor = SIG_FRAME_COL_YELLOW;
2215 if( summary & GpgME::Signature::SysError ){
2216 result2 += i18n(
"A system error occurred.");
2220 showKeyInfos =
false;
2221 frameColor = SIG_FRAME_COL_YELLOW;
2225 if( summary & GpgME::Signature::KeyRevoked ){
2227 result2 += i18n(
"One key has been revoked.");
2228 frameColor = SIG_FRAME_COL_RED;
2230 if( summary & GpgME::Signature::Red ) {
2231 if( result2.isEmpty() )
2244 showKeyInfos =
false;
2245 frameColor = SIG_FRAME_COL_RED;
2250 if( SIG_FRAME_COL_GREEN == frameColor ) {
2251 result = i18n(
"Good signature.");
2252 }
else if( SIG_FRAME_COL_RED == frameColor ) {
2253 result = i18n(
"<b>Bad</b> signature.");
2257 if( !result2.isEmpty() ) {
2258 if( !result.isEmpty() )
2259 result.append(
"<br />");
2260 result.append( result2 );
2274static TQString writeSimpleSigstatHeader(
const PartMetaData &block )
2277 html +=
"<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
2279 if ( block.signClass ==
"signErr" ) {
2280 html += i18n(
"Invalid signature." );
2281 }
else if ( block.signClass ==
"signOkKeyBad" || block.signClass ==
"signWarn" ) {
2282 html += i18n(
"Not enough information to check signature validity." );
2283 }
else if ( block.signClass ==
"signOkKeyOk" ) {
2285 if ( !block.signerMailAddresses.isEmpty() )
2286 addr = block.signerMailAddresses.first();
2287 TQString name = addr;
2288 if ( name.isEmpty() )
2289 name = block.signer;
2290 if ( addr.isEmpty() ) {
2291 html += i18n(
"Signature is valid." );
2293 html += i18n(
"Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
2297 html += i18n(
"Unknown signature state" );
2299 html +=
"</td><td align=\"right\">";
2300 html +=
"<a href=\"kmail:showSignatureDetails\">";
2301 html += i18n(
"Show Details" );
2302 html +=
"</a></td></tr></table>";
2306static TQString beginVerboseSigstatHeader()
2308 return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
2311static TQString makeShowAuditLogLink(
const GpgME::Error & err,
const TQString & auditLog ) {
2312 if (
const unsigned int code = err.code() ) {
2313 if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
2316 }
else if ( code == GPG_ERR_NO_DATA ) {
2318 return i18n(
"No Audit Log available");
2320 return i18n(
"Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) );
2324 if ( !auditLog.isEmpty() ) {
2326 url.setProtocol(
"kmail" );
2327 url.setPath(
"showAuditLog" );
2328 url.addQueryItem(
"log", auditLog );
2330 return "<a href=\"" + url.htmlURL() +
"\">" + i18n(
"The Audit Log is a detailed error log from the gnupg backend",
"Show Audit Log") +
"</a>";
2336static TQString endVerboseSigstatHeader(
const PartMetaData & pmd )
2339 html +=
"</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
2340 html +=
"<a href=\"kmail:hideSignatureDetails\">";
2341 html += i18n(
"Hide Details" );
2342 html +=
"</a></td></tr>";
2343 html +=
"<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
2344 html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
2345 html +=
"</td></tr></table>";
2349TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
2350 const Kleo::CryptoBackend::Protocol * cryptProto,
2351 const TQString & fromAddress,
2354 const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
2355 TQString signer = block.signer;
2357 TQString htmlStr, simpleHtmlStr;
2358 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2359 TQString cellPadding(
"cellpadding=\"1\"");
2361 if( block.isEncapsulatedRfc822Message )
2363 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"rfc822\">"
2364 "<tr class=\"rfc822H\"><td dir=\"" + dir +
"\">";
2366 htmlStr +=
"<a href=\"" + node->asHREF(
"body" ) +
"\">"
2367 + i18n(
"Encapsulated message") +
"</a>";
2369 htmlStr += i18n(
"Encapsulated message");
2370 htmlStr +=
"</td></tr><tr class=\"rfc822B\"><td>";
2373 if( block.isEncrypted )
2375 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"encr\">"
2376 "<tr class=\"encrH\"><td dir=\"" + dir +
"\">";
2377 if ( block.inProgress )
2378 htmlStr += i18n(
"Please wait while the message is being decrypted...");
2379 else if ( block.isDecryptable )
2380 htmlStr += i18n(
"Encrypted message");
2382 htmlStr += i18n(
"Encrypted message (decryption not possible)");
2383 if( !block.errorText.isEmpty() )
2384 htmlStr +=
"<br />" + i18n(
"Reason: %1").arg( block.errorText );
2386 htmlStr +=
"</td></tr><tr class=\"encrB\"><td>";
2389 if ( block.isSigned && block.inProgress )
2391 block.signClass =
"signInProgress";
2392 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"signInProgress\">"
2393 "<tr class=\"signInProgressH\"><td dir=\"" + dir +
"\">";
2394 htmlStr += i18n(
"Please wait while the signature is being verified...");
2395 htmlStr +=
"</td></tr><tr class=\"signInProgressB\"><td>";
2397 simpleHtmlStr = htmlStr;
2399 if ( block.isSigned && !block.inProgress ) {
2400 TQStringList& blockAddrs( block.signerMailAddresses );
2404 int frameColor = SIG_FRAME_COL_UNDEF;
2406 bool onlyShowKeyURL =
false;
2407 bool cannotCheckSignature =
true;
2408 TQString statusStr = sigStatusToString( cryptProto,
2415 if( statusStr.isEmpty() )
2416 statusStr = block.status;
2417 if( block.technicalProblem )
2418 frameColor = SIG_FRAME_COL_YELLOW;
2420 switch( frameColor ){
2421 case SIG_FRAME_COL_RED:
2422 cannotCheckSignature =
false;
2424 case SIG_FRAME_COL_YELLOW:
2425 cannotCheckSignature =
true;
2427 case SIG_FRAME_COL_GREEN:
2428 cannotCheckSignature =
false;
2437 TQString startKeyHREF;
2440 TQString(
"<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
2441 .arg( cryptProto->displayName(),
2444 TQString keyWithWithoutURL
2446 ? TQString(
"%1%2</a>")
2448 cannotCheckSignature ? i18n(
"[Details]") : (
"0x" + block.keyId) )
2449 :
"0x" + TQString::fromUtf8( block.keyId );
2453 showKeyInfos =
true;
2458 if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
2462 if( !statusStr.isEmpty() ) {
2463 statusStr.prepend(
"<i>");
2464 statusStr.append(
"</i>");
2468 switch( frameColor ) {
2469 case SIG_FRAME_COL_RED:
2470 block.signClass =
"signErr";
2471 onlyShowKeyURL =
true;
2473 case SIG_FRAME_COL_YELLOW:
2474 if( block.technicalProblem )
2475 block.signClass =
"signWarn";
2477 block.signClass =
"signOkKeyBad";
2479 case SIG_FRAME_COL_GREEN:
2480 block.signClass =
"signOkKeyOk";
2483 TQString greenCaseWarning;
2484 TQString msgFrom( KPIM::getEmailAddress(fromAddress) );
2485 TQString certificate;
2486 if( block.keyId.isEmpty() )
2487 certificate = i18n(
"certificate");
2489 certificate = startKeyHREF + i18n(
"certificate") +
"</a>";
2490 if( !blockAddrs.empty() ){
2491 if( blockAddrs.grep(
2493 false ).isEmpty() ) {
2498 i18n(
"Sender's mail address is not stored "
2499 "in the %1 used for signing.").arg(certificate) +
2510 for(TQStringList::ConstIterator it = blockAddrs.begin();
2511 it != blockAddrs.end(); ++it ){
2513 greenCaseWarning.append(
", <br /> ");
2515 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
2523 i18n(
"No mail address is stored in the %1 used for signing, "
2524 "so we cannot compare it to the sender's address %2.")
2525 .arg(certificate,msgFrom);
2527 if( !greenCaseWarning.isEmpty() ) {
2528 if( !statusStr.isEmpty() )
2529 statusStr.append(
"<br /> <br />");
2530 statusStr.append( greenCaseWarning );
2535 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2536 "class=\"" + block.signClass +
"\">"
2537 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2538 htmlStr += frame + beginVerboseSigstatHeader();
2539 simpleHtmlStr += frame;
2540 simpleHtmlStr += writeSimpleSigstatHeader( block );
2541 if( block.technicalProblem ) {
2542 htmlStr += block.errorText;
2544 else if( showKeyInfos ) {
2545 if( cannotCheckSignature ) {
2546 htmlStr += i18n(
"Not enough information to check "
2548 .arg( keyWithWithoutURL );
2552 if (block.signer.isEmpty())
2555 if( !blockAddrs.empty() ){
2557 signer =
"<a href=\"mailto:" + address +
"\">" + signer +
"</a>";
2561 if( block.keyId.isEmpty() ) {
2562 if( signer.isEmpty() || onlyShowKeyURL )
2563 htmlStr += i18n(
"Message was signed with unknown key." );
2565 htmlStr += i18n(
"Message was signed by %1." )
2568 TQDateTime created = block.creationTime;
2569 if( created.isValid() ) {
2570 if( signer.isEmpty() ) {
2571 if( onlyShowKeyURL )
2572 htmlStr += i18n(
"Message was signed with key %1." )
2573 .arg( keyWithWithoutURL );
2575 htmlStr += i18n(
"Message was signed on %1 with key %2." )
2576 .arg( TDEGlobal::locale()->formatDateTime( created ),
2577 keyWithWithoutURL );
2580 if( onlyShowKeyURL )
2581 htmlStr += i18n(
"Message was signed with key %1." )
2582 .arg( keyWithWithoutURL );
2584 htmlStr += i18n(
"Message was signed by %3 on %1 with key %2" )
2585 .arg( TDEGlobal::locale()->formatDateTime( created ),
2591 if( signer.isEmpty() || onlyShowKeyURL )
2592 htmlStr += i18n(
"Message was signed with key %1." )
2593 .arg( keyWithWithoutURL );
2595 htmlStr += i18n(
"Message was signed by %2 with key %1." )
2596 .arg( keyWithWithoutURL,
2601 htmlStr +=
"<br />";
2602 if( !statusStr.isEmpty() ) {
2603 htmlStr +=
" <br />";
2604 htmlStr += i18n(
"Status: " );
2605 htmlStr += statusStr;
2608 htmlStr += statusStr;
2610 frame =
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
2611 htmlStr += endVerboseSigstatHeader( block ) + frame;
2612 simpleHtmlStr += frame;
2618 if( block.signer.isEmpty() || block.technicalProblem ) {
2619 block.signClass =
"signWarn";
2620 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2621 "class=\"" + block.signClass +
"\">"
2622 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2623 htmlStr += frame + beginVerboseSigstatHeader();
2624 simpleHtmlStr += frame;
2625 simpleHtmlStr += writeSimpleSigstatHeader( block );
2626 if( block.technicalProblem ) {
2627 htmlStr += block.errorText;
2630 if( !block.keyId.isEmpty() ) {
2631 TQDateTime created = block.creationTime;
2632 if ( created.isValid() )
2633 htmlStr += i18n(
"Message was signed on %1 with unknown key %2." )
2634 .arg( TDEGlobal::locale()->formatDateTime( created ),
2635 keyWithWithoutURL );
2637 htmlStr += i18n(
"Message was signed with unknown key %1." )
2638 .arg( keyWithWithoutURL );
2641 htmlStr += i18n(
"Message was signed with unknown key." );
2642 htmlStr +=
"<br />";
2643 htmlStr += i18n(
"The validity of the signature cannot be "
2645 if( !statusStr.isEmpty() ) {
2646 htmlStr +=
"<br />";
2647 htmlStr += i18n(
"Status: " );
2649 htmlStr += statusStr;
2653 frame =
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
2654 htmlStr += endVerboseSigstatHeader( block ) + frame;
2655 simpleHtmlStr += frame;
2661 signer =
"<a href=\"mailto:" + signer +
"\">" + signer +
"</a>";
2663 if (block.isGoodSignature) {
2664 if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
2665 block.signClass =
"signOkKeyBad";
2667 block.signClass =
"signOkKeyOk";
2668 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2669 "class=\"" + block.signClass +
"\">"
2670 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2671 htmlStr += frame + beginVerboseSigstatHeader();
2672 simpleHtmlStr += frame;
2673 simpleHtmlStr += writeSimpleSigstatHeader( block );
2674 if( !block.keyId.isEmpty() )
2675 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
2676 .arg( keyWithWithoutURL,
2679 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
2680 htmlStr +=
"<br />";
2682 switch( block.keyTrust )
2684 case Kpgp::KPGP_VALIDITY_UNKNOWN:
2685 htmlStr += i18n(
"The signature is valid, but the key's "
2686 "validity is unknown." );
2688 case Kpgp::KPGP_VALIDITY_MARGINAL:
2689 htmlStr += i18n(
"The signature is valid and the key is "
2690 "marginally trusted." );
2692 case Kpgp::KPGP_VALIDITY_FULL:
2693 htmlStr += i18n(
"The signature is valid and the key is "
2696 case Kpgp::KPGP_VALIDITY_ULTIMATE:
2697 htmlStr += i18n(
"The signature is valid and the key is "
2698 "ultimately trusted." );
2701 htmlStr += i18n(
"The signature is valid, but the key is "
2704 frame =
"</td></tr>"
2705 "<tr class=\"" + block.signClass +
"B\"><td>";
2706 htmlStr += endVerboseSigstatHeader( block ) + frame;
2707 simpleHtmlStr += frame;
2711 block.signClass =
"signErr";
2712 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2713 "class=\"" + block.signClass +
"\">"
2714 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2715 htmlStr += frame + beginVerboseSigstatHeader();
2716 simpleHtmlStr += frame;
2717 simpleHtmlStr += writeSimpleSigstatHeader( block );
2718 if( !block.keyId.isEmpty() )
2719 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
2720 .arg( keyWithWithoutURL,
2723 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
2724 htmlStr +=
"<br />";
2725 htmlStr += i18n(
"Warning: The signature is bad.");
2726 frame =
"</td></tr>"
2727 "<tr class=\"" + block.signClass +
"B\"><td>";
2728 htmlStr += endVerboseSigstatHeader( block ) + frame;
2729 simpleHtmlStr += frame;
2735 if ( mReader->showSignatureDetails() )
2737 return simpleHtmlStr;
2740TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
2742 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2746 if (block.isSigned) {
2747 htmlStr +=
"</td></tr><tr class=\"" + block.signClass +
"H\">";
2748 htmlStr +=
"<td dir=\"" + dir +
"\">" +
2749 i18n(
"End of signed message" ) +
2750 "</td></tr></table>";
2753 if (block.isEncrypted) {
2754 htmlStr +=
"</td></tr><tr class=\"encrH\"><td dir=\"" + dir +
"\">" +
2755 i18n(
"End of encrypted message" ) +
2756 "</td></tr></table>";
2759 if( block.isEncapsulatedRfc822Message )
2761 htmlStr +=
"</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir +
"\">" +
2762 i18n(
"End of encapsulated message" ) +
2763 "</td></tr></table>";
2771void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
2776 htmlWriter()->queue( TQString(
"<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
2781void ObjectTreeParser::writeAttachmentMarkFooter()
2786 htmlWriter()->queue( TQString(
"</div>" ) );
2790void ObjectTreeParser::writeBodyStr(
const TQCString& aStr,
const TQTextCodec *aCodec,
2791 const TQString& fromAddress )
2793 KMMsgSignatureState dummy1;
2794 KMMsgEncryptionState dummy2;
2795 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2,
false );
2799void ObjectTreeParser::writeBodyStr(
const TQCString& aStr,
const TQTextCodec *aCodec,
2800 const TQString& fromAddress,
2801 KMMsgSignatureState& inlineSignatureState,
2802 KMMsgEncryptionState& inlineEncryptionState,
2805 bool goodSignature =
false;
2806 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
2808 bool isPgpMessage =
false;
2810 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2811 TQString headerStr = TQString(
"<div dir=\"%1\">").arg(dir);
2813 inlineSignatureState = KMMsgNotSigned;
2814 inlineEncryptionState = KMMsgNotEncrypted;
2815 TQPtrList<Kpgp::Block> pgpBlocks;
2816 TQStrList nonPgpBlocks;
2817 if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
2819 bool isEncrypted =
false, isSigned =
false;
2820 bool fullySignedOrEncrypted =
true;
2821 bool firstNonPgpBlock =
true;
2822 bool couldDecrypt =
false;
2825 TQString decryptionError;
2826 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
2828 TQPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
2830 TQStrListIterator npbit( nonPgpBlocks );
2833 for( ; *pbit != 0; ++pbit, ++npbit )
2836 TQCString str( *npbit );
2837 if( !str.isEmpty() ) {
2838 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2843 if( firstNonPgpBlock ) {
2845 for( TQCString::ConstIterator c = str.begin(); *c; ++c ) {
2847 fullySignedOrEncrypted =
false;
2853 fullySignedOrEncrypted =
false;
2856 firstNonPgpBlock =
false;
2860 Kpgp::Block* block = *pbit;
2861 if( ( block->type() == Kpgp::PgpMessageBlock &&
2863 !kmkernel->contextMenuShown() ) ||
2864 ( block->type() == Kpgp::ClearsignedBlock ) )
2866 isPgpMessage =
true;
2867 if( block->type() == Kpgp::PgpMessageBlock )
2870 emit mReader->noDrag();
2872 couldDecrypt = block->decrypt();
2873 isEncrypted = block->isEncrypted();
2874 if (!couldDecrypt) {
2875 decryptionError = pgp->lastErrorMsg();
2884 isSigned = block->isSigned();
2887 keyId = block->signatureKeyId();
2888 signer = block->signatureUserId();
2889 if( !signer.isEmpty() )
2891 goodSignature = block->goodSignature();
2893 if( !keyId.isEmpty() ) {
2894 keyTrust = pgp->keyTrust( keyId );
2895 Kpgp::Key* key = pgp->publicKey( keyId );
2899 signer = key->primaryUserID();
2905 keyTrust = pgp->keyTrust( signer );
2910 inlineSignatureState = KMMsgPartiallySigned;
2912 inlineEncryptionState = KMMsgPartiallyEncrypted;
2914 PartMetaData messagePart;
2916 messagePart.isSigned = isSigned;
2917 messagePart.technicalProblem =
false;
2918 messagePart.isGoodSignature = goodSignature;
2919 messagePart.isEncrypted = isEncrypted;
2920 messagePart.isDecryptable = couldDecrypt;
2921 messagePart.decryptionError = decryptionError;
2922 messagePart.signer = signer;
2923 messagePart.keyId = keyId;
2924 messagePart.keyTrust = keyTrust;
2926 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
2928 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
2929 htmlStr += writeSigstatFooter( messagePart );
2932 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
2937 TQCString str( nonPgpBlocks.last() );
2938 if( !str.isEmpty() ) {
2939 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2946 if( fullySignedOrEncrypted ) {
2947 if( inlineSignatureState == KMMsgPartiallySigned )
2948 inlineSignatureState = KMMsgFullySigned;
2949 if( inlineEncryptionState == KMMsgPartiallyEncrypted )
2950 inlineEncryptionState = KMMsgFullyEncrypted;
2952 htmlWriter()->queue( htmlStr );
2955 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
2959TQString ObjectTreeParser::quotedHTML(
const TQString& s,
bool decorate )
2962 assert( cssHelper() );
2964 int convertFlags = LinkLocator::PreserveSpaces;
2965 if ( decorate && GlobalSettings::self()->showEmoticons() ) {
2966 convertFlags |= LinkLocator::ReplaceSmileys;
2969 const TQString normalStartTag = cssHelper()->nonQuotedFontTag();
2970 TQString quoteFontTag[3];
2971 TQString deepQuoteFontTag[3];
2972 for (
int i = 0 ; i < 3 ; ++i ) {
2973 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
2974 deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
2976 const TQString normalEndTag =
"</div>";
2977 const TQString quoteEnd =
"</div>";
2979 unsigned int pos, beg;
2980 const unsigned int length = s.length();
2983 for ( pos = 0; pos < length && s.at(pos) <= TQChar(
' '); pos++ ) { ; }
2984 while (pos > 0 && (s[pos-1] ==
' ' || s[pos-1] ==
'\t')) pos--;
2987 int currQuoteLevel = -2;
2988 bool curHidden =
false;
2995 pos = s.find(
'\n', beg,
false);
2996 if (pos == (
unsigned int)(-1))
2999 line = s.mid(beg,pos-beg);
3003 int actQuoteLevel = -1;
3005 if ( GlobalSettings::self()->showExpandQuotesMark() )
3008 if ( mCollapseIcon.isEmpty() ) {
3009 mCollapseIcon= LinkLocator::pngToDataUrl(
3010 TDEGlobal::instance()->iconLoader()->iconPath(
"quotecollapse",0 ));
3012 if ( mExpandIcon.isEmpty() )
3013 mExpandIcon= LinkLocator::pngToDataUrl(
3014 TDEGlobal::instance()->iconLoader()->iconPath(
"quoteexpand",0 ));
3017 for (
unsigned int p=0; p<line.length(); p++) {
3018 switch (line[p].latin1()) {
3033 bool actHidden =
false;
3034 TQString textExpand;
3037 if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
3038 && mReader->mLevelQuote <= ( actQuoteLevel ) )
3041 if ( actQuoteLevel != currQuoteLevel ) {
3043 if (currQuoteLevel == -1)
3044 htmlStr.append( normalEndTag );
3045 else if ( currQuoteLevel >= 0 && !curHidden )
3046 htmlStr.append( quoteEnd );
3049 if (actQuoteLevel == -1)
3050 htmlStr += normalStartTag;
3053 if ( GlobalSettings::self()->showExpandQuotesMark() )
3061 htmlStr +=
"<div class=\"quotelevelmark\" >" ;
3062 htmlStr += TQString(
"<a href=\"kmail:levelquote?%1 \">"
3063 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3065 .arg( mExpandIcon );
3066 htmlStr +=
"</div><br/>";
3067 htmlStr += quoteEnd;
3070 htmlStr +=
"<div class=\"quotelevelmark\" >" ;
3071 htmlStr += TQString(
"<a href=\"kmail:levelquote?%1 \">"
3072 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3074 .arg( mCollapseIcon);
3075 htmlStr +=
"</div>";
3076 if ( actQuoteLevel < 3 )
3077 htmlStr += quoteFontTag[actQuoteLevel];
3079 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3082 if ( actQuoteLevel < 3 )
3083 htmlStr += quoteFontTag[actQuoteLevel];
3085 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3087 currQuoteLevel = actQuoteLevel;
3089 curHidden = actHidden;
3096 if( !line.replace(
'\015',
"").isEmpty() )
3098 htmlStr +=TQString(
"<div dir=\"%1\">" ).arg( line.isRightToLeft() ?
"rtl":
"ltr" );
3099 htmlStr += LinkLocator::convertToHtml( line, convertFlags );
3100 htmlStr += TQString(
"</div>" );
3108 if (currQuoteLevel == -1)
3109 htmlStr.append( normalEndTag );
3111 htmlStr.append( quoteEnd );
3118 const TQTextCodec * ObjectTreeParser::codecFor( partNode * node )
const {
3120 if ( mReader && mReader->overrideCodec() )
3121 return mReader->overrideCodec();
3122 return node->msgPart().codec();
3126 void ObjectTreeParser::dumpToFile(
const char * filename,
const char * start,
3130 TQFile f( filename );
3131 if ( f.open( IO_WriteOnly ) ) {
3133 TQDataStream ds( &f );
3134 ds.writeRawBytes( start, len );
static TQString quoteHtmlChars(const TQString &str, bool removeLineBreaks=false)
Quotes the following characters which have a special meaning in HTML: '<' '>' '&' '"'....
static TQCString html2source(const TQCString &src)
Convert '<' into "<" resp.
static TQString encodeMailtoUrl(const TQString &str)
Encodes an email address as mailto URL.
This class implements a "reader window", that is a window used for reading or viewing messages.
KMail::HtmlWriter * htmlWriter()
Return the HtmlWriter connected to the TDEHTMLPart we use.
const KMail::AttachmentStrategy * attachmentStrategy() const
Get/set the message attachment strategy.
TQCString CString(const DwString &str)
Construct a TQCString from a DwString.