7#include "defaultrenderer.h"
9#include "defaultrenderer_p.h"
11#include "utils/messageviewerutil.h"
13#include "messageviewer_debug.h"
15#include "converthtmltoplaintext.h"
17#include "messagepartrendererbase.h"
18#include "messagepartrendererfactory.h"
19#include "messagepartrenderermanager.h"
20#include "utils/iconnamecache.h"
21#include "viewer/attachmentstrategy.h"
22#include "viewer/csshelperbase.h"
24#include "htmlwriter/bufferedhtmlwriter.h"
25#include <MimeTreeParser/MessagePart>
26#include <MimeTreeParser/ObjectTreeParser>
28#include <Libkleo/Formatting>
29#include <QGpgME/Protocol>
31#include <Libkleo/Formatting>
32#include <MessageCore/StringUtil>
34#include <KEmailAddress>
36#include <KLocalizedString>
38#include <KTextTemplate/Context>
39#include <KTextTemplate/Engine>
40#include <KTextTemplate/MetaType>
41#include <KTextTemplate/Template>
42#include <KTextTemplate/TemplateLoader>
45using namespace MimeTreeParser;
46using namespace MessageViewer;
47#ifndef COMPILE_WITH_UNITY_CMAKE_SUPPORT
48Q_DECLARE_METATYPE(GpgME::DecryptionResult::Recipient)
49Q_DECLARE_METATYPE(GpgME::Key)
50Q_DECLARE_METATYPE(
const QGpgME::Protocol *)
52static const int SIG_FRAME_COL_UNDEF = 99;
53#define SIG_FRAME_COL_RED -1
54#define SIG_FRAME_COL_YELLOW 0
55#define SIG_FRAME_COL_GREEN 1
56QString sigStatusToString(
const QGpgME::Protocol *cryptProto,
int status_code, GpgME::Signature::Summary summary,
int &frameColor,
bool &showKeyInfos)
64 if (cryptProto == QGpgME::openpgp()) {
67 switch (status_code) {
69 result =
i18n(
"Error: Signature not verified");
70 frameColor = SIG_FRAME_COL_YELLOW;
73 result =
i18n(
"Good signature");
74 frameColor = SIG_FRAME_COL_GREEN;
77 result =
i18n(
"Bad signature");
78 frameColor = SIG_FRAME_COL_RED;
81 result =
i18n(
"No public key to verify the signature");
82 frameColor = SIG_FRAME_COL_RED;
85 result =
i18n(
"No signature found");
86 frameColor = SIG_FRAME_COL_RED;
89 result =
i18n(
"Error verifying the signature");
90 frameColor = SIG_FRAME_COL_RED;
93 result =
i18n(
"Different results for signatures");
94 frameColor = SIG_FRAME_COL_RED;
108 }
else if (cryptProto == QGpgME::smime()) {
112 if (summary == GpgME::Signature::None) {
113 result =
i18n(
"No status information available.");
114 frameColor = SIG_FRAME_COL_YELLOW;
115 showKeyInfos =
false;
119 if (summary & GpgME::Signature::Valid) {
120 result =
i18n(
"Good signature.");
129 frameColor = SIG_FRAME_COL_GREEN;
130 showKeyInfos =
false;
137 frameColor = SIG_FRAME_COL_GREEN;
139 if (summary & GpgME::Signature::KeyExpired) {
141 result2 =
i18n(
"One key has expired.");
143 if (summary & GpgME::Signature::SigExpired) {
145 result2 +=
i18n(
"The signature has expired.");
149 if (summary & GpgME::Signature::KeyMissing) {
150 result2 +=
i18n(
"Unable to verify: key missing.");
153 showKeyInfos =
false;
154 frameColor = SIG_FRAME_COL_YELLOW;
156 if (summary & GpgME::Signature::CrlMissing) {
157 result2 +=
i18n(
"CRL not available.");
158 frameColor = SIG_FRAME_COL_YELLOW;
160 if (summary & GpgME::Signature::CrlTooOld) {
161 result2 +=
i18n(
"Available CRL is too old.");
162 frameColor = SIG_FRAME_COL_YELLOW;
164 if (summary & GpgME::Signature::BadPolicy) {
165 result2 +=
i18n(
"A policy was not met.");
166 frameColor = SIG_FRAME_COL_YELLOW;
168 if (summary & GpgME::Signature::SysError) {
169 result2 +=
i18n(
"A system error occurred.");
173 showKeyInfos =
false;
174 frameColor = SIG_FRAME_COL_YELLOW;
178 if (summary & GpgME::Signature::KeyRevoked) {
180 result2 +=
i18n(
"One key has been revoked.");
181 frameColor = SIG_FRAME_COL_RED;
183 if (summary & GpgME::Signature::Red) {
197 showKeyInfos =
false;
199 frameColor = SIG_FRAME_COL_RED;
204 if (SIG_FRAME_COL_GREEN == frameColor) {
205 result =
i18n(
"Good signature.");
206 }
else if (SIG_FRAME_COL_RED == frameColor) {
207 result =
i18n(
"Bad signature.");
230 : mCSSHelper(cssHelper)
231 , mRendererFactory(rendererFactory)
235DefaultRendererPrivate::~DefaultRendererPrivate() =
default;
244 return mMsgPart->source();
249 for (
const auto &m : msgPart->subParts()) {
250 renderFactory(m, htmlWriter);
263 if (mp->isAttachment()) {
267 renderSubParts(mp, htmlWriter);
274 if (mp->isAttachment()) {
281 renderSubParts(mp, htmlWriter);
286 if (!mp->hasSubParts()) {
289 KTextTemplate::Template t = MessagePartRendererManager::self()->loadByName(QStringLiteral(
"encapsulatedrfc822messagepart.html"));
293 c.
insert(QStringLiteral(
"block"), &block);
294 block.
setProperty(
"link", mp->nodeHelper()->asHREF(mp->message().data(), QStringLiteral(
"body")));
296 c.
insert(QStringLiteral(
"msgHeader"), mCreateMessageHeader(mp->message().data()));
298 renderSubParts(mp, htmlWriter);
301 if (mp->isAttachment()) {
310 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"htmlmessagepart.html"));
314 c.
insert(QStringLiteral(
"block"), &block);
316 auto preferredMode = mp->source()->preferredMode();
319 block.
setProperty(
"loadExternal", htmlLoadExternal());
323 const Util::HtmlMessageInfo messageInfo = Util::processHtml(mp->bodyHtml());
325 if (isHtmlPreferred) {
326 mp->nodeHelper()->setNodeDisplayedEmbedded(mp->content(),
true);
327 htmlWriter->setExtraHead(messageInfo.extraHead);
328 htmlWriter->setStyleBody(Util::parseBodyStyle(messageInfo.bodyStyle));
332 c.
insert(QStringLiteral(
"content"), messageInfo.htmlSource);
336 ConvertHtmlToPlainText
convert;
337 convert.setHtmlString(mp->bodyHtml());
340 c.
insert(QStringLiteral(
"plaintext"), plaintext);
346 if (mp->isAttachment()) {
356 const auto metaData = *mp->partMetaData();
357 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"encryptedmessagepart.html"));
360 if (node || mp->hasSubParts()) {
363 if (mp->content() && mp->isRoot()) {
366 renderSubParts(mp, htmlWriter);
368 }
else if (!metaData.inProgress) {
370 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
374 if (!mp->decryptRecipients().empty()) {
377 c.
insert(QStringLiteral(
"block"), &block);
380 block.
setProperty(
"detailHeader", showEncryptionDetails());
381 block.
setProperty(
"inProgress", metaData.inProgress);
382 block.
setProperty(
"isDecrypted", mp->decryptMessage());
383 block.
setProperty(
"isDecryptable", metaData.isDecryptable);
385 block.
setProperty(
"errorText", metaData.errorText);
387 block.
setProperty(
"isCompliant", metaData.isCompliant);
388 block.
setProperty(
"compliance", metaData.compliance);
396 const auto metaData = *mp->partMetaData();
397 auto cryptoProto = mp->cryptoProto();
399 const bool isSMIME = cryptoProto && (cryptoProto == QGpgME::smime());
400 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"signedmessagepart.html"));
410 renderSubParts(mp, htmlWriter);
412 }
else if (!metaData.inProgress) {
414 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
418 c.
insert(QStringLiteral(
"block"), &block);
420 block.
setProperty(
"inProgress", metaData.inProgress);
421 block.
setProperty(
"errorText", metaData.errorText);
423 block.
setProperty(
"detailHeader", showSignatureDetails());
425 block.
setProperty(
"technicalProblem", metaData.technicalProblem);
427 if (metaData.creationTime.isValid()) {
430 block.
setProperty(
"isGoodSignature", metaData.isGoodSignature);
431 block.
setProperty(
"isCompliant", metaData.isCompliant);
432 block.
setProperty(
"compliance", metaData.compliance);
445 block.
setProperty(
"keyWithWithoutURL", keyWithWithoutURL);
451 const auto signatures = metaData.verificationResult.signatures();
452 if (metaData.inProgress) {
453 mClass = QStringLiteral(
"signInProgress");
455 Q_ASSERT(!signatures.empty());
456 const auto signature = signatures.front();
457 const auto summary = signature.summary();
459 statusStr = Kleo::Formatting::prettySignature(signature, {});
461 if (summary & GpgME::Signature::Summary::Red) {
462 mClass = QStringLiteral(
"signErr");
463 }
else if (summary & GpgME::Signature::Summary::Valid) {
464 mClass = QStringLiteral(
"signOkKeyOk");
466 mClass = QStringLiteral(
"signWarn");
478 const auto metaData = *mp->partMetaData();
479 if (metaData.isSigned || metaData.inProgress) {
481 if (mp->isAttachment()) {
484 renderSigned(mp, htmlWriter);
489 if (mp->isAttachment()) {
492 if (mp->hasSubParts()) {
493 renderSubParts(mp, htmlWriter);
494 }
else if (!metaData.inProgress) {
495 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
501 const auto metaData = *mp->partMetaData();
503 if (metaData.isEncrypted || metaData.inProgress) {
505 if (mp->isAttachment()) {
508 renderEncrypted(mp, htmlWriter);
513 if (mp->isAttachment()) {
517 if (mp->hasSubParts()) {
518 renderSubParts(mp, htmlWriter);
519 }
else if (!metaData.inProgress) {
520 renderWithFactory<MimeTreeParser::MessagePart>(mp, htmlWriter);
527 if (mp->isAttachment()) {
531 auto mode = mp->preferredMode();
533 const auto availableModes = mp->availableModes();
534 for (
const auto m : availableModes) {
542 if (mp->childParts().contains(mode)) {
543 part = mp->childParts()[mode];
546 render(part, htmlWriter);
551 const GpgME::ImportResult &importResult(mp->importResult());
552 KTextTemplate::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral(
"certmessagepart.html"));
556 c.
insert(QStringLiteral(
"block"), &block);
557 block.
setProperty(
"importError", Kleo::Formatting::errorAsString(importResult.error()));
558 block.
setProperty(
"nImp", importResult.numImported());
559 block.
setProperty(
"nUnc", importResult.numUnchanged());
560 block.
setProperty(
"nSKImp", importResult.numSecretKeysImported());
561 block.
setProperty(
"nSKUnc", importResult.numSecretKeysUnchanged());
563 QVariantList keylist;
564 const auto imports = importResult.imports();
566 auto end(imports.end());
567 for (
auto it = imports.begin(); it != end; ++it) {
569 key->setProperty(
"error", Kleo::Formatting::errorAsString(it->error()));
570 key->setProperty(
"status", (*it).status());
576 if (mp->isAttachment()) {
585 if (!mRendererFactory) {
588 for (
auto r : mRendererFactory->renderersForPart(mo, msgPart)) {
589 if (r->render(msgPart, htmlWriter,
this)) {
600 if (isHiddenHint(msgPart)) {
601 const QByteArray cid = msgPart->content()->contentID()->identifier();
604 const QString fileName = mp->temporaryFilePath();
610 if (renderWithFactory(msgPart, htmlWriter)) {
617 render(mp, htmlWriter);
622 render(mp, htmlWriter);
624 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::EncapsulatedRfc822MessagePart")) {
627 render(mp, htmlWriter);
632 render(mp, htmlWriter);
634 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::SignedMessagePart")) {
637 render(mp, htmlWriter);
639 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::EncryptedMessagePart")) {
642 render(mp, htmlWriter);
644 }
else if (className ==
QLatin1StringView(
"MimeTreeParser::AlternativeMessagePart")) {
647 render(mp, htmlWriter);
652 render(mp, htmlWriter);
655 qCWarning(MESSAGEVIEWER_LOG) <<
"We got a unknown classname, using default behaviour for " << className;
662 auto content = msgPart->content();
664 if (!mp || !content) {
668 if (mShowOnlyOneMimePart && mMsgPart.data() == msgPart->parentPart()) {
669 if (mMsgPart->subParts().at(0) == msgPart.
data()) {
674 if (msgPart->nodeHelper()->isNodeDisplayedHidden(content)) {
679 const bool defaultHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None);
680 auto preferredMode = source()->preferredMode();
684 if (content->contentType(
false) && !content->contentType(
false)->mediaType().isEmpty() && !content->contentType(
false)->subType().isEmpty()) {
685 mediaType = content->contentType(
false)->mediaType();
687 const bool isTextPart = (mediaType == QByteArrayLiteral(
"text"));
689 bool defaultAsIcon =
true;
690 if (!mp->neverDisplayInline()) {
692 defaultAsIcon = as->defaultDisplay(content) == AttachmentStrategy::AsIcon;
697 if (!mp->isImage() && !isTextPart) {
698 defaultAsIcon =
true;
703 hidden = defaultHidden;
705 if (mp->isImage() && isHtmlPreferred && content->
parent() && content->
parent()->contentType(
false)->subType() ==
"related") {
708 hidden = defaultHidden && content->
parent();
709 hidden |= defaultAsIcon && defaultHidden;
712 msgPart->nodeHelper()->setNodeDisplayedHidden(content, hidden);
719 auto content = msgPart->content();
721 if (!content || !mp) {
722 return MimeTreeParser::IconType::NoIcon;
726 const bool defaultDisplayHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None);
727 const bool defaultDisplayInline(as && as->defaultDisplay(content) == AttachmentStrategy::Inline);
728 const bool defaultDisplayAsIcon(as && as->defaultDisplay(content) == AttachmentStrategy::AsIcon);
729 const bool showOnlyOneMimePart(mShowOnlyOneMimePart);
730 auto preferredMode = source()->preferredMode();
734 if (content->contentType(
false) && !content->contentType(
false)->mediaType().isEmpty() && !content->contentType(
false)->subType().isEmpty()) {
735 mediaType = content->contentType(
false)->mediaType();
737 const bool isTextPart = (mediaType == QByteArrayLiteral(
"text"));
739 bool defaultAsIcon =
true;
740 if (!mp->neverDisplayInline()) {
742 defaultAsIcon = defaultDisplayAsIcon;
745 if (mp->isImage() && showOnlyOneMimePart && !mp->neverDisplayInline()) {
746 defaultAsIcon =
false;
750 if (!mp->isImage() && !isTextPart) {
751 defaultAsIcon =
true;
755 if (as && !defaultDisplayInline) {
756 return MimeTreeParser::IconExternal;
758 return MimeTreeParser::NoIcon;
760 if (mp->isImage() && isHtmlPreferred && content->
parent() && content->
parent()->contentType(
false)->subType() ==
"related") {
761 return MimeTreeParser::IconInline;
764 if (defaultDisplayHidden && !showOnlyOneMimePart && content->
parent()) {
765 return MimeTreeParser::IconInline;
769 return MimeTreeParser::IconExternal;
770 }
else if (mp->isImage()) {
771 return MimeTreeParser::IconInline;
775 return MimeTreeParser::NoIcon;
778bool DefaultRendererPrivate::showEmoticons()
const
780 return mShowEmoticons;
783bool DefaultRendererPrivate::isPrinting()
const
788bool DefaultRendererPrivate::htmlLoadExternal()
const
790 return mHtmlLoadExternal;
793bool DefaultRendererPrivate::showExpandQuotesMark()
const
795 return mShowExpandQuotesMark;
798bool DefaultRendererPrivate::showOnlyOneMimePart()
const
800 return mShowOnlyOneMimePart;
803bool DefaultRendererPrivate::showSignatureDetails()
const
805 return mShowSignatureDetails;
808bool DefaultRendererPrivate::showEncryptionDetails()
const
810 return mShowEncryptionDetails;
813int DefaultRendererPrivate::levelQuote()
const
823DefaultRenderer::~DefaultRenderer() =
default;
825void DefaultRenderer::setShowOnlyOneMimePart(
bool onlyOneMimePart)
827 d->mShowOnlyOneMimePart = onlyOneMimePart;
832 d->mAttachmentStrategy = strategy;
835void DefaultRenderer::setShowEmoticons(
bool showEmoticons)
837 d->mShowEmoticons = showEmoticons;
840void DefaultRenderer::setIsPrinting(
bool isPrinting)
842 d->mIsPrinting = isPrinting;
845void DefaultRenderer::setShowExpandQuotesMark(
bool showExpandQuotesMark)
847 d->mShowExpandQuotesMark = showExpandQuotesMark;
850void DefaultRenderer::setShowEncryptionDetails(
bool showEncryptionDetails)
852 d->mShowEncryptionDetails = showEncryptionDetails;
855void DefaultRenderer::setShowSignatureDetails(
bool showSignatureDetails)
857 d->mShowSignatureDetails = showSignatureDetails;
860void DefaultRenderer::setLevelQuote(
int levelQuote)
862 d->mLevelQuote = levelQuote;
865void DefaultRenderer::setHtmlLoadExternal(
bool htmlLoadExternal)
867 d->mHtmlLoadExternal = htmlLoadExternal;
870void DefaultRenderer::setCreateMessageHeader(
const std::function<
QString(
KMime::Message *)> &createMessageHeader)
872 d->mCreateMessageHeader = createMessageHeader;
879 for (
const auto &subPart : messagePart->subParts()) {
880 ret += renderTreeHelper(subPart, indent);
887 qCDebug(MESSAGEVIEWER_LOG) <<
"MimeTreeParser structure:";
888 qCDebug(MESSAGEVIEWER_LOG) << qPrintable(renderTreeHelper(msgPart,
QString()));
889 d->mMsgPart = msgPart;
890 d->renderFactory(d->mMsgPart, writer);
void insert(const QString &name, const QVariant &variant)
QString render(Context *c) const
The AttachmentMarkBlock class.
The AttachmentStrategy class.
An interface for HTML sinks.
virtual void embedPart(const QByteArray &contentId, const QString &url)=0
Embed a part with Content-ID contentId, using url url.
QTextStream * stream() const
Returns a QTextStream on device().
The MessagePartRendererFactory class.
The AlternativeMessagePart class.
The CertMessagePart class.
The EncapsulatedRfc822MessagePart class.
The EncryptedMessagePart class.
The HtmlMessagePart class.
Interface for object tree sources.
The MessagePartList class.
The MimeMessagePart class.
The SignedMessagePart class.
The TextMessagePart class.
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
T convert(const QVariant &value)
const QList< QKeySequence > & end()
MESSAGEVIEWER_EXPORT bool containsExternalReferences(const QString &str, const QString &extraHead)
Checks whether str contains external references.
@ Html
A HTML message, non-multipart.
@ Normal
A normal plaintext message, non-multipart.
@ MultipartPlain
A multipart/alternative message, the plain text part is currently displayed.
@ MultipartHtml
A multipart/alternative message, the HTML part is currently displayed.
bool isEmpty() const const
QObject * parent() const const
bool setProperty(const char *name, QVariant &&value)
QSharedPointer< X > dynamicCast() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QUrl fromLocalFile(const QString &localFile)
QString url(FormattingOptions options) const const
QVariant fromValue(T &&value)