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"
49 #include "csshelper.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>
79 #include <kpgpblock.h>
81 #include <linklocator.h>
83 #include <ktnef/ktnefparser.h>
84 #include <ktnef/ktnefmessage.h>
85 #include <ktnef/ktnefattach.h>
89 #include <tdelocale.h>
90 #include <kmimetype.h>
91 #include <tdeglobal.h>
92 #include <tdehtml_part.h>
93 #include <tdetempfile.h>
94 #include <kstandarddirs.h>
95 #include <tdeapplication.h>
96 #include <tdemessagebox.h>
97 #include <kiconloader.h>
101 #include <tqtextcodec.h>
104 #include <tqapplication.h>
105 #include <tdestyle.h>
106 #include <tqbuffer.h>
107 #include <tqpixmap.h>
108 #include <tqpainter.h>
109 #include <tqregexp.h>
114 #include <sys/stat.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,
418 TQCString* cleartextData,
419 const std::vector<GpgME::Signature> & paramSignatures,
422 bool bIsOpaqueSigned =
false;
423 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_VERIFY_SIGNATURES }
424 cryptPlugError = NO_PLUGIN;
426 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
428 TQString cryptPlugLibName;
429 TQString cryptPlugDisplayName;
431 cryptPlugLibName = cryptProto->name();
432 cryptPlugDisplayName = cryptProto->displayName();
449 if ( doCheck && cryptProto ) {
455 TQByteArray signaturetext;
457 if ( doCheck && cryptProto ) {
461 dumpToFile(
"dat_01_reader_signedtext_before_canonicalization",
462 cleartext.data(), cleartext.length() );
467 cleartext = Util::lf2crlf( cleartext );
471 dumpToFile(
"dat_02_reader_signedtext_after_canonicalization",
472 cleartext.data(), cleartext.length() );
474 signaturetext = sign.msgPart().bodyDecodedBinary();
475 dumpToFile(
"dat_03_reader.sig", signaturetext.data(),
476 signaturetext.size() );
479 std::vector<GpgME::Signature> signatures;
481 signatures = paramSignatures;
483 PartMetaData messagePart;
484 messagePart.isSigned =
true;
485 messagePart.technicalProblem = ( cryptProto == 0 );
486 messagePart.isGoodSignature =
false;
487 messagePart.isEncrypted =
false;
488 messagePart.isDecryptable =
false;
489 messagePart.keyTrust = Kpgp::KPGP_VALIDITY_UNKNOWN;
490 messagePart.status = i18n(
"Wrong Crypto Plug-In.");
491 messagePart.status_code = GPGME_SIG_STAT_NONE;
495 if ( doCheck && cryptProto ) {
496 GpgME::VerificationResult result;
498 const VerifyDetachedBodyPartMemento * m
499 =
dynamic_cast<VerifyDetachedBodyPartMemento*
>( sign.bodyPartMemento(
"verifydetached" ) );
501 Kleo::VerifyDetachedJob * job = cryptProto->verifyDetachedJob();
503 cryptPlugError = CANT_VERIFY_SIGNATURES;
506 TQByteArray plainData = cleartext;
507 plainData.resize( cleartext.size() - 1 );
508 VerifyDetachedBodyPartMemento * newM
509 =
new VerifyDetachedBodyPartMemento( job, cryptProto->keyListJob(), signaturetext, plainData );
510 if ( allowAsync() ) {
511 if ( newM->start() ) {
512 messagePart.inProgress =
true;
513 mHasPendingAsyncJobs =
true;
521 sign.setBodyPartMemento(
"verifydetached", newM );
523 }
else if ( m->isRunning() ) {
524 messagePart.inProgress =
true;
525 mHasPendingAsyncJobs =
true;
530 result = m->verifyResult();
531 messagePart.auditLogError = m->auditLogError();
532 messagePart.auditLog = m->auditLogAsHtml();
533 key = m->signingKey();
536 const VerifyOpaqueBodyPartMemento * m
537 =
dynamic_cast<VerifyOpaqueBodyPartMemento*
>( sign.bodyPartMemento(
"verifyopaque" ) );
539 Kleo::VerifyOpaqueJob * job = cryptProto->verifyOpaqueJob();
541 cryptPlugError = CANT_VERIFY_SIGNATURES;
544 VerifyOpaqueBodyPartMemento * newM
545 =
new VerifyOpaqueBodyPartMemento( job, cryptProto->keyListJob(), signaturetext );
546 if ( allowAsync() ) {
547 if ( newM->start() ) {
548 messagePart.inProgress =
true;
549 mHasPendingAsyncJobs =
true;
557 sign.setBodyPartMemento(
"verifyopaque", newM );
559 }
else if ( m->isRunning() ) {
560 messagePart.inProgress =
true;
561 mHasPendingAsyncJobs =
true;
566 result = m->verifyResult();
567 const TQByteArray & plainData = m->plainText();
568 cleartext = TQCString( plainData.data(), plainData.size() + 1 );
569 messagePart.auditLogError = m->auditLogError();
570 messagePart.auditLog = m->auditLogAsHtml();
571 key = m->signingKey();
574 std::stringstream ss;
577 signatures = result.signatures();
585 if ( signatures.size() > 0 ) {
587 GpgME::Signature signature = signatures[0];
589 messagePart.status_code = signatureToStatus( signature );
590 messagePart.status = TQString::fromUtf8( signature.status().asString() );
591 for ( uint i = 1; i < signatures.size(); ++i ) {
592 if ( signatureToStatus( signatures[i] ) != messagePart.status_code ) {
593 messagePart.status_code = GPGME_SIG_STAT_DIFF;
594 messagePart.status = i18n(
"Different results for signatures");
597 if ( messagePart.status_code & GPGME_SIG_STAT_GOOD )
598 messagePart.isGoodSignature =
true;
601 messagePart.sigSummary = signature.summary();
604 messagePart.keyId = key.keyID();
605 if ( messagePart.keyId.isEmpty() )
606 messagePart.keyId = signature.fingerprint();
608 messagePart.keyTrust = (Kpgp::Validity)signature.validity();
609 if ( key.numUserIDs() > 0 && key.userID( 0 ).id() )
610 messagePart.signer = Kleo::DN( key.userID( 0 ).id() ).prettyDN();
611 for ( uint iMail = 0; iMail < key.numUserIDs(); ++iMail ) {
614 if ( key.userID( iMail ).email() ) {
615 TQString email = TQString::fromUtf8( key.userID( iMail ).email() );
618 if ( email.startsWith(
"<" ) && email.endsWith(
">" ) )
619 email = email.mid( 1, email.length() - 2 );
620 if ( !email.isEmpty() )
621 messagePart.signerMailAddresses.append( email );
625 if ( signature.creationTime() )
626 messagePart.creationTime.setTime_t( signature.creationTime() );
628 messagePart.creationTime = TQDateTime();
629 if ( messagePart.signer.isEmpty() ) {
630 if ( key.numUserIDs() > 0 && key.userID( 0 ).name() )
631 messagePart.signer = Kleo::DN( key.userID( 0 ).name() ).prettyDN();
632 if ( !messagePart.signerMailAddresses.empty() ) {
633 if ( messagePart.signer.isEmpty() )
634 messagePart.signer = messagePart.signerMailAddresses.front();
636 messagePart.signer +=
" <" + messagePart.signerMailAddresses.front() +
'>';
645 messagePart.creationTime = TQDateTime();
648 if ( !doCheck || !data ){
649 if ( cleartextData || !cleartext.isEmpty() ) {
651 htmlWriter()->queue( writeSigstatHeader( messagePart,
654 bIsOpaqueSigned =
true;
656 CryptoProtocolSaver cpws(
this, cryptProto );
657 insertAndParseNewChildNode( sign, doCheck ? cleartext.data() : cleartextData->data(),
658 "opaqued signed data" );
661 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
664 else if ( !hideErrors ) {
667 txt.append( i18n(
"The crypto engine returned no cleartext data." ) );
668 txt.append(
"</h2></b>" );
669 txt.append(
"<br> <br>" );
670 txt.append( i18n(
"Status: " ) );
671 if ( !messagePart.status.isEmpty() ) {
673 txt.append( messagePart.status );
674 txt.append(
"</i>" );
677 txt.append( i18n(
"(unknown)") );
679 htmlWriter()->queue(txt);
686 switch ( cryptPlugError ) {
687 case NOT_INITIALIZED:
688 errorMsg = i18n(
"Crypto plug-in \"%1\" is not initialized." )
689 .arg( cryptPlugLibName );
691 case CANT_VERIFY_SIGNATURES:
692 errorMsg = i18n(
"Crypto plug-in \"%1\" cannot verify signatures." )
693 .arg( cryptPlugLibName );
696 if ( cryptPlugDisplayName.isEmpty() )
697 errorMsg = i18n(
"No appropriate crypto plug-in was found." );
699 errorMsg = i18n(
"%1 is either 'OpenPGP' or 'S/MIME'",
700 "No %1 plug-in was found." )
701 .arg( cryptPlugDisplayName );
704 messagePart.errorText = i18n(
"The message is signed, but the "
705 "validity of the signature cannot be "
712 htmlWriter()->queue( writeSigstatHeader( messagePart,
717 ObjectTreeParser otp( mReader, cryptProto,
true );
718 otp.parseObjectTree( data );
719 mRawReplyString += otp.rawReplyString();
720 mTextualContent += otp.textualContent();
721 if ( !otp.textualContentCharset().isEmpty() )
722 mTextualContentCharset = otp.textualContentCharset();
725 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
730 return bIsOpaqueSigned;
733 void ObjectTreeParser::writeDecryptionInProgressBlock() {
737 const TQString decryptedData = i18n(
"Encrypted data not shown");
738 PartMetaData messagePart;
739 messagePart.isDecryptable =
true;
740 messagePart.isEncrypted =
true;
741 messagePart.isSigned =
false;
742 messagePart.inProgress =
true;
743 htmlWriter()->queue( writeSigstatHeader( messagePart,
747 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
750 void ObjectTreeParser::writeDeferredDecryptionBlock() {
752 const TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath(
"decrypted", TDEIcon::Small );
753 const TQString decryptedData =
754 "<div style=\"font-size:large; text-align:center;padding-top:20pt;\">" +
755 i18n(
"This message is encrypted.") +
757 "<div style=\"text-align:center; padding-bottom:20pt;\">"
758 "<a href=\"kmail:decryptMessage\">"
759 "<img src=\"" + iconName +
"\"/>" +
760 i18n(
"Decrypt Message") +
762 PartMetaData messagePart;
763 messagePart.isDecryptable =
true;
764 messagePart.isEncrypted =
true;
765 messagePart.isSigned =
false;
766 mRawReplyString += decryptedData.utf8();
767 htmlWriter()->queue( writeSigstatHeader( messagePart,
770 htmlWriter()->queue( decryptedData );
771 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
774 bool ObjectTreeParser::okDecryptMIME( partNode& data,
775 TQCString& decryptedData,
776 bool& signatureFound,
777 std::vector<GpgME::Signature> &signatures,
779 bool& passphraseError,
780 bool& actuallyEncrypted,
781 bool& decryptionStarted,
782 TQString& aErrorText,
783 GpgME::Error & auditLogError,
786 passphraseError =
false;
787 decryptionStarted =
false;
788 aErrorText = TQString();
789 auditLogError = GpgME::Error();
790 auditLog = TQString();
791 bool bDecryptionOk =
false;
792 enum { NO_PLUGIN, NOT_INITIALIZED, CANT_DECRYPT }
793 cryptPlugError = NO_PLUGIN;
795 const Kleo::CryptoBackend::Protocol* cryptProto = cryptoProtocol();
797 TQString cryptPlugLibName;
799 cryptPlugLibName = cryptProto->name();
801 assert( !mReader || mReader->decryptMessage() );
803 if ( cryptProto && !kmkernel->contextMenuShown() ) {
804 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
806 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
807 bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
808 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
809 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
811 dumpToFile(
"dat_04_reader.encrypted", ciphertext.data(), ciphertext.size() );
814 deb =
"\n\nE N C R Y P T E D D A T A = ";
815 if ( cipherIsBinary )
816 deb +=
"[binary data]";
823 kdDebug(5006) << deb << endl;
830 emit mReader->noDrag();
833 const DecryptVerifyBodyPartMemento * m
834 =
dynamic_cast<DecryptVerifyBodyPartMemento*
>( data.bodyPartMemento(
"decryptverify" ) );
836 Kleo::DecryptVerifyJob * job = cryptProto->decryptVerifyJob();
838 cryptPlugError = CANT_DECRYPT;
841 DecryptVerifyBodyPartMemento * newM
842 =
new DecryptVerifyBodyPartMemento( job, ciphertext );
843 if ( allowAsync() ) {
844 if ( newM->start() ) {
845 decryptionStarted =
true;
846 mHasPendingAsyncJobs =
true;
854 data.setBodyPartMemento(
"decryptverify", newM );
856 }
else if ( m->isRunning() ) {
857 decryptionStarted =
true;
858 mHasPendingAsyncJobs =
true;
863 const TQByteArray & plainText = m->plainText();
864 const GpgME::DecryptionResult & decryptResult = m->decryptResult();
865 const GpgME::VerificationResult & verifyResult = m->verifyResult();
866 std::stringstream ss;
867 ss << decryptResult <<
'\n' << verifyResult;
869 signatureFound = verifyResult.signatures().size() > 0;
870 signatures = verifyResult.signatures();
871 bDecryptionOk = !decryptResult.error();
872 passphraseError = decryptResult.error().isCanceled()
873 || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
874 actuallyEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
875 aErrorText = TQString::fromLocal8Bit( decryptResult.error().asString() );
876 auditLogError = m->auditLogError();
877 auditLog = m->auditLogAsHtml();
882 decryptedData = TQCString( plainText.data(), plainText.size() + 1 );
883 else if ( mReader && showWarning ) {
884 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
886 + i18n(
"Encrypted data not shown.").utf8()
888 if ( !passphraseError )
889 aErrorText = i18n(
"Crypto plug-in \"%1\" could not decrypt the data.")
890 .arg( cryptPlugLibName )
892 + i18n(
"Error: %1").arg( aErrorText );
898 decryptedData =
"<div style=\"text-align:center; padding:20pt;\">"
899 + i18n(
"Encrypted data not shown.").utf8()
901 switch ( cryptPlugError ) {
902 case NOT_INITIALIZED:
903 aErrorText = i18n(
"Crypto plug-in \"%1\" is not initialized." )
904 .arg( cryptPlugLibName );
907 aErrorText = i18n(
"Crypto plug-in \"%1\" cannot decrypt messages." )
908 .arg( cryptPlugLibName );
911 aErrorText = i18n(
"No appropriate crypto plug-in was found." );
914 }
else if ( kmkernel->contextMenuShown() ) {
917 TQByteArray ciphertext( data.msgPart().bodyDecodedBinary() );
918 TQCString cipherStr( ciphertext.data(), ciphertext.size() + 1 );
919 bool cipherIsBinary = (-1 == cipherStr.find(
"BEGIN ENCRYPTED MESSAGE", 0,
false) ) &&
920 (-1 == cipherStr.find(
"BEGIN PGP ENCRYPTED MESSAGE", 0,
false) ) &&
921 (-1 == cipherStr.find(
"BEGIN PGP MESSAGE", 0,
false) );
922 if ( !cipherIsBinary ) {
923 decryptedData = cipherStr;
926 decryptedData =
"<div style=\"font-size:x-large; text-align:center;"
928 + i18n(
"Encrypted data not shown.").utf8()
933 dumpToFile(
"dat_05_reader.decrypted", decryptedData.data(), decryptedData.size() );
935 return bDecryptionOk;
939 bool ObjectTreeParser::containsExternalReferences(
const TQCString & str )
941 TQRegExp httpRegExp(
"(\\\"|\\\'|url\\s*\\(\\s*)http[s]?:");
942 int httpPos = str.find( httpRegExp, 0 );
944 while ( httpPos >= 0 ) {
947 int hrefPos = str.findRev(
"href", httpPos - 5,
true );
951 if ( ( hrefPos == -1 ) || ( httpPos - hrefPos > 7 ) )
955 httpPos = str.find( httpRegExp, httpPos + 6 );
960 bool ObjectTreeParser::processTextHtmlSubtype( partNode * curNode, ProcessResult & ) {
961 TQCString cstr( curNode->msgPart().bodyDecoded() );
963 mRawReplyString = cstr;
964 if ( curNode->isFirstTextPart() ) {
965 mTextualContent += curNode->msgPart().bodyToUnicode();
966 mTextualContentCharset = curNode->msgPart().charset();
972 if ( curNode->isFirstTextPart() ||
973 attachmentStrategy()->defaultDisplay( curNode ) == AttachmentStrategy::Inline ||
974 showOnlyOneMimePart() )
976 if ( mReader->htmlMail() ) {
977 curNode->setDisplayedEmbedded(
true );
982 int i = cstr.findRev(
"</body>", -1,
false);
987 i = cstr.findRev(
"</html>", -1,
false);
988 if ( 0 <= i ) cstr.truncate(i);
997 if ( !mReader->htmlLoadExternal() &&
998 containsExternalReferences( cstr ) ) {
999 htmlWriter()->queue(
"<div class=\"htmlWarn\">\n" );
1000 htmlWriter()->queue( i18n(
"<b>Note:</b> This HTML message may contain external "
1001 "references to images etc. For security/privacy reasons "
1002 "external references are not loaded. If you trust the "
1003 "sender of this message then you can load the external "
1004 "references for this message "
1005 "<a href=\"kmail:loadExternal\">by clicking here</a>.") );
1006 htmlWriter()->queue(
"</div><br><br>" );
1009 htmlWriter()->queue(
"<div class=\"htmlWarn\">\n" );
1010 htmlWriter()->queue( i18n(
"<b>Note:</b> This is an HTML message. For "
1011 "security reasons, only the raw HTML code "
1012 "is shown. If you trust the sender of this "
1013 "message then you can activate formatted "
1014 "HTML display for this message "
1015 "<a href=\"kmail:showHTML\">by clicking here</a>.") );
1016 htmlWriter()->queue(
"</div><br><br>" );
1018 htmlWriter()->queue( codecFor( curNode )->toUnicode( mReader->htmlMail() ? cstr :
KMMessage::html2source( cstr )));
1019 mReader->mColorBar->setHtmlMode();
1026 static bool isMailmanMessage( partNode * curNode ) {
1027 if ( !curNode->dwPart() || !curNode->dwPart()->hasHeaders() )
1029 DwHeaders & headers = curNode->dwPart()->Headers();
1030 if ( headers.HasField(
"X-Mailman-Version") )
1032 if ( headers.HasField(
"X-Mailer") &&
1033 0 == TQCString( headers.FieldBody(
"X-Mailer").AsString().c_str() )
1034 .find(
"MAILMAN", 0,
false) )
1041 bool ObjectTreeParser::processMailmanMessage( partNode * curNode ) {
1042 const TQCString cstr = curNode->msgPart().bodyDecoded();
1045 const TQCString delim1(
"--__--__--\n\nMessage:");
1046 const TQCString delim2(
"--__--__--\r\n\r\nMessage:");
1047 const TQCString delimZ2(
"--__--__--\n\n_____________");
1048 const TQCString delimZ1(
"--__--__--\r\n\r\n_____________");
1049 TQCString partStr, digestHeaderStr;
1050 int thisDelim = cstr.find(delim1.data(), 0,
false);
1051 if ( thisDelim == -1 )
1052 thisDelim = cstr.find(delim2.data(), 0,
false);
1053 if ( thisDelim == -1 ) {
1054 kdDebug(5006) <<
" Sorry: Old style Mailman message but no delimiter found." << endl;
1058 int nextDelim = cstr.find(delim1.data(), thisDelim+1,
false);
1059 if ( -1 == nextDelim )
1060 nextDelim = cstr.find(delim2.data(), thisDelim+1,
false);
1061 if ( -1 == nextDelim )
1062 nextDelim = cstr.find(delimZ1.data(), thisDelim+1,
false);
1063 if ( -1 == nextDelim )
1064 nextDelim = cstr.find(delimZ2.data(), thisDelim+1,
false);
1073 digestHeaderStr =
"Content-Type=text/plain\nContent-Description=digest header\n\n";
1074 digestHeaderStr += cstr.mid( 0, thisDelim );
1075 insertAndParseNewChildNode( *curNode,
1077 "Digest Header",
true );
1081 curNode->setType( DwMime::kTypeMultipart );
1082 curNode->setSubType( DwMime::kSubtypeDigest );
1083 while( -1 < nextDelim ){
1084 int thisEoL = cstr.find(
"\nMessage:", thisDelim,
false);
1086 thisDelim = thisEoL+1;
1088 thisEoL = cstr.find(
"\n_____________", thisDelim,
false);
1090 thisDelim = thisEoL+1;
1092 thisEoL = cstr.find(
'\n', thisDelim);
1094 thisDelim = thisEoL+1;
1096 thisDelim = thisDelim+1;
1100 partStr =
"Content-Type=message/rfc822\nContent-Description=embedded message\n";
1101 partStr += cstr.mid( thisDelim, nextDelim-thisDelim );
1102 TQCString subject(
"embedded message");
1103 TQCString subSearch(
"\nSubject:");
1104 int subPos = partStr.find(subSearch.data(), 0,
false);
1106 subject = partStr.mid(subPos+subSearch.length());
1107 thisEoL = subject.find(
'\n');
1109 subject.truncate( thisEoL );
1112 insertAndParseNewChildNode( *curNode,
1116 thisDelim = nextDelim+1;
1117 nextDelim = cstr.find(delim1.data(), thisDelim,
false);
1118 if ( -1 == nextDelim )
1119 nextDelim = cstr.find(delim2.data(), thisDelim,
false);
1120 if ( -1 == nextDelim )
1121 nextDelim = cstr.find(delimZ1.data(), thisDelim,
false);
1122 if ( -1 == nextDelim )
1123 nextDelim = cstr.find(delimZ2.data(), thisDelim,
false);
1126 curNode->setType( DwMime::kTypeText );
1127 curNode->setSubType( DwMime::kSubtypePlain );
1128 int thisEoL = cstr.find(
"_____________", thisDelim);
1129 if ( -1 < thisEoL ){
1130 thisDelim = thisEoL;
1131 thisEoL = cstr.find(
'\n', thisDelim);
1133 thisDelim = thisEoL+1;
1136 thisDelim = thisDelim+1;
1137 partStr =
"Content-Type=text/plain\nContent-Description=digest footer\n\n";
1138 partStr += cstr.mid( thisDelim );
1139 insertAndParseNewChildNode( *curNode,
1141 "Digest Footer",
true );
1145 bool ObjectTreeParser::processTextPlainSubtype( partNode * curNode, ProcessResult & result ) {
1147 mRawReplyString = curNode->msgPart().bodyDecoded();
1148 if ( curNode->isFirstTextPart() ) {
1149 mTextualContent += curNode->msgPart().bodyToUnicode();
1150 mTextualContentCharset = curNode->msgPart().charset();
1155 if ( !curNode->isFirstTextPart() &&
1156 attachmentStrategy()->defaultDisplay( curNode ) != AttachmentStrategy::Inline &&
1157 !showOnlyOneMimePart() )
1160 mRawReplyString = curNode->msgPart().bodyDecoded();
1161 if ( curNode->isFirstTextPart() ) {
1162 mTextualContent += curNode->msgPart().bodyToUnicode();
1163 mTextualContentCharset = curNode->msgPart().charset();
1166 TQString label = curNode->msgPart().fileName().stripWhiteSpace();
1167 if ( label.isEmpty() )
1168 label = curNode->msgPart().name().stripWhiteSpace();
1170 const bool bDrawFrame = !curNode->isFirstTextPart()
1171 && !showOnlyOneMimePart()
1172 && !label.isEmpty();
1176 const TQString comment =
1179 const TQString fileName =
1180 mReader->writeMessagePartToTempFile( &curNode->msgPart(),
1181 curNode->nodeId() );
1183 const TQString dir = TQApplication::reverseLayout() ?
"rtl" :
"ltr" ;
1185 TQString htmlStr =
"<table cellspacing=\"1\" class=\"textAtm\">"
1186 "<tr class=\"textAtmH\"><td dir=\"" + dir +
"\">";
1187 if ( !fileName.isEmpty() )
1188 htmlStr +=
"<a href=\"" + curNode->asHREF(
"body" ) +
"\">"
1192 if ( !comment.isEmpty() )
1193 htmlStr +=
"<br>" + comment;
1194 htmlStr +=
"</td></tr><tr class=\"textAtmB\"><td>";
1196 htmlWriter()->queue( htmlStr );
1200 if ( !isMailmanMessage( curNode ) ||
1201 !processMailmanMessage( curNode ) ) {
1202 writeBodyString( mRawReplyString, curNode->trueFromAddress(),
1203 codecFor( curNode ), result, !bDrawFrame );
1204 curNode->setDisplayedEmbedded(
true );
1207 htmlWriter()->queue(
"</td></tr></table>" );
1212 void ObjectTreeParser::stdChildHandling( partNode * child ) {
1216 ObjectTreeParser otp( *
this );
1217 otp.setShowOnlyOneMimePart(
false );
1218 otp.parseObjectTree( child );
1219 mRawReplyString += otp.rawReplyString();
1220 mTextualContent += otp.textualContent();
1221 if ( !otp.textualContentCharset().isEmpty() )
1222 mTextualContentCharset = otp.textualContentCharset();
1225 TQString ObjectTreeParser::defaultToltecReplacementText()
1227 return i18n(
"This message is a <i>Toltec</i> Groupware object, it can only be viewed with "
1228 "Microsoft Outlook in combination with the Toltec connector." );
1231 bool ObjectTreeParser::processToltecMail( partNode *node )
1233 if ( !node || !mHtmlWriter || !GlobalSettings::self()->showToltecReplacementText() ||
1234 !node->isToltecMessage() || mShowRawToltecMail )
1237 htmlWriter()->queue( GlobalSettings::self()->toltecReplacementText() );
1238 htmlWriter()->queue(
"<br><br><a href=\"kmail:showRawToltecMail\">" +
1239 i18n(
"Show Raw Message" ) +
"</a>" );
1243 bool ObjectTreeParser::processMultiPartMixedSubtype( partNode * node, ProcessResult & ) {
1245 if ( processToltecMail( node ) ) {
1249 partNode * child = node->firstChild();
1254 stdChildHandling( child );
1258 bool ObjectTreeParser::processMultiPartAlternativeSubtype( partNode * node, ProcessResult & ) {
1259 partNode * child = node->firstChild();
1263 partNode * dataHtml = child->findType( DwMime::kTypeText,
1264 DwMime::kSubtypeHtml,
false,
true );
1265 partNode * dataPlain = child->findType( DwMime::kTypeText,
1266 DwMime::kSubtypePlain,
false,
true );
1268 if ( (mReader && mReader->htmlMail() && dataHtml) ||
1269 (dataHtml && dataPlain && dataPlain->msgPart().body().isEmpty()) ) {
1271 dataPlain->setProcessed(
true,
false );
1272 stdChildHandling( dataHtml );
1276 if ( !mReader || (!mReader->htmlMail() && dataPlain) ) {
1278 dataHtml->setProcessed(
true,
false );
1279 stdChildHandling( dataPlain );
1283 stdChildHandling( child );
1287 bool ObjectTreeParser::processMultiPartDigestSubtype( partNode * node, ProcessResult & result ) {
1288 return processMultiPartMixedSubtype( node, result );
1291 bool ObjectTreeParser::processMultiPartParallelSubtype( partNode * node, ProcessResult & result ) {
1292 return processMultiPartMixedSubtype( node, result );
1295 bool ObjectTreeParser::processMultiPartSignedSubtype( partNode * node, ProcessResult & ) {
1296 if ( node->childCount() != 2 ) {
1297 kdDebug(5006) <<
"mulitpart/signed must have exactly two child parts!" << endl
1298 <<
"processing as multipart/mixed" << endl;
1299 if ( node->firstChild() )
1300 stdChildHandling( node->firstChild() );
1301 return node->firstChild();
1304 partNode * signedData = node->firstChild();
1305 assert( signedData );
1307 partNode * signature = signedData->nextSibling();
1308 assert( signature );
1310 signature->setProcessed(
true,
true );
1312 if ( !includeSignatures() ) {
1313 stdChildHandling( signedData );
1321 const TQString contentType = node->contentTypeParameter(
"protocol" ).lower();
1322 const Kleo::CryptoBackend::Protocol *protocol = 0;
1323 if ( contentType ==
"application/pkcs7-signature" || contentType ==
"application/x-pkcs7-signature" )
1324 protocol = Kleo::CryptoBackendFactory::instance()->smime();
1325 else if ( contentType ==
"application/pgp-signature" || contentType ==
"application/x-pgp-signature" )
1326 protocol = Kleo::CryptoBackendFactory::instance()->openpgp();
1329 signature->setProcessed(
true,
true );
1330 stdChildHandling( signedData );
1334 CryptoProtocolSaver saver(
this, protocol );
1336 node->setSignatureState( KMMsgFullySigned );
1337 writeOpaqueOrMultipartSignedData( signedData, *signature,
1338 node->trueFromAddress() );
1342 bool ObjectTreeParser::processMultiPartEncryptedSubtype( partNode * node, ProcessResult & result ) {
1343 partNode * child = node->firstChild();
1347 if ( keepEncryptions() ) {
1348 node->setEncryptionState( KMMsgFullyEncrypted );
1349 const TQCString cstr = node->msgPart().bodyDecoded();
1351 writeBodyString( cstr, node->trueFromAddress(),
1352 codecFor( node ), result,
false );
1353 mRawReplyString += cstr;
1357 const Kleo::CryptoBackend::Protocol * useThisCryptProto = 0;
1362 partNode * data = child->findType( DwMime::kTypeApplication,
1363 DwMime::kSubtypeOctetStream,
false,
true );
1365 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->openpgp();
1368 data = child->findType( DwMime::kTypeApplication,
1369 DwMime::kSubtypePkcs7Mime,
false,
true );
1371 useThisCryptProto = Kleo::CryptoBackendFactory::instance()->smime();
1379 stdChildHandling( child );
1383 CryptoProtocolSaver cpws(
this, useThisCryptProto );
1385 if ( partNode * dataChild = data->firstChild() ) {
1387 stdChildHandling( dataChild );
1392 node->setEncryptionState( KMMsgFullyEncrypted );
1394 if ( mReader && !mReader->decryptMessage() ) {
1395 writeDeferredDecryptionBlock();
1396 data->setProcessed(
true,
false );
1401 PartMetaData messagePart;
1402 TQCString decryptedData;
1403 bool signatureFound;
1404 std::vector<GpgME::Signature> signatures;
1405 bool passphraseError;
1406 bool actuallyEncrypted =
true;
1407 bool decryptionStarted;
1409 bool bOkDecrypt = okDecryptMIME( *data,
1417 messagePart.errorText,
1418 messagePart.auditLogError,
1419 messagePart.auditLog );
1421 if ( decryptionStarted ) {
1422 writeDecryptionInProgressBlock();
1428 messagePart.isDecryptable = bOkDecrypt;
1429 messagePart.isEncrypted =
true;
1430 messagePart.isSigned =
false;
1431 htmlWriter()->queue( writeSigstatHeader( messagePart,
1433 node->trueFromAddress() ) );
1448 if ( signatureFound ) {
1449 writeOpaqueOrMultipartSignedData( 0,
1451 node->trueFromAddress(),
1456 node->setSignatureState( KMMsgFullySigned );
1458 insertAndParseNewChildNode( *node,
1463 mRawReplyString += decryptedData;
1467 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1472 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1473 data->setProcessed(
true,
false );
1478 bool ObjectTreeParser::processMessageRfc822Subtype( partNode * node, ProcessResult & ) {
1480 && !attachmentStrategy()->inlineNestedMessages()
1481 && !showOnlyOneMimePart() )
1484 if ( partNode * child = node->firstChild() ) {
1486 ObjectTreeParser otp( mReader, cryptoProtocol() );
1487 otp.parseObjectTree( child );
1488 mRawReplyString += otp.rawReplyString();
1489 mTextualContent += otp.textualContent();
1490 if ( !otp.textualContentCharset().isEmpty() )
1491 mTextualContentCharset = otp.textualContentCharset();
1497 PartMetaData messagePart;
1499 messagePart.isEncrypted =
false;
1500 messagePart.isSigned =
false;
1501 messagePart.isEncapsulatedRfc822Message =
true;
1503 mReader->writeMessagePartToTempFile( &node->msgPart(),
1505 htmlWriter()->queue( writeSigstatHeader( messagePart,
1507 node->trueFromAddress(),
1510 TQCString rfc822messageStr( node->msgPart().bodyDecoded() );
1512 DwMessage* rfc822DwMessage =
new DwMessage();
1513 rfc822DwMessage->FromString( rfc822messageStr );
1514 rfc822DwMessage->Parse();
1515 KMMessage rfc822message( rfc822DwMessage );
1516 node->setFromAddress( rfc822message.from() );
1519 htmlWriter()->queue( mReader->writeMsgHeader( &rfc822message ) );
1522 insertAndParseNewChildNode( *node,
1524 "encapsulated message",
false ,
1526 node->setDisplayedEmbedded(
true );
1528 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1533 bool ObjectTreeParser::processApplicationOctetStreamSubtype( partNode * node, ProcessResult & result ) {
1534 if ( partNode * child = node->firstChild() ) {
1536 ObjectTreeParser otp( mReader, cryptoProtocol() );
1537 otp.parseObjectTree( child );
1538 mRawReplyString += otp.rawReplyString();
1539 mTextualContent += otp.textualContent();
1540 if ( !otp.textualContentCharset().isEmpty() )
1541 mTextualContentCharset = otp.textualContentCharset();
1546 const Kleo::CryptoBackend::Protocol* oldUseThisCryptPlug = cryptoProtocol();
1547 if ( node->parentNode()
1548 && DwMime::kTypeMultipart == node->parentNode()->type()
1549 && DwMime::kSubtypeEncrypted == node->parentNode()->subType() ) {
1551 node->setEncryptionState( KMMsgFullyEncrypted );
1552 if ( keepEncryptions() ) {
1553 const TQCString cstr = node->msgPart().bodyDecoded();
1555 writeBodyString( cstr, node->trueFromAddress(),
1556 codecFor( node ), result,
false );
1557 mRawReplyString += cstr;
1558 }
else if ( mReader && !mReader->decryptMessage() ) {
1559 writeDeferredDecryptionBlock();
1564 PartMetaData messagePart;
1565 setCryptoProtocol( Kleo::CryptoBackendFactory::instance()->openpgp() );
1566 TQCString decryptedData;
1567 bool signatureFound;
1568 std::vector<GpgME::Signature> signatures;
1569 bool passphraseError;
1570 bool actuallyEncrypted =
true;
1571 bool decryptionStarted;
1573 bool bOkDecrypt = okDecryptMIME( *node,
1581 messagePart.errorText,
1582 messagePart.auditLogError,
1583 messagePart.auditLog );
1585 if ( decryptionStarted ) {
1586 writeDecryptionInProgressBlock();
1592 messagePart.isDecryptable = bOkDecrypt;
1593 messagePart.isEncrypted =
true;
1594 messagePart.isSigned =
false;
1595 htmlWriter()->queue( writeSigstatHeader( messagePart,
1597 node->trueFromAddress() ) );
1602 insertAndParseNewChildNode( *node,
1606 mRawReplyString += decryptedData;
1610 htmlWriter()->queue( TQString::fromUtf8( decryptedData.data() ) );
1615 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1619 setCryptoProtocol( oldUseThisCryptPlug );
1623 bool ObjectTreeParser::processApplicationPkcs7MimeSubtype( partNode * node, ProcessResult & result ) {
1624 if ( partNode * child = node->firstChild() ) {
1626 ObjectTreeParser otp( mReader, cryptoProtocol() );
1627 otp.parseObjectTree( child );
1628 mRawReplyString += otp.rawReplyString();
1629 mTextualContent += otp.textualContent();
1630 if ( !otp.textualContentCharset().isEmpty() )
1631 mTextualContentCharset = otp.textualContentCharset();
1637 if ( !node->dwPart() || !node->dwPart()->hasHeaders() )
1640 const Kleo::CryptoBackend::Protocol * smimeCrypto = Kleo::CryptoBackendFactory::instance()->smime();
1642 const TQString smimeType = node->contentTypeParameter(
"smime-type").lower();
1644 if ( smimeType ==
"certs-only" ) {
1645 result.setNeverDisplayInline(
true );
1646 if ( !smimeCrypto || !mReader )
1649 const TDEConfigGroup reader( KMKernel::config(),
"Reader" );
1650 if ( !reader.readBoolEntry(
"AutoImportKeys",
false ) )
1653 const TQByteArray certData = node->msgPart().bodyDecodedBinary();
1655 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::ImportJob>
import( smimeCrypto->importJob() );
1656 const GpgME::ImportResult res =
import->exec( certData );
1657 if ( res.error() ) {
1658 htmlWriter()->queue( i18n(
"Sorry, certificate could not be imported.<br>"
1659 "Reason: %1").arg( TQString::fromLocal8Bit( res.error().asString() ) ) );
1663 const int nImp = res.numImported();
1664 const int nUnc = res.numUnchanged();
1665 const int nSKImp = res.numSecretKeysImported();
1666 const int nSKUnc = res.numSecretKeysUnchanged();
1667 if ( !nImp && !nSKImp && !nUnc && !nSKUnc ) {
1668 htmlWriter()->queue( i18n(
"Sorry, no certificates were found in this message." ) );
1671 TQString comment =
"<b>" + i18n(
"Certificate import status:" ) +
"</b><br> <br>";
1673 comment += i18n(
"1 new certificate was imported.",
1674 "%n new certificates were imported.", nImp ) +
"<br>";
1676 comment += i18n(
"1 certificate was unchanged.",
1677 "%n certificates were unchanged.", nUnc ) +
"<br>";
1679 comment += i18n(
"1 new secret key was imported.",
1680 "%n new secret keys were imported.", nSKImp ) +
"<br>";
1682 comment += i18n(
"1 secret key was unchanged.",
1683 "%n secret keys were unchanged.", nSKUnc ) +
"<br>";
1684 comment +=
" <br>";
1685 htmlWriter()->queue( comment );
1686 if ( !nImp && !nSKImp ) {
1687 htmlWriter()->queue(
"<hr>" );
1690 const std::vector<GpgME::Import> imports = res.imports();
1691 if ( imports.empty() ) {
1692 htmlWriter()->queue( i18n(
"Sorry, no details on certificate import available." ) +
"<hr>" );
1695 htmlWriter()->queue(
"<b>" + i18n(
"Certificate import details:" ) +
"</b><br>" );
1696 for ( std::vector<GpgME::Import>::const_iterator it = imports.begin() ; it != imports.end() ; ++it ) {
1697 if ( (*it).error() )
1698 htmlWriter()->queue( i18n(
"Failed: %1 (%2)" )
1699 .arg( (*it).fingerprint(),
1700 TQString::fromLocal8Bit( (*it).error().asString() ) ) );
1701 else if ( (*it).status() & ~GpgME::Import::ContainedSecretKey ) {
1702 if ( (*it).status() & GpgME::Import::ContainedSecretKey ) {
1703 htmlWriter()->queue( i18n(
"New or changed: %1 (secret key available)" ).arg( (*it).fingerprint() ) );
1706 htmlWriter()->queue( i18n(
"New or changed: %1" ).arg( (*it).fingerprint() ) );
1709 htmlWriter()->queue(
"<br>" );
1712 htmlWriter()->queue(
"<hr>" );
1718 CryptoProtocolSaver cpws(
this, smimeCrypto );
1720 bool isSigned = smimeType ==
"signed-data";
1721 bool isEncrypted = smimeType ==
"enveloped-data";
1726 partNode* signTestNode = isEncrypted ? 0 : node;
1733 if ( isEncrypted ) {
1739 TQCString decryptedData;
1740 PartMetaData messagePart;
1741 messagePart.isEncrypted =
true;
1742 messagePart.isSigned =
false;
1743 bool signatureFound;
1744 std::vector<GpgME::Signature> signatures;
1745 bool passphraseError;
1746 bool actuallyEncrypted =
true;
1747 bool decryptionStarted;
1749 if ( mReader && !mReader->decryptMessage() ) {
1750 writeDeferredDecryptionBlock();
1754 const bool bOkDecrypt = okDecryptMIME( *node,
1762 messagePart.errorText,
1763 messagePart.auditLogError,
1764 messagePart.auditLog );
1765 if ( decryptionStarted ) {
1766 writeDecryptionInProgressBlock();
1772 node->setEncryptionState( KMMsgFullyEncrypted );
1775 messagePart.isDecryptable =
true;
1777 htmlWriter()->queue( writeSigstatHeader( messagePart,
1779 node->trueFromAddress() ) );
1780 insertAndParseNewChildNode( *node,
1784 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1790 if ( passphraseError || ( smimeType.isEmpty() && actuallyEncrypted ) ) {
1795 if ( isEncrypted ) {
1798 messagePart.isDecryptable =
false;
1800 htmlWriter()->queue( writeSigstatHeader( messagePart,
1802 node->trueFromAddress() ) );
1803 assert( mReader->decryptMessage() );
1804 writePartIcon( &node->msgPart(), node->nodeId() );
1805 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1813 node->setEncryptionState( KMMsgFullyEncrypted );
1817 if ( signTestNode ) {
1825 bool sigFound = writeOpaqueOrMultipartSignedData( 0,
1827 node->trueFromAddress(),
1830 std::vector<GpgME::Signature>(),
1837 signTestNode->setSignatureState( KMMsgFullySigned );
1838 if ( signTestNode != node )
1839 node->setSignatureState( KMMsgFullySigned );
1845 return isSigned || isEncrypted;
1848 bool ObjectTreeParser::decryptChiasmus(
const TQByteArray& data, TQByteArray& bodyDecoded, TQString& errorText )
1850 const Kleo::CryptoBackend::Protocol * chiasmus =
1851 Kleo::CryptoBackendFactory::instance()->protocol(
"Chiasmus" );
1852 Q_ASSERT( chiasmus );
1856 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> listjob( chiasmus->specialJob(
"x-obtain-keys", TQStringVariantMap() ) );
1858 errorText = i18n(
"Chiasmus backend does not offer the "
1859 "\"x-obtain-keys\" function. Please report this bug." );
1863 if ( listjob->exec() ) {
1864 errorText = i18n(
"Chiasmus Backend Error" );
1868 const TQVariant result = listjob->property(
"result" );
1869 if ( result.type() != TQVariant::StringList ) {
1870 errorText = i18n(
"Unexpected return value from Chiasmus backend: "
1871 "The \"x-obtain-keys\" function did not return a "
1872 "string list. Please report this bug." );
1876 const TQStringList keys = result.toStringList();
1877 if ( keys.empty() ) {
1878 errorText = i18n(
"No keys have been found. Please check that a "
1879 "valid key path has been set in the Chiasmus "
1884 emit mReader->noDrag();
1885 ChiasmusKeySelector selectorDlg( mReader, i18n(
"Chiasmus Decryption Key Selection" ),
1886 keys, GlobalSettings::chiasmusDecryptionKey(),
1887 GlobalSettings::chiasmusDecryptionOptions() );
1888 if ( selectorDlg.exec() != TQDialog::Accepted )
1891 GlobalSettings::setChiasmusDecryptionOptions( selectorDlg.options() );
1892 GlobalSettings::setChiasmusDecryptionKey( selectorDlg.key() );
1893 assert( !GlobalSettings::chiasmusDecryptionKey().isEmpty() );
1895 const STD_NAMESPACE_PREFIX unique_ptr<Kleo::SpecialJob> job( chiasmus->specialJob(
"x-decrypt", TQStringVariantMap() ) );
1897 errorText = i18n(
"Chiasmus backend does not offer the "
1898 "\"x-decrypt\" function. Please report this bug." );
1902 if ( !job->setProperty(
"key", GlobalSettings::chiasmusDecryptionKey() ) ||
1903 !job->setProperty(
"options", GlobalSettings::chiasmusDecryptionOptions() ) ||
1904 !job->setProperty(
"input", data ) ) {
1905 errorText = i18n(
"The \"x-decrypt\" function does not accept "
1906 "the expected parameters. Please report this bug." );
1910 if ( job->exec() ) {
1911 errorText = i18n(
"Chiasmus Decryption Error" );
1915 const TQVariant resultData = job->property(
"result" );
1916 if ( resultData.type() != TQVariant::ByteArray ) {
1917 errorText = i18n(
"Unexpected return value from Chiasmus backend: "
1918 "The \"x-decrypt\" function did not return a "
1919 "byte array. Please report this bug." );
1922 bodyDecoded = resultData.toByteArray();
1926 bool ObjectTreeParser::processApplicationChiasmusTextSubtype( partNode * curNode, ProcessResult & result )
1929 mRawReplyString = curNode->msgPart().bodyDecoded();
1930 mTextualContent += curNode->msgPart().bodyToUnicode();
1931 mTextualContentCharset = curNode->msgPart().charset();
1935 TQByteArray decryptedBody;
1937 const TQByteArray data = curNode->msgPart().bodyDecodedBinary();
1938 bool bOkDecrypt = decryptChiasmus( data, decryptedBody, errorText );
1939 PartMetaData messagePart;
1940 messagePart.isDecryptable = bOkDecrypt;
1941 messagePart.isEncrypted =
true;
1942 messagePart.isSigned =
false;
1943 messagePart.errorText = errorText;
1945 htmlWriter()->queue( writeSigstatHeader( messagePart,
1947 curNode->trueFromAddress() ) );
1948 const TQByteArray body = bOkDecrypt ? decryptedBody : data;
1949 const TQString chiasmusCharset = curNode->contentTypeParameter(
"chiasmus-charset");
1950 const TQTextCodec* aCodec = chiasmusCharset.isEmpty()
1951 ? codecFor( curNode )
1952 : KMMsgBase::codecForName( chiasmusCharset.ascii() );
1953 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( body ),
false ) );
1954 result.setInlineEncryptionState( KMMsgFullyEncrypted );
1956 htmlWriter()->queue( writeSigstatFooter( messagePart ) );
1960 bool ObjectTreeParser::processApplicationMsTnefSubtype( partNode *node, ProcessResult &result )
1966 const TQString fileName = mReader->writeMessagePartToTempFile( &node->msgPart(), node->nodeId() );
1968 if ( !parser.openFile( fileName ) || !parser.message()) {
1969 kdDebug() << k_funcinfo <<
"Could not parse " << fileName << endl;
1973 TQPtrList<KTNEFAttach> tnefatts = parser.message()->attachmentList();
1974 if ( tnefatts.isEmpty() ) {
1975 kdDebug() << k_funcinfo <<
"No attachments found in " << fileName << endl;
1979 if ( !showOnlyOneMimePart() ) {
1980 TQString label = node->msgPart().fileName().stripWhiteSpace();
1981 if ( label.isEmpty() )
1982 label = node->msgPart().name().stripWhiteSpace();
1985 const TQString dir = TQApplication::reverseLayout() ?
"rtl" :
"ltr" ;
1987 TQString htmlStr =
"<table cellspacing=\"1\" class=\"textAtm\">"
1988 "<tr class=\"textAtmH\"><td dir=\"" + dir +
"\">";
1989 if ( !fileName.isEmpty() )
1990 htmlStr +=
"<a href=\"" + node->asHREF(
"body" ) +
"\">"
1994 if ( !comment.isEmpty() )
1995 htmlStr +=
"<br>" + comment;
1996 htmlStr +=
"</td></tr><tr class=\"textAtmB\"><td>";
1997 htmlWriter()->queue( htmlStr );
2000 for ( uint i = 0; i < tnefatts.count(); ++i ) {
2001 KTNEFAttach *att = tnefatts.at( i );
2002 TQString label = att->displayName();
2003 if( label.isEmpty() )
2004 label = att->name();
2007 TQString dir = mReader->createTempDir(
"ktnef-" + TQString::number( i ) );
2008 parser.extractFileTo( att->name(), dir );
2009 mReader->mTempFiles.append( dir + TQDir::separator() + att->name() );
2010 TQString href =
"file:" + KURL::encode_string( dir + TQDir::separator() + att->name() );
2012 KMimeType::Ptr mimeType = KMimeType::mimeType( att->mimeTag() );
2013 TQString iconName = TDEGlobal::instance()->iconLoader()->iconPath( mimeType->icon( TQString(),
false ), TDEIcon::Desktop );
2015 htmlWriter()->queue(
"<div><a href=\"" + href +
"\"><img src=\"" +
2016 iconName +
"\" border=\"0\" style=\"max-width: 100%\">" + label +
2020 if ( !showOnlyOneMimePart() )
2021 htmlWriter()->queue(
"</td></tr></table>" );
2026 void ObjectTreeParser::writeBodyString(
const TQCString & bodyString,
2027 const TQString & fromAddress,
2028 const TQTextCodec * codec,
2029 ProcessResult & result,
2031 assert( mReader ); assert( codec );
2032 KMMsgSignatureState inlineSignatureState = result.inlineSignatureState();
2033 KMMsgEncryptionState inlineEncryptionState = result.inlineEncryptionState();
2034 writeBodyStr( bodyString, codec, fromAddress,
2035 inlineSignatureState, inlineEncryptionState, decorate );
2036 result.setInlineSignatureState( inlineSignatureState );
2037 result.setInlineEncryptionState( inlineEncryptionState );
2040 void ObjectTreeParser::writePartIcon( KMMessagePart * msgPart,
int partNum,
bool inlineImage ) {
2041 if ( !mReader || !msgPart )
2044 TQString label = msgPart->fileName();
2045 if( label.isEmpty() )
2046 label = msgPart->name();
2047 if( label.isEmpty() )
2051 TQString comment = msgPart->contentDescription();
2053 if ( label == comment ) comment = TQString();
2055 TQString fileName = mReader->writeMessagePartToTempFile( msgPart, partNum );
2057 TQString href = TQString(
"attachment:%1?place=body" ).arg( partNum );
2063 iconName = msgPart->iconName();
2064 if( iconName.right( 14 ) ==
"mime_empty.png" ) {
2065 msgPart->magicSetType();
2066 iconName = msgPart->iconName();
2070 TQCString contentId = msgPart->contentId();
2071 if ( !contentId.isEmpty() ) {
2072 htmlWriter()->embedPart( contentId, href );
2077 htmlWriter()->queue(
"<div><a href=\"" + href +
"\">"
2078 "<img src=\"" + fileName +
"\" border=\"0\" style=\"max-width: 100%\"></a>"
2080 "<div><a href=\"" + href +
"\">" + label +
"</a>"
2082 "<div>" + comment +
"</div><br>" );
2085 htmlWriter()->queue(
"<div><a href=\"" + href +
"\"><img src=\"" +
2086 iconName +
"\" border=\"0\" style=\"max-width: 100%\">" + label +
2088 "<div>" + comment +
"</div><br>" );
2091 #define SIG_FRAME_COL_UNDEF 99
2092 #define SIG_FRAME_COL_RED -1
2093 #define SIG_FRAME_COL_YELLOW 0
2094 #define SIG_FRAME_COL_GREEN 1
2095 TQString ObjectTreeParser::sigStatusToString(
const Kleo::CryptoBackend::Protocol* cryptProto,
2097 GpgME::Signature::Summary summary,
2099 bool& showKeyInfos )
2104 showKeyInfos =
true;
2107 if( cryptProto == Kleo::CryptoBackendFactory::instance()->openpgp() ) {
2110 switch( status_code ) {
2112 result = i18n(
"Error: Signature not verified");
2115 result = i18n(
"Good signature");
2118 result = i18n(
"<b>Bad</b> signature");
2121 result = i18n(
"No public key to verify the signature");
2124 result = i18n(
"No signature found");
2127 result = i18n(
"Error verifying the signature");
2130 result = i18n(
"Different results for signatures");
2145 else if ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() ) {
2149 if( summary == GpgME::Signature::None ) {
2150 result = i18n(
"No status information available.");
2151 frameColor = SIG_FRAME_COL_YELLOW;
2152 showKeyInfos =
false;
2156 if( summary & GpgME::Signature::Valid ) {
2157 result = i18n(
"Good signature.");
2166 frameColor = SIG_FRAME_COL_GREEN;
2167 showKeyInfos =
false;
2174 frameColor = SIG_FRAME_COL_GREEN;
2176 if( summary & GpgME::Signature::KeyExpired ){
2178 result2 += i18n(
"One key has expired.");
2180 if( summary & GpgME::Signature::SigExpired ){
2182 result2 += i18n(
"The signature has expired.");
2186 if( summary & GpgME::Signature::KeyMissing ) {
2187 result2 += i18n(
"Unable to verify: key missing.");
2190 showKeyInfos =
false;
2191 frameColor = SIG_FRAME_COL_YELLOW;
2193 if( summary & GpgME::Signature::CrlMissing ){
2194 result2 += i18n(
"CRL not available.");
2195 frameColor = SIG_FRAME_COL_YELLOW;
2197 if( summary & GpgME::Signature::CrlTooOld ){
2198 result2 += i18n(
"Available CRL is too old.");
2199 frameColor = SIG_FRAME_COL_YELLOW;
2201 if( summary & GpgME::Signature::BadPolicy ){
2202 result2 += i18n(
"A policy was not met.");
2203 frameColor = SIG_FRAME_COL_YELLOW;
2205 if( summary & GpgME::Signature::SysError ){
2206 result2 += i18n(
"A system error occurred.");
2210 showKeyInfos =
false;
2211 frameColor = SIG_FRAME_COL_YELLOW;
2215 if( summary & GpgME::Signature::KeyRevoked ){
2217 result2 += i18n(
"One key has been revoked.");
2218 frameColor = SIG_FRAME_COL_RED;
2220 if( summary & GpgME::Signature::Red ) {
2221 if( result2.isEmpty() )
2234 showKeyInfos =
false;
2235 frameColor = SIG_FRAME_COL_RED;
2240 if( SIG_FRAME_COL_GREEN == frameColor ) {
2241 result = i18n(
"Good signature.");
2242 }
else if( SIG_FRAME_COL_RED == frameColor ) {
2243 result = i18n(
"<b>Bad</b> signature.");
2247 if( !result2.isEmpty() ) {
2248 if( !result.isEmpty() )
2249 result.append(
"<br />");
2250 result.append( result2 );
2264 static TQString writeSimpleSigstatHeader(
const PartMetaData &block )
2267 html +=
"<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td>";
2269 if ( block.signClass ==
"signErr" ) {
2270 html += i18n(
"Invalid signature." );
2271 }
else if ( block.signClass ==
"signOkKeyBad" || block.signClass ==
"signWarn" ) {
2272 html += i18n(
"Not enough information to check signature validity." );
2273 }
else if ( block.signClass ==
"signOkKeyOk" ) {
2275 if ( !block.signerMailAddresses.isEmpty() )
2276 addr = block.signerMailAddresses.first();
2277 TQString name = addr;
2278 if ( name.isEmpty() )
2279 name = block.signer;
2280 if ( addr.isEmpty() ) {
2281 html += i18n(
"Signature is valid." );
2283 html += i18n(
"Signed by <a href=\"mailto:%1\">%2</a>." ).arg( addr, name );
2287 html += i18n(
"Unknown signature state" );
2289 html +=
"</td><td align=\"right\">";
2290 html +=
"<a href=\"kmail:showSignatureDetails\">";
2291 html += i18n(
"Show Details" );
2292 html +=
"</a></td></tr></table>";
2296 static TQString beginVerboseSigstatHeader()
2298 return "<table cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td rowspan=\"2\">";
2301 static TQString makeShowAuditLogLink(
const GpgME::Error & err,
const TQString & auditLog ) {
2302 if (
const unsigned int code = err.code() ) {
2303 if ( code == GPG_ERR_NOT_IMPLEMENTED ) {
2306 }
else if ( code == GPG_ERR_NO_DATA ) {
2308 return i18n(
"No Audit Log available");
2310 return i18n(
"Error Retrieving Audit Log: %1").arg( TQString::fromLocal8Bit( err.asString() ) );
2314 if ( !auditLog.isEmpty() ) {
2316 url.setProtocol(
"kmail" );
2317 url.setPath(
"showAuditLog" );
2318 url.addQueryItem(
"log", auditLog );
2320 return "<a href=\"" + url.htmlURL() +
"\">" + i18n(
"The Audit Log is a detailed error log from the gnupg backend",
"Show Audit Log") +
"</a>";
2326 static TQString endVerboseSigstatHeader(
const PartMetaData & pmd )
2329 html +=
"</td><td align=\"right\" valign=\"top\" nowrap=\"nowrap\">";
2330 html +=
"<a href=\"kmail:hideSignatureDetails\">";
2331 html += i18n(
"Hide Details" );
2332 html +=
"</a></td></tr>";
2333 html +=
"<tr><td align=\"right\" valign=\"bottom\" nowrap=\"nowrap\">";
2334 html += makeShowAuditLogLink( pmd.auditLogError, pmd.auditLog );
2335 html +=
"</td></tr></table>";
2339 TQString ObjectTreeParser::writeSigstatHeader( PartMetaData & block,
2340 const Kleo::CryptoBackend::Protocol * cryptProto,
2341 const TQString & fromAddress,
2344 const bool isSMIME = cryptProto && ( cryptProto == Kleo::CryptoBackendFactory::instance()->smime() );
2345 TQString signer = block.signer;
2347 TQString htmlStr, simpleHtmlStr;
2348 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2349 TQString cellPadding(
"cellpadding=\"1\"");
2351 if( block.isEncapsulatedRfc822Message )
2353 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"rfc822\">"
2354 "<tr class=\"rfc822H\"><td dir=\"" + dir +
"\">";
2356 htmlStr +=
"<a href=\"" + node->asHREF(
"body" ) +
"\">"
2357 + i18n(
"Encapsulated message") +
"</a>";
2359 htmlStr += i18n(
"Encapsulated message");
2360 htmlStr +=
"</td></tr><tr class=\"rfc822B\"><td>";
2363 if( block.isEncrypted )
2365 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"encr\">"
2366 "<tr class=\"encrH\"><td dir=\"" + dir +
"\">";
2367 if ( block.inProgress )
2368 htmlStr += i18n(
"Please wait while the message is being decrypted...");
2369 else if ( block.isDecryptable )
2370 htmlStr += i18n(
"Encrypted message");
2372 htmlStr += i18n(
"Encrypted message (decryption not possible)");
2373 if( !block.errorText.isEmpty() )
2374 htmlStr +=
"<br />" + i18n(
"Reason: %1").arg( block.errorText );
2376 htmlStr +=
"</td></tr><tr class=\"encrB\"><td>";
2379 if ( block.isSigned && block.inProgress )
2381 block.signClass =
"signInProgress";
2382 htmlStr +=
"<table cellspacing=\"1\" "+cellPadding+
" class=\"signInProgress\">"
2383 "<tr class=\"signInProgressH\"><td dir=\"" + dir +
"\">";
2384 htmlStr += i18n(
"Please wait while the signature is being verified...");
2385 htmlStr +=
"</td></tr><tr class=\"signInProgressB\"><td>";
2387 simpleHtmlStr = htmlStr;
2389 if ( block.isSigned && !block.inProgress ) {
2390 TQStringList& blockAddrs( block.signerMailAddresses );
2394 int frameColor = SIG_FRAME_COL_UNDEF;
2396 bool onlyShowKeyURL =
false;
2397 bool cannotCheckSignature =
true;
2398 TQString statusStr = sigStatusToString( cryptProto,
2405 if( statusStr.isEmpty() )
2406 statusStr = block.status;
2407 if( block.technicalProblem )
2408 frameColor = SIG_FRAME_COL_YELLOW;
2410 switch( frameColor ){
2411 case SIG_FRAME_COL_RED:
2412 cannotCheckSignature =
false;
2414 case SIG_FRAME_COL_YELLOW:
2415 cannotCheckSignature =
true;
2417 case SIG_FRAME_COL_GREEN:
2418 cannotCheckSignature =
false;
2427 TQString startKeyHREF;
2430 TQString(
"<a href=\"kmail:showCertificate#%1 ### %2 ### %3\">")
2431 .arg( cryptProto->displayName(),
2434 TQString keyWithWithoutURL
2436 ? TQString(
"%1%2</a>")
2438 cannotCheckSignature ? i18n(
"[Details]") : (
"0x" + block.keyId) )
2439 :
"0x" + TQString::fromUtf8( block.keyId );
2443 showKeyInfos =
true;
2448 if( isSMIME && (SIG_FRAME_COL_UNDEF != frameColor) ) {
2452 if( !statusStr.isEmpty() ) {
2453 statusStr.prepend(
"<i>");
2454 statusStr.append(
"</i>");
2458 switch( frameColor ) {
2459 case SIG_FRAME_COL_RED:
2460 block.signClass =
"signErr";
2461 onlyShowKeyURL =
true;
2463 case SIG_FRAME_COL_YELLOW:
2464 if( block.technicalProblem )
2465 block.signClass =
"signWarn";
2467 block.signClass =
"signOkKeyBad";
2469 case SIG_FRAME_COL_GREEN:
2470 block.signClass =
"signOkKeyOk";
2473 TQString greenCaseWarning;
2474 TQString msgFrom( KPIM::getEmailAddress(fromAddress) );
2475 TQString certificate;
2476 if( block.keyId.isEmpty() )
2477 certificate = i18n(
"certificate");
2479 certificate = startKeyHREF + i18n(
"certificate") +
"</a>";
2480 if( !blockAddrs.empty() ){
2481 if( blockAddrs.grep(
2483 false ).isEmpty() ) {
2488 i18n(
"Sender's mail address is not stored "
2489 "in the %1 used for signing.").arg(certificate) +
2500 for(TQStringList::ConstIterator it = blockAddrs.begin();
2501 it != blockAddrs.end(); ++it ){
2503 greenCaseWarning.append(
", <br /> ");
2505 greenCaseWarning.append( KPIM::getEmailAddress(*it) );
2513 i18n(
"No mail address is stored in the %1 used for signing, "
2514 "so we cannot compare it to the sender's address %2.")
2515 .arg(certificate,msgFrom);
2517 if( !greenCaseWarning.isEmpty() ) {
2518 if( !statusStr.isEmpty() )
2519 statusStr.append(
"<br /> <br />");
2520 statusStr.append( greenCaseWarning );
2525 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2526 "class=\"" + block.signClass +
"\">"
2527 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2528 htmlStr += frame + beginVerboseSigstatHeader();
2529 simpleHtmlStr += frame;
2530 simpleHtmlStr += writeSimpleSigstatHeader( block );
2531 if( block.technicalProblem ) {
2532 htmlStr += block.errorText;
2534 else if( showKeyInfos ) {
2535 if( cannotCheckSignature ) {
2536 htmlStr += i18n(
"Not enough information to check "
2538 .arg( keyWithWithoutURL );
2542 if (block.signer.isEmpty())
2545 if( !blockAddrs.empty() ){
2547 signer =
"<a href=\"mailto:" + address +
"\">" + signer +
"</a>";
2551 if( block.keyId.isEmpty() ) {
2552 if( signer.isEmpty() || onlyShowKeyURL )
2553 htmlStr += i18n(
"Message was signed with unknown key." );
2555 htmlStr += i18n(
"Message was signed by %1." )
2558 TQDateTime created = block.creationTime;
2559 if( created.isValid() ) {
2560 if( signer.isEmpty() ) {
2561 if( onlyShowKeyURL )
2562 htmlStr += i18n(
"Message was signed with key %1." )
2563 .arg( keyWithWithoutURL );
2565 htmlStr += i18n(
"Message was signed on %1 with key %2." )
2566 .arg( TDEGlobal::locale()->formatDateTime( created ),
2567 keyWithWithoutURL );
2570 if( onlyShowKeyURL )
2571 htmlStr += i18n(
"Message was signed with key %1." )
2572 .arg( keyWithWithoutURL );
2574 htmlStr += i18n(
"Message was signed by %3 on %1 with key %2" )
2575 .arg( TDEGlobal::locale()->formatDateTime( created ),
2581 if( signer.isEmpty() || onlyShowKeyURL )
2582 htmlStr += i18n(
"Message was signed with key %1." )
2583 .arg( keyWithWithoutURL );
2585 htmlStr += i18n(
"Message was signed by %2 with key %1." )
2586 .arg( keyWithWithoutURL,
2591 htmlStr +=
"<br />";
2592 if( !statusStr.isEmpty() ) {
2593 htmlStr +=
" <br />";
2594 htmlStr += i18n(
"Status: " );
2595 htmlStr += statusStr;
2598 htmlStr += statusStr;
2600 frame =
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
2601 htmlStr += endVerboseSigstatHeader( block ) + frame;
2602 simpleHtmlStr += frame;
2608 if( block.signer.isEmpty() || block.technicalProblem ) {
2609 block.signClass =
"signWarn";
2610 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2611 "class=\"" + block.signClass +
"\">"
2612 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2613 htmlStr += frame + beginVerboseSigstatHeader();
2614 simpleHtmlStr += frame;
2615 simpleHtmlStr += writeSimpleSigstatHeader( block );
2616 if( block.technicalProblem ) {
2617 htmlStr += block.errorText;
2620 if( !block.keyId.isEmpty() ) {
2621 TQDateTime created = block.creationTime;
2622 if ( created.isValid() )
2623 htmlStr += i18n(
"Message was signed on %1 with unknown key %2." )
2624 .arg( TDEGlobal::locale()->formatDateTime( created ),
2625 keyWithWithoutURL );
2627 htmlStr += i18n(
"Message was signed with unknown key %1." )
2628 .arg( keyWithWithoutURL );
2631 htmlStr += i18n(
"Message was signed with unknown key." );
2632 htmlStr +=
"<br />";
2633 htmlStr += i18n(
"The validity of the signature cannot be "
2635 if( !statusStr.isEmpty() ) {
2636 htmlStr +=
"<br />";
2637 htmlStr += i18n(
"Status: " );
2639 htmlStr += statusStr;
2643 frame =
"</td></tr><tr class=\"" + block.signClass +
"B\"><td>";
2644 htmlStr += endVerboseSigstatHeader( block ) + frame;
2645 simpleHtmlStr += frame;
2651 signer =
"<a href=\"mailto:" + signer +
"\">" + signer +
"</a>";
2653 if (block.isGoodSignature) {
2654 if( block.keyTrust < Kpgp::KPGP_VALIDITY_MARGINAL )
2655 block.signClass =
"signOkKeyBad";
2657 block.signClass =
"signOkKeyOk";
2658 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2659 "class=\"" + block.signClass +
"\">"
2660 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2661 htmlStr += frame + beginVerboseSigstatHeader();
2662 simpleHtmlStr += frame;
2663 simpleHtmlStr += writeSimpleSigstatHeader( block );
2664 if( !block.keyId.isEmpty() )
2665 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
2666 .arg( keyWithWithoutURL,
2669 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
2670 htmlStr +=
"<br />";
2672 switch( block.keyTrust )
2674 case Kpgp::KPGP_VALIDITY_UNKNOWN:
2675 htmlStr += i18n(
"The signature is valid, but the key's "
2676 "validity is unknown." );
2678 case Kpgp::KPGP_VALIDITY_MARGINAL:
2679 htmlStr += i18n(
"The signature is valid and the key is "
2680 "marginally trusted." );
2682 case Kpgp::KPGP_VALIDITY_FULL:
2683 htmlStr += i18n(
"The signature is valid and the key is "
2686 case Kpgp::KPGP_VALIDITY_ULTIMATE:
2687 htmlStr += i18n(
"The signature is valid and the key is "
2688 "ultimately trusted." );
2691 htmlStr += i18n(
"The signature is valid, but the key is "
2694 frame =
"</td></tr>"
2695 "<tr class=\"" + block.signClass +
"B\"><td>";
2696 htmlStr += endVerboseSigstatHeader( block ) + frame;
2697 simpleHtmlStr += frame;
2701 block.signClass =
"signErr";
2702 TQString frame =
"<table cellspacing=\"1\" "+cellPadding+
" "
2703 "class=\"" + block.signClass +
"\">"
2704 "<tr class=\"" + block.signClass +
"H\"><td dir=\"" + dir +
"\">";
2705 htmlStr += frame + beginVerboseSigstatHeader();
2706 simpleHtmlStr += frame;
2707 simpleHtmlStr += writeSimpleSigstatHeader( block );
2708 if( !block.keyId.isEmpty() )
2709 htmlStr += i18n(
"Message was signed by %2 (Key ID: %1)." )
2710 .arg( keyWithWithoutURL,
2713 htmlStr += i18n(
"Message was signed by %1." ).arg( signer );
2714 htmlStr +=
"<br />";
2715 htmlStr += i18n(
"Warning: The signature is bad.");
2716 frame =
"</td></tr>"
2717 "<tr class=\"" + block.signClass +
"B\"><td>";
2718 htmlStr += endVerboseSigstatHeader( block ) + frame;
2719 simpleHtmlStr += frame;
2725 if ( mReader->showSignatureDetails() )
2727 return simpleHtmlStr;
2730 TQString ObjectTreeParser::writeSigstatFooter( PartMetaData& block )
2732 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2736 if (block.isSigned) {
2737 htmlStr +=
"</td></tr><tr class=\"" + block.signClass +
"H\">";
2738 htmlStr +=
"<td dir=\"" + dir +
"\">" +
2739 i18n(
"End of signed message" ) +
2740 "</td></tr></table>";
2743 if (block.isEncrypted) {
2744 htmlStr +=
"</td></tr><tr class=\"encrH\"><td dir=\"" + dir +
"\">" +
2745 i18n(
"End of encrypted message" ) +
2746 "</td></tr></table>";
2749 if( block.isEncapsulatedRfc822Message )
2751 htmlStr +=
"</td></tr><tr class=\"rfc822H\"><td dir=\"" + dir +
"\">" +
2752 i18n(
"End of encapsulated message" ) +
2753 "</td></tr></table>";
2761 void ObjectTreeParser::writeAttachmentMarkHeader( partNode *node )
2766 htmlWriter()->queue( TQString(
"<div id=\"attachmentDiv%1\">\n" ).arg( node->nodeId() ) );
2771 void ObjectTreeParser::writeAttachmentMarkFooter()
2776 htmlWriter()->queue( TQString(
"</div>" ) );
2780 void ObjectTreeParser::writeBodyStr(
const TQCString& aStr,
const TQTextCodec *aCodec,
2781 const TQString& fromAddress )
2783 KMMsgSignatureState dummy1;
2784 KMMsgEncryptionState dummy2;
2785 writeBodyStr( aStr, aCodec, fromAddress, dummy1, dummy2,
false );
2789 void ObjectTreeParser::writeBodyStr(
const TQCString& aStr,
const TQTextCodec *aCodec,
2790 const TQString& fromAddress,
2791 KMMsgSignatureState& inlineSignatureState,
2792 KMMsgEncryptionState& inlineEncryptionState,
2795 bool goodSignature =
false;
2796 Kpgp::Module* pgp = Kpgp::Module::getKpgp();
2798 bool isPgpMessage =
false;
2800 TQString dir = ( TQApplication::reverseLayout() ?
"rtl" :
"ltr" );
2801 TQString headerStr = TQString(
"<div dir=\"%1\">").arg(dir);
2803 inlineSignatureState = KMMsgNotSigned;
2804 inlineEncryptionState = KMMsgNotEncrypted;
2805 TQPtrList<Kpgp::Block> pgpBlocks;
2806 TQStrList nonPgpBlocks;
2807 if( Kpgp::Module::prepareMessageForDecryption( aStr, pgpBlocks, nonPgpBlocks ) )
2809 bool isEncrypted =
false, isSigned =
false;
2810 bool fullySignedOrEncrypted =
true;
2811 bool firstNonPgpBlock =
true;
2812 bool couldDecrypt =
false;
2815 TQString decryptionError;
2816 Kpgp::Validity keyTrust = Kpgp::KPGP_VALIDITY_FULL;
2818 TQPtrListIterator<Kpgp::Block> pbit( pgpBlocks );
2820 TQStrListIterator npbit( nonPgpBlocks );
2823 for( ; *pbit != 0; ++pbit, ++npbit )
2826 TQCString str( *npbit );
2827 if( !str.isEmpty() ) {
2828 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2833 if( firstNonPgpBlock ) {
2835 for( TQCString::ConstIterator c = str.begin(); *c; ++c ) {
2837 fullySignedOrEncrypted =
false;
2843 fullySignedOrEncrypted =
false;
2846 firstNonPgpBlock =
false;
2850 Kpgp::Block* block = *pbit;
2851 if( ( block->type() == Kpgp::PgpMessageBlock &&
2853 !kmkernel->contextMenuShown() ) ||
2854 ( block->type() == Kpgp::ClearsignedBlock ) )
2856 isPgpMessage =
true;
2857 if( block->type() == Kpgp::PgpMessageBlock )
2860 emit mReader->noDrag();
2862 couldDecrypt = block->decrypt();
2863 isEncrypted = block->isEncrypted();
2864 if (!couldDecrypt) {
2865 decryptionError = pgp->lastErrorMsg();
2874 isSigned = block->isSigned();
2877 keyId = block->signatureKeyId();
2878 signer = block->signatureUserId();
2879 if( !signer.isEmpty() )
2881 goodSignature = block->goodSignature();
2883 if( !keyId.isEmpty() ) {
2884 keyTrust = pgp->keyTrust( keyId );
2885 Kpgp::Key* key = pgp->publicKey( keyId );
2889 signer = key->primaryUserID();
2895 keyTrust = pgp->keyTrust( signer );
2900 inlineSignatureState = KMMsgPartiallySigned;
2902 inlineEncryptionState = KMMsgPartiallyEncrypted;
2904 PartMetaData messagePart;
2906 messagePart.isSigned = isSigned;
2907 messagePart.technicalProblem =
false;
2908 messagePart.isGoodSignature = goodSignature;
2909 messagePart.isEncrypted = isEncrypted;
2910 messagePart.isDecryptable = couldDecrypt;
2911 messagePart.decryptionError = decryptionError;
2912 messagePart.signer = signer;
2913 messagePart.keyId = keyId;
2914 messagePart.keyTrust = keyTrust;
2916 htmlStr += writeSigstatHeader( messagePart, 0, fromAddress );
2918 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ), decorate );
2919 htmlStr += writeSigstatFooter( messagePart );
2922 htmlStr += quotedHTML( aCodec->toUnicode( block->text() ),
2927 TQCString str( nonPgpBlocks.last() );
2928 if( !str.isEmpty() ) {
2929 htmlStr += quotedHTML( aCodec->toUnicode( str ), decorate );
2936 if( fullySignedOrEncrypted ) {
2937 if( inlineSignatureState == KMMsgPartiallySigned )
2938 inlineSignatureState = KMMsgFullySigned;
2939 if( inlineEncryptionState == KMMsgPartiallyEncrypted )
2940 inlineEncryptionState = KMMsgFullyEncrypted;
2942 htmlWriter()->queue( htmlStr );
2945 htmlWriter()->queue( quotedHTML( aCodec->toUnicode( aStr ), decorate ) );
2949 TQString ObjectTreeParser::quotedHTML(
const TQString& s,
bool decorate )
2952 assert( cssHelper() );
2954 int convertFlags = LinkLocator::PreserveSpaces;
2955 if ( decorate && GlobalSettings::self()->showEmoticons() ) {
2956 convertFlags |= LinkLocator::ReplaceSmileys;
2959 const TQString normalStartTag = cssHelper()->nonQuotedFontTag();
2960 TQString quoteFontTag[3];
2961 TQString deepQuoteFontTag[3];
2962 for (
int i = 0 ; i < 3 ; ++i ) {
2963 quoteFontTag[i] = cssHelper()->quoteFontTag( i );
2964 deepQuoteFontTag[i] = cssHelper()->quoteFontTag( i+3 );
2966 const TQString normalEndTag =
"</div>";
2967 const TQString quoteEnd =
"</div>";
2969 unsigned int pos, beg;
2970 const unsigned int length = s.length();
2973 for ( pos = 0; pos < length && s.at(pos) <= TQChar(
' '); pos++ ) { ; }
2974 while (pos > 0 && (s[pos-1] ==
' ' || s[pos-1] ==
'\t')) pos--;
2977 int currQuoteLevel = -2;
2978 bool curHidden =
false;
2985 pos = s.find(
'\n', beg, FALSE);
2986 if (pos == (
unsigned int)(-1))
2989 line = s.mid(beg,pos-beg);
2993 int actQuoteLevel = -1;
2995 if ( GlobalSettings::self()->showExpandQuotesMark() )
2998 if ( mCollapseIcon.isEmpty() ) {
2999 mCollapseIcon= LinkLocator::pngToDataUrl(
3000 TDEGlobal::instance()->iconLoader()->iconPath(
"quotecollapse",0 ));
3002 if ( mExpandIcon.isEmpty() )
3003 mExpandIcon= LinkLocator::pngToDataUrl(
3004 TDEGlobal::instance()->iconLoader()->iconPath(
"quoteexpand",0 ));
3007 for (
unsigned int p=0; p<line.length(); p++) {
3008 switch (line[p].latin1()) {
3023 bool actHidden =
false;
3024 TQString textExpand;
3027 if (GlobalSettings::self()->showExpandQuotesMark() && mReader->mLevelQuote >= 0
3028 && mReader->mLevelQuote <= ( actQuoteLevel ) )
3031 if ( actQuoteLevel != currQuoteLevel ) {
3033 if (currQuoteLevel == -1)
3034 htmlStr.append( normalEndTag );
3035 else if ( currQuoteLevel >= 0 && !curHidden )
3036 htmlStr.append( quoteEnd );
3039 if (actQuoteLevel == -1)
3040 htmlStr += normalStartTag;
3043 if ( GlobalSettings::self()->showExpandQuotesMark() )
3051 htmlStr +=
"<div class=\"quotelevelmark\" >" ;
3052 htmlStr += TQString(
"<a href=\"kmail:levelquote?%1 \">"
3053 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3055 .arg( mExpandIcon );
3056 htmlStr +=
"</div><br/>";
3057 htmlStr += quoteEnd;
3060 htmlStr +=
"<div class=\"quotelevelmark\" >" ;
3061 htmlStr += TQString(
"<a href=\"kmail:levelquote?%1 \">"
3062 "<img src=\"%2\" alt=\"\" title=\"\"/></a>" )
3064 .arg( mCollapseIcon);
3065 htmlStr +=
"</div>";
3066 if ( actQuoteLevel < 3 )
3067 htmlStr += quoteFontTag[actQuoteLevel];
3069 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3072 if ( actQuoteLevel < 3 )
3073 htmlStr += quoteFontTag[actQuoteLevel];
3075 htmlStr += deepQuoteFontTag[actQuoteLevel%3];
3077 currQuoteLevel = actQuoteLevel;
3079 curHidden = actHidden;
3086 if( !line.replace(
'\015',
"").isEmpty() )
3088 htmlStr +=TQString(
"<div dir=\"%1\">" ).arg( line.isRightToLeft() ?
"rtl":
"ltr" );
3089 htmlStr += LinkLocator::convertToHtml( line, convertFlags );
3090 htmlStr += TQString(
"</div>" );
3098 if (currQuoteLevel == -1)
3099 htmlStr.append( normalEndTag );
3101 htmlStr.append( quoteEnd );
3108 const TQTextCodec * ObjectTreeParser::codecFor( partNode * node )
const {
3110 if ( mReader && mReader->overrideCodec() )
3111 return mReader->overrideCodec();
3112 return node->msgPart().codec();
3116 void ObjectTreeParser::dumpToFile(
const char * filename,
const char * start,
3120 TQFile f( filename );
3121 if ( f.open( IO_WriteOnly ) ) {
3123 TQDataStream ds( &f );
3124 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.