diff --git a/messageviewer/src/messagepartthemes/default/cachehtmlwriter.h b/messageviewer/src/messagepartthemes/default/cachehtmlwriter.h new file mode 100644 index 00000000..4c43480e --- /dev/null +++ b/messageviewer/src/messagepartthemes/default/cachehtmlwriter.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2016 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef MESSAGEVIEWER_CACHEHTMLWRITER_H +#define MESSAGEVIEWER_CACHEHTMLWRITER_H + +#include "messageviewer_export.h" +#include + +#include + +namespace MessageViewer { + +/** @deprecated port to proper streaming API */ +class MESSAGEVIEWER_DEPRECATED CacheHtmlWriter : public MimeTreeParser::BufferedHtmlWriter +{ +public: + explicit CacheHtmlWriter(MimeTreeParser::HtmlWriter *baseWriter = nullptr) + : mBaseWriter(baseWriter) + { + BufferedHtmlWriter::begin(); + } + ~CacheHtmlWriter() = default; + + void embedPart(const QByteArray &contentId, const QString &url) override + { + if (mBaseWriter) + mBaseWriter->embedPart(contentId, url); + else + embedParts.insert(contentId, url); + } + + void extraHead(const QString &extra) override + { + if (mBaseWriter) + mBaseWriter->extraHead(extra); + else + head.append(extra); + } + + QString html() + { + BufferedHtmlWriter::end(); + return QString::fromUtf8(data()); + } + + QString head; + QMap embedParts; + +private: + MimeTreeParser::HtmlWriter *mBaseWriter = nullptr; +}; + +} + +#endif // MESSAGEVIEWER_CACHEHTMLWRITER_H diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp index fab2146e..f8225190 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp @@ -1,1005 +1,962 @@ /* Copyright (c) 2016 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "defaultrenderer.h" #include "defaultrenderer_p.h" #include "messageviewer_debug.h" +#include "cachehtmlwriter.h" #include "converthtmltoplaintext.h" #include "messagepartrendererbase.h" #include "messagepartrendererfactory.h" #include "htmlblock.h" #include "partrendered.h" #include "utils/iconnamecache.h" #include "utils/mimetype.h" #include "viewer/csshelperbase.h" #include "messagepartrenderermanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace MimeTreeParser; using namespace MessageViewer; Q_DECLARE_METATYPE(GpgME::DecryptionResult::Recipient) Q_DECLARE_METATYPE(const QGpgME::Protocol *) static const int SIG_FRAME_COL_UNDEF = 99; #define SIG_FRAME_COL_RED -1 #define SIG_FRAME_COL_YELLOW 0 #define SIG_FRAME_COL_GREEN 1 QString sigStatusToString(const QGpgME::Protocol *cryptProto, int status_code, GpgME::Signature::Summary summary, int &frameColor, bool &showKeyInfos) { // note: At the moment frameColor and showKeyInfos are // used for CMS only but not for PGP signatures // pending(khz): Implement usage of these for PGP sigs as well. showKeyInfos = true; QString result; if (cryptProto) { if (cryptProto == QGpgME::openpgp()) { // process enum according to it's definition to be read in // GNU Privacy Guard CVS repository /gpgme/gpgme/gpgme.h switch (status_code) { case 0: // GPGME_SIG_STAT_NONE result = i18n("Error: Signature not verified"); break; case 1: // GPGME_SIG_STAT_GOOD result = i18n("Good signature"); break; case 2: // GPGME_SIG_STAT_BAD result = i18n("Bad signature"); break; case 3: // GPGME_SIG_STAT_NOKEY result = i18n("No public key to verify the signature"); break; case 4: // GPGME_SIG_STAT_NOSIG result = i18n("No signature found"); break; case 5: // GPGME_SIG_STAT_ERROR result = i18n("Error verifying the signature"); break; case 6: // GPGME_SIG_STAT_DIFF result = i18n("Different results for signatures"); break; /* PENDING(khz) Verify exact meaning of the following values: case 7: // GPGME_SIG_STAT_GOOD_EXP return i18n("Signature certificate is expired"); break; case 8: // GPGME_SIG_STAT_GOOD_EXPKEY return i18n("One of the certificate's keys is expired"); break; */ default: result.clear(); // do *not* return a default text here ! break; } } else if (cryptProto == QGpgME::smime()) { // process status bits according to SigStatus_... // definitions in kdenetwork/libkdenetwork/cryptplug.h if (summary == GpgME::Signature::None) { result = i18n("No status information available."); frameColor = SIG_FRAME_COL_YELLOW; showKeyInfos = false; return result; } if (summary & GpgME::Signature::Valid) { result = i18n("Good signature."); // Note: // Here we are work differently than KMail did before! // // The GOOD case ( == sig matching and the complete // certificate chain was verified and is valid today ) // by definition does *not* show any key // information but just states that things are OK. // (khz, according to LinuxTag 2002 meeting) frameColor = SIG_FRAME_COL_GREEN; showKeyInfos = false; return result; } // we are still there? OK, let's test the different cases: // we assume green, test for yellow or red (in this order!) frameColor = SIG_FRAME_COL_GREEN; QString result2; if (summary & GpgME::Signature::KeyExpired) { // still is green! result2 = i18n("One key has expired."); } if (summary & GpgME::Signature::SigExpired) { // and still is green! result2 += i18n("The signature has expired."); } // test for yellow: if (summary & GpgME::Signature::KeyMissing) { result2 += i18n("Unable to verify: key missing."); // if the signature certificate is missing // we cannot show information on it showKeyInfos = false; frameColor = SIG_FRAME_COL_YELLOW; } if (summary & GpgME::Signature::CrlMissing) { result2 += i18n("CRL not available."); frameColor = SIG_FRAME_COL_YELLOW; } if (summary & GpgME::Signature::CrlTooOld) { result2 += i18n("Available CRL is too old."); frameColor = SIG_FRAME_COL_YELLOW; } if (summary & GpgME::Signature::BadPolicy) { result2 += i18n("A policy was not met."); frameColor = SIG_FRAME_COL_YELLOW; } if (summary & GpgME::Signature::SysError) { result2 += i18n("A system error occurred."); // if a system error occurred // we cannot trust any information // that was given back by the plug-in showKeyInfos = false; frameColor = SIG_FRAME_COL_YELLOW; } // test for red: if (summary & GpgME::Signature::KeyRevoked) { // this is red! result2 += i18n("One key has been revoked."); frameColor = SIG_FRAME_COL_RED; } if (summary & GpgME::Signature::Red) { if (result2.isEmpty()) { // Note: // Here we are work differently than KMail did before! // // The BAD case ( == sig *not* matching ) // by definition does *not* show any key // information but just states that things are BAD. // // The reason for this: In this case ALL information // might be falsificated, we can NOT trust the data // in the body NOT the signature - so we don't show // any key/signature information at all! // (khz, according to LinuxTag 2002 meeting) showKeyInfos = false; } frameColor = SIG_FRAME_COL_RED; } else { result.clear(); } if (SIG_FRAME_COL_GREEN == frameColor) { result = i18n("Good signature."); } else if (SIG_FRAME_COL_RED == frameColor) { result = i18n("Bad signature."); } else { result.clear(); } if (!result2.isEmpty()) { if (!result.isEmpty()) { result.append(QLatin1String("
")); } result.append(result2); } } /* // add i18n support for 3rd party plug-ins here: else if ( cryptPlug->libName().contains( "yetanotherpluginname", Qt::CaseInsensitive )) { } */ } return result; } /** Checks whether @p str contains external references. To be precise, we only check whether @p str contains 'xxx="http[s]:' where xxx is not href. Obfuscated external references are ignored on purpose. */ bool containsExternalReferences(const QString &str, const QString &extraHead) { const bool hasBaseInHeader = extraHead.contains(QStringLiteral( "= 0 || httpsPos >= 0) { // pos = index of next occurrence of "http: or "https: whichever comes first int pos = (httpPos < httpsPos) ? ((httpPos >= 0) ? httpPos : httpsPos) : ((httpsPos >= 0) ? httpsPos : httpPos); // look backwards for "href" if (pos > 5) { int hrefPos = str.lastIndexOf(QLatin1String("href"), pos - 5, Qt::CaseInsensitive); // if no 'href' is found or the distance between 'href' and '"http[s]:' // is larger than 7 (7 is the distance in 'href = "http[s]:') then // we assume that we have found an external reference if ((hrefPos == -1) || (pos - hrefPos > 7)) { // HTML messages created by KMail itself for now contain the following: // // Make sure not to show an external references warning for this string int dtdPos = str.indexOf(QLatin1String( "http://www.w3.org/TR/html4/loose.dtd"), pos + 1); if (dtdPos != (pos + 1)) { return true; } } } // find next occurrence of "http: or "https: if (pos == httpPos) { httpPos = str.indexOf(QLatin1String("\"http:"), httpPos + 6, Qt::CaseInsensitive); } else { httpsPos = str.indexOf(QLatin1String("\"https:"), httpsPos + 7, Qt::CaseInsensitive); } } return false; } // FIXME this used to go through the full webkit parser to extract the body and head blocks // until we have that back, at least attempt to fix some of the damage // yes, "parsing" HTML with regexps is very very wrong, but it's still better than not filtering // this at all... QString processHtml(const QString &htmlSource, QString &extraHead) { auto s = htmlSource.trimmed(); s = s.replace(QRegExp(QStringLiteral("^]*>"), Qt::CaseInsensitive), QString()).trimmed(); s = s.replace(QRegExp(QStringLiteral("^]*>"), Qt::CaseInsensitive), QString()).trimmed(); // head s = s.replace(QRegExp(QStringLiteral("^"), Qt::CaseInsensitive), QString()).trimmed(); if (s.startsWith(QLatin1String("", Qt::CaseInsensitive))) { const auto idx = s.indexOf(QLatin1String(""), Qt::CaseInsensitive); if (idx < 0) { return htmlSource; } extraHead = s.mid(6, idx - 6); s = s.mid(idx + 7).trimmed(); } // body s = s.replace(QRegExp(QStringLiteral("]*>"), Qt::CaseInsensitive), QString()).trimmed(); s = s.replace(QRegExp(QStringLiteral("$"), Qt::CaseInsensitive), QString()).trimmed(); s = s.replace(QRegExp(QStringLiteral("$"), Qt::CaseInsensitive), QString()).trimmed(); return s; } -class CacheHtmlWriter : public MimeTreeParser::BufferedHtmlWriter -{ -public: - explicit CacheHtmlWriter(MimeTreeParser::HtmlWriter *baseWriter) - : mBaseWriter(baseWriter) - { - BufferedHtmlWriter::begin(); - } - - ~CacheHtmlWriter() = default; - void begin() override - { - mBaseWriter->begin(); - } - - void end() override - { - mBaseWriter->end(); - } - - void reset() override - { - mBaseWriter->reset(); - } - - void embedPart(const QByteArray &contentId, const QString &url) override - { - mBaseWriter->embedPart(contentId, url); - } - - void extraHead(const QString &extra) override - { - mBaseWriter->extraHead(extra); - } - - QString html() - { - BufferedHtmlWriter::end(); - return QString::fromUtf8(data()); - } - - MimeTreeParser::HtmlWriter *mBaseWriter = nullptr; -}; - DefaultRendererPrivate::DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory) : mMsgPart(msgPart) , mOldWriter(writer) , mCSSHelper(cssHelper) , mRendererFactory(rendererFactory) { mHtml = renderFactory(mMsgPart, nullptr); } DefaultRendererPrivate::~DefaultRendererPrivate() { } QString DefaultRendererPrivate::alignText() { return QApplication::isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral("ltr"); } CSSHelperBase *DefaultRendererPrivate::cssHelper() const { return mCSSHelper; } Interface::ObjectTreeSource *DefaultRendererPrivate::source() const { return mMsgPart->source(); } void DefaultRendererPrivate::renderSubParts(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) { foreach (const auto &m, msgPart->subParts()) htmlWriter->write(renderFactory(m, htmlWriter)); } void DefaultRendererPrivate::render(const MessagePartList::Ptr &mp, HtmlWriter *htmlWriter) { HTMLBlock::Ptr rBlock; HTMLBlock::Ptr aBlock; if (mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter)); } if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } renderSubParts(mp, htmlWriter); } void DefaultRendererPrivate::render(const MimeMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { HTMLBlock::Ptr aBlock; HTMLBlock::Ptr rBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } if (mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter)); } renderSubParts(mp, htmlWriter); } void DefaultRendererPrivate::render(const EncapsulatedRfc822MessagePart::Ptr &mp, HtmlWriter *htmlWriter) { if (!mp->hasSubParts()) { return; } Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/encapsulatedrfc822messagepart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; c.insert(QStringLiteral("block"), &block); block.setProperty("dir", alignText()); block.setProperty("link", mp->mOtp->nodeHelper()->asHREF(mp->mMessage.data(), QStringLiteral("body"))); c.insert(QStringLiteral("msgHeader"), mp->source()->createMessageHeader(mp->mMessage.data())); { auto _htmlWriter = QSharedPointer(new CacheHtmlWriter(htmlWriter)); renderSubParts(mp, _htmlWriter.data()); c.insert(QStringLiteral("content"), _htmlWriter->html()); } HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); } void DefaultRendererPrivate::render(const HtmlMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/htmlmessagepart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; c.insert(QStringLiteral("block"), &block); auto preferredMode = mp->source()->preferredMode(); bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); const bool isPrinting = mp->source()->isPrinting(); block.setProperty("htmlMail", isHtmlPreferred); block.setProperty("loadExternal", mp->source()->htmlLoadExternal()); block.setProperty("isPrinting", isPrinting); { QString extraHead; //laurent: FIXME port to async method webengine QString bodyText = processHtml(mp->mBodyHTML, extraHead); if (isHtmlPreferred) { mp->mOtp->nodeHelper()->setNodeDisplayedEmbedded(mp->content(), true); htmlWriter->extraHead(extraHead); } block.setProperty("containsExternalReferences", containsExternalReferences(bodyText, extraHead)); c.insert(QStringLiteral("content"), bodyText); } { ConvertHtmlToPlainText convert; convert.setHtmlString(mp->mBodyHTML); QString plaintext = convert.generatePlainText(); plaintext.replace(QLatin1Char('\n'), QStringLiteral("
")); c.insert(QStringLiteral("plaintext"), plaintext); } mp->source()->setHtmlMode(Util::Html, QList() << Util::Html); HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); } void DefaultRendererPrivate::renderEncrypted(const EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { KMime::Content *node = mp->content(); const auto metaData = *mp->partMetaData(); Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/encryptedmessagepart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; if (node || mp->hasSubParts()) { auto _htmlWriter = QSharedPointer(new CacheHtmlWriter(mOldWriter)); { HTMLBlock::Ptr rBlock; if (node && mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(_htmlWriter.data())); } renderSubParts(mp, _htmlWriter.data()); } c.insert(QStringLiteral("content"), _htmlWriter->html()); } else if (!metaData.inProgress) { auto part = renderWithFactory(QStringLiteral("MimeTreeParser::MessagePart"), mp); if (part) { c.insert(QStringLiteral("content"), part->html()); } else { c.insert(QStringLiteral("content"), QString()); } } c.insert(QStringLiteral("cryptoProto"), QVariant::fromValue(mp->mCryptoProto)); if (mp->mDecryptRecipients.size() > 0) { c.insert(QStringLiteral("decryptedRecipients"), QVariant::fromValue(mp->mDecryptRecipients)); } c.insert(QStringLiteral("block"), &block); block.setProperty("dir", alignText()); block.setProperty("inProgress", metaData.inProgress); block.setProperty("isDecrypted", mp->decryptMessage()); block.setProperty("isDecryptable", metaData.isDecryptable); block.setProperty("decryptIcon", QUrl::fromLocalFile(IconNameCache::instance()->iconPath(QStringLiteral( "document-decrypt"), KIconLoader::Small)).url()); block.setProperty("errorText", metaData.errorText); block.setProperty("noSecKey", mp->mNoSecKey); block.setProperty("iconSize", MessageViewer::MessagePartRendererManager::self()->iconCurrentSize()); Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); } void DefaultRendererPrivate::renderSigned(const SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { KMime::Content *node = mp->content(); const auto metaData = *mp->partMetaData(); auto cryptoProto = mp->mCryptoProto; const bool isSMIME = cryptoProto && (cryptoProto == QGpgME::smime()); Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/signedmessagepart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; if (node) { auto _htmlWriter = QSharedPointer(new CacheHtmlWriter(htmlWriter)); { HTMLBlock::Ptr rBlock; if (mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(_htmlWriter.data())); } renderSubParts(mp, _htmlWriter.data()); } c.insert(QStringLiteral("content"), _htmlWriter->html()); } else if (!metaData.inProgress) { auto part = renderWithFactory(QStringLiteral("MimeTreeParser::MessagePart"), mp); if (part) { c.insert(QStringLiteral("content"), part->html()); } else { c.insert(QStringLiteral("content"), QString()); } } c.insert(QStringLiteral("cryptoProto"), QVariant::fromValue(cryptoProto)); c.insert(QStringLiteral("block"), &block); block.setProperty("dir", alignText()); block.setProperty("inProgress", metaData.inProgress); block.setProperty("errorText", metaData.errorText); block.setProperty("detailHeader", mp->source()->showSignatureDetails()); block.setProperty("printing", false); block.setProperty("addr", metaData.signerMailAddresses.join(QLatin1Char(','))); block.setProperty("technicalProblem", metaData.technicalProblem); block.setProperty("keyId", metaData.keyId); if (metaData.creationTime.isValid()) { //should be handled inside grantlee but currently not possible see: https://bugs.kde.org/363475 block.setProperty("creationTime", QLocale().toString(metaData.creationTime, QLocale::ShortFormat)); } block.setProperty("isGoodSignature", metaData.isGoodSignature); block.setProperty("isSMIME", isSMIME); if (metaData.keyTrust == GpgME::Signature::Unknown) { block.setProperty("keyTrust", QStringLiteral("unknown")); } else if (metaData.keyTrust == GpgME::Signature::Marginal) { block.setProperty("keyTrust", QStringLiteral("marginal")); } else if (metaData.keyTrust == GpgME::Signature::Full) { block.setProperty("keyTrust", QStringLiteral("full")); } else if (metaData.keyTrust == GpgME::Signature::Ultimate) { block.setProperty("keyTrust", QStringLiteral("ultimate")); } else { block.setProperty("keyTrust", QStringLiteral("untrusted")); } QString startKeyHREF; { QString keyWithWithoutURL; if (cryptoProto) { startKeyHREF = QStringLiteral("") .arg(cryptoProto->displayName(), cryptoProto->name(), QString::fromLatin1(metaData.keyId)); keyWithWithoutURL = QStringLiteral("%1%2").arg(startKeyHREF, QString::fromLatin1(QByteArray(QByteArrayLiteral( "0x") + metaData.keyId))); } else { keyWithWithoutURL = QStringLiteral("0x") + QString::fromUtf8(metaData.keyId); } block.setProperty("keyWithWithoutURL", keyWithWithoutURL); } bool onlyShowKeyURL = false; bool showKeyInfos = false; bool cannotCheckSignature = true; QString signer = metaData.signer; QString statusStr; QString mClass; QString greenCaseWarning; if (metaData.inProgress) { mClass = QStringLiteral("signInProgress"); } else { const QStringList &blockAddrs(metaData.signerMailAddresses); // note: At the moment frameColor and showKeyInfos are // used for CMS only but not for PGP signatures // pending(khz): Implement usage of these for PGP sigs as well. int frameColor = SIG_FRAME_COL_UNDEF; statusStr = sigStatusToString(cryptoProto, metaData.status_code, metaData.sigSummary, frameColor, showKeyInfos); // if needed fallback to english status text // that was reported by the plugin if (statusStr.isEmpty()) { statusStr = metaData.status; } if (metaData.technicalProblem) { frameColor = SIG_FRAME_COL_YELLOW; } switch (frameColor) { case SIG_FRAME_COL_RED: cannotCheckSignature = false; break; case SIG_FRAME_COL_YELLOW: cannotCheckSignature = true; break; case SIG_FRAME_COL_GREEN: cannotCheckSignature = false; break; } // temporary hack: always show key information! showKeyInfos = true; if (isSMIME && (SIG_FRAME_COL_UNDEF != frameColor)) { switch (frameColor) { case SIG_FRAME_COL_RED: mClass = QStringLiteral("signErr"); onlyShowKeyURL = true; break; case SIG_FRAME_COL_YELLOW: if (metaData.technicalProblem) { mClass = QStringLiteral("signWarn"); } else { mClass = QStringLiteral("signOkKeyBad"); } break; case SIG_FRAME_COL_GREEN: mClass = QStringLiteral("signOkKeyOk"); // extra hint for green case // that email addresses in DN do not match fromAddress QString msgFrom(KEmailAddress::extractEmailAddress(mp->mFromAddress)); QString certificate; if (metaData.keyId.isEmpty()) { certificate = i18n("certificate"); } else { certificate = startKeyHREF + i18n("certificate") + QStringLiteral(""); } if (!blockAddrs.empty()) { if (!blockAddrs.contains(msgFrom, Qt::CaseInsensitive)) { greenCaseWarning = QStringLiteral("") +i18nc("Start of warning message.", "Warning:") +QStringLiteral(" ") +i18n( "Sender's mail address is not stored in the %1 used for signing.", certificate) +QStringLiteral("
") +i18n("sender: ") +msgFrom +QStringLiteral("
") +i18n("stored: "); // We cannot use Qt's join() function here but // have to join the addresses manually to // extract the mail addresses (without '<''>') // before including it into our string: bool bStart = true; QStringList::ConstIterator end(blockAddrs.constEnd()); for (QStringList::ConstIterator it = blockAddrs.constBegin(); it != end; ++it) { if (!bStart) { greenCaseWarning.append(QStringLiteral(",
   ")); } bStart = false; greenCaseWarning.append(KEmailAddress::extractEmailAddress(*it)); } } } else { greenCaseWarning = QStringLiteral("") +i18nc("Start of warning message.", "Warning:") +QStringLiteral(" ") +i18n("No mail address is stored in the %1 used for signing, " "so we cannot compare it to the sender's address %2.", certificate, msgFrom); } break; } if (showKeyInfos && !cannotCheckSignature) { if (metaData.signer.isEmpty()) { signer.clear(); } else { if (!blockAddrs.empty()) { const QUrl address = KEmailAddress::encodeMailtoUrl(blockAddrs.first()); signer = QStringLiteral("%2").arg(QLatin1String(QUrl :: toPercentEncoding( address . path())), signer); } } } } else { if (metaData.signer.isEmpty() || metaData.technicalProblem) { mClass = QStringLiteral("signWarn"); } else { // HTMLize the signer's user id and create mailto: link signer = MessageCore::StringUtil::quoteHtmlChars(signer, true); signer = QStringLiteral("%1").arg(signer); if (metaData.isGoodSignature) { if (metaData.keyTrust < GpgME::Signature::Marginal) { mClass = QStringLiteral("signOkKeyBad"); } else { mClass = QStringLiteral("signOkKeyOk"); } } else { mClass = QStringLiteral("signErr"); } } } } block.setProperty("onlyShowKeyURL", onlyShowKeyURL); block.setProperty("showKeyInfos", showKeyInfos); block.setProperty("cannotCheckSignature", cannotCheckSignature); block.setProperty("signer", signer); block.setProperty("statusStr", statusStr); block.setProperty("signClass", mClass); block.setProperty("greenCaseWarning", greenCaseWarning); Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); } void DefaultRendererPrivate::render(const SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { const auto metaData = *mp->partMetaData(); if (metaData.isSigned || metaData.inProgress) { HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } renderSigned(mp, htmlWriter); return; } HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } if (mp->hasSubParts()) { renderSubParts(mp, htmlWriter); } else if (!metaData.inProgress) { auto part = renderWithFactory(QStringLiteral("MimeTreeParser::MessagePart"), mp); if (part) { htmlWriter->write(part->html()); } } } void DefaultRendererPrivate::render(const EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { const auto metaData = *mp->partMetaData(); if (metaData.isEncrypted || metaData.inProgress) { HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } renderEncrypted(mp, htmlWriter); return; } HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } if (mp->hasSubParts()) { renderSubParts(mp, htmlWriter); } else if (!metaData.inProgress) { auto part = renderWithFactory(QStringLiteral("MimeTreeParser::MessagePart"), mp); if (part) { htmlWriter->write(part->html()); } } } void DefaultRendererPrivate::render(const AlternativeMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } auto mode = mp->preferredMode(); if (mode == MimeTreeParser::Util::MultipartPlain && mp->text().trimmed().isEmpty()) { foreach (const auto m, mp->availableModes()) { if (m != MimeTreeParser::Util::MultipartPlain) { mode = m; break; } } } MimeMessagePart::Ptr part(mp->mChildParts.first()); if (mp->mChildParts.contains(mode)) { part = mp->mChildParts[mode]; } render(part, htmlWriter); } void DefaultRendererPrivate::render(const CertMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { const GpgME::ImportResult &importResult(mp->mImportResult); Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/certmessagepart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; c.insert(QStringLiteral("block"), &block); block.setProperty("importError", QString::fromLocal8Bit(importResult.error().asString())); block.setProperty("nImp", importResult.numImported()); block.setProperty("nUnc", importResult.numUnchanged()); block.setProperty("nSKImp", importResult.numSecretKeysImported()); block.setProperty("nSKUnc", importResult.numSecretKeysUnchanged()); QVariantList keylist; const auto imports = importResult.imports(); auto end(imports.end()); for (auto it = imports.begin(); it != end; ++it) { QObject *key(new QObject(mp.data())); key->setProperty("error", QString::fromLocal8Bit((*it).error().asString())); key->setProperty("status", (*it).status()); key->setProperty("fingerprint", QLatin1String((*it).fingerprint())); keylist << QVariant::fromValue(key); } HTMLBlock::Ptr aBlock; if (mp->isAttachment()) { aBlock = HTMLBlock::Ptr(new AttachmentMarkBlock(htmlWriter, mp->attachmentContent())); } Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); } QSharedPointer DefaultRendererPrivate::renderWithFactory(QString className, const MessagePart::Ptr &msgPart) { if (mRendererFactory) { const auto registry = mRendererFactory->typeRegistry(className); if (registry.size() > 0) { const auto plugin = registry.at(0); return plugin->render(this, msgPart); } } return QSharedPointer(); } QString DefaultRendererPrivate::renderFactory(const MessagePart::Ptr &msgPart, HtmlWriter *_htmlWriter) { const QString className = QString::fromUtf8(msgPart->metaObject()->className()); const auto rendered = renderWithFactory(className, msgPart); if (rendered) { const auto parts = rendered->embededParts(); foreach (auto key, parts.keys()) { _htmlWriter->embedPart(key, parts.value(key)); } foreach (auto entry, rendered->extraHeader()) { _htmlWriter->extraHead(entry); } return rendered->html(); } auto htmlWriter = QSharedPointer(new CacheHtmlWriter(mOldWriter)); if (className == QStringLiteral("MimeTreeParser::MessagePartList")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::MimeMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::EncapsulatedRfc822MessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::HtmlMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::SignedMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::EncryptedMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::AlternativeMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (className == QStringLiteral("MimeTreeParser::CertMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter.data()); } } else if (auto mp = msgPart.dynamicCast()) { return mp->formatOutput(); } else { qCWarning(MESSAGEVIEWER_LOG) << "We got a unkonwn classname, using default behaviour for " << className; } return htmlWriter->html(); } DefaultRenderer::DefaultRenderer(const MimeTreeParser::MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, MimeTreeParser::HtmlWriter *writer) : d(new MimeTreeParser::DefaultRendererPrivate(msgPart, cssHelper, writer, MessagePartRendererFactory::instance())) { } DefaultRenderer::~DefaultRenderer() { delete d; } QString DefaultRenderer::html() const { return d->mHtml; } diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h index b5faa5f1..4699a607 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h @@ -1,73 +1,72 @@ /* Copyright (c) 2016 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __MESSAGEVIEWER_DEFAULTRENDERER_P_H__ #define __MESSAGEVIEWER_DEFAULTRENDERER_P_H__ #include "defaultrenderer.h" #include using namespace MimeTreeParser; using namespace MessageViewer; -class CacheHtmlWriter; class PartRendered; namespace MessageViewer { class MessagePartRendererFactory; class CSSHelperBase; class HtmlWriter; } namespace MimeTreeParser { class DefaultRendererPrivate { public: DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory); ~DefaultRendererPrivate(); QString alignText(); CSSHelperBase *cssHelper() const; Interface::ObjectTreeSource *source() const; void renderSubParts(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter); void render(const MessagePartList::Ptr &mp, HtmlWriter *htmlWriter); void render(const MimeMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const EncapsulatedRfc822MessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const HtmlMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void renderEncrypted(const EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void renderSigned(const SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const AlternativeMessagePart::Ptr &mp, HtmlWriter *htmlWriter); void render(const CertMessagePart::Ptr &mp, HtmlWriter *htmlWriter); QSharedPointer renderWithFactory(QString className, const MessagePart::Ptr &msgPart); QString renderFactory(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter); QString mHtml; MessagePart::Ptr mMsgPart; HtmlWriter *mOldWriter = nullptr; CSSHelperBase *mCSSHelper = nullptr; const MessageViewer::MessagePartRendererFactory *mRendererFactory = nullptr; }; } #endif diff --git a/messageviewer/src/messagepartthemes/default/partrendered.cpp b/messageviewer/src/messagepartthemes/default/partrendered.cpp index b8305478..ec1a05ab 100644 --- a/messageviewer/src/messagepartthemes/default/partrendered.cpp +++ b/messageviewer/src/messagepartthemes/default/partrendered.cpp @@ -1,258 +1,229 @@ /* Copyright (c) 2017 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "partrendered.h" #include "defaultrenderer.h" #include "defaultrenderer_p.h" #include "htmlblock.h" #include "messagepartrenderermanager.h" #include #include #include #include #include #include using namespace MessageViewer; -class CacheHtmlWriter2 : public MimeTreeParser::BufferedHtmlWriter -{ -public: - CacheHtmlWriter2() - { - begin(); - } - ~CacheHtmlWriter2() = default; - - void embedPart(const QByteArray &contentId, const QString &url) override - { - embedParts.insert(contentId, url); - } - - void extraHead(const QString &extra) override - { - head.append(extra); - } - - QString html() - { - end(); - return QString::fromUtf8(data()); - } - - QString head; - QMap embedParts; -}; - PartRendered::PartRendered() { } PartRendered::~PartRendered() { } inline QString PartRendered::alignText() { return QApplication::isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral("ltr"); } QVector > PartRendered::renderSubParts( MimeTreeParser::MessagePart::Ptr mp) { QVector > ret; foreach (const auto &_m, mp->subParts()) { - CacheHtmlWriter2 cacheWriter; + CacheHtmlWriter cacheWriter; DefaultRenderer::Ptr renderer = mp->source()->messagePartTheme(_m); cacheWriter.write(renderer->html()); ret.append(QSharedPointer(new WrapperPartRendered(&cacheWriter))); } return ret; } EmptyPartRendered::EmptyPartRendered() { } EmptyPartRendered::~EmptyPartRendered() { } QString EmptyPartRendered::extraHeader() { return QString(); } QString EmptyPartRendered::html() { return QString(); } QMap EmptyPartRendered::embededParts() { return QMap(); } -WrapperPartRendered::WrapperPartRendered(CacheHtmlWriter2 *htmlWriter) +WrapperPartRendered::WrapperPartRendered(CacheHtmlWriter *htmlWriter) : PartRendered() { mHtml = htmlWriter->html(); mHead = htmlWriter->head; mEmbeded = htmlWriter->embedParts; } WrapperPartRendered::~WrapperPartRendered() { } QString WrapperPartRendered::html() { return mHtml; } QString WrapperPartRendered::extraHeader() { return mHead; } QMap WrapperPartRendered::embededParts() { return mEmbeded; } HtmlOnlyPartRendered::HtmlOnlyPartRendered(MimeTreeParser::MessagePart::Ptr mp, const QString &html) : mHtml(html) { mShowAttachmentBlock = mp->isAttachment(); mAttachmentNode = mp->attachmentContent(); } HtmlOnlyPartRendered::~HtmlOnlyPartRendered() { } QMap HtmlOnlyPartRendered::embededParts() { return QMap(); } QString HtmlOnlyPartRendered::extraHeader() { return QString(); } QString HtmlOnlyPartRendered::html() { MimeTreeParser::AttachmentMarkBlock block(nullptr, mAttachmentNode); QString ret; if (mShowAttachmentBlock) { ret += block.enter(); } ret += mHtml; ret += block.exit(); return ret; } TextPartRendered::TextPartRendered(MimeTreeParser::TextMessagePart::Ptr mp) : mShowAttachmentBlock(false) , mAttachmentNode(nullptr) { auto node = mp->content(); auto nodeHelper = mp->mOtp->nodeHelper(); if (mp->isHidden()) { return; } Grantlee::Template t; Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); QObject block; c.insert(QStringLiteral("block"), &block); block.setProperty("showTextFrame", mp->showTextFrame()); block.setProperty("label", MessageCore::StringUtil::quoteHtmlChars(MimeTreeParser::NodeHelper::fileName( node), true)); block.setProperty("comment", MessageCore::StringUtil::quoteHtmlChars(node->contentDescription()-> asUnicodeString(), true)); block.setProperty("link", nodeHelper->asHREF(node, QStringLiteral("body"))); block.setProperty("showLink", mp->showLink()); block.setProperty("dir", alignText()); t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/textmessagepart.html")); mSubList = renderSubParts(mp); QString content; foreach (auto part, mSubList) { content += part->html(); } c.insert(QStringLiteral("content"), content); mShowAttachmentBlock = mp->isAttachment(); mHtml = t->render(&c); mAttachmentNode = mp->attachmentContent(); } TextPartRendered::~TextPartRendered() { } QMap TextPartRendered::embededParts() { QMap ret; foreach (auto part, mSubList) { //ret += part->embededParts(); } return ret; } QString TextPartRendered::extraHeader() { QString ret; foreach (auto part, mSubList) { ret += part->extraHeader(); } return ret; } QString TextPartRendered::html() { MimeTreeParser::AttachmentMarkBlock block(nullptr, mAttachmentNode); QString ret; if (mShowAttachmentBlock) { ret += block.enter(); } ret += mHtml; ret += block.exit(); return ret; } diff --git a/messageviewer/src/messagepartthemes/default/partrendered.h b/messageviewer/src/messagepartthemes/default/partrendered.h index 7ba5da95..0d75adcd 100644 --- a/messageviewer/src/messagepartthemes/default/partrendered.h +++ b/messageviewer/src/messagepartthemes/default/partrendered.h @@ -1,119 +1,119 @@ /* Copyright (c) 2017 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __MESSAGEVIEWER_PARTRENDERED_H__ #define __MESSAGEVIEWER_PARTRENDERED_H__ +#include "cachehtmlwriter.h" + #include #include #include #include namespace MimeTreeParser { class MessagePart; typedef QSharedPointer MessagePartPtr; class TextMessagePart; typedef QSharedPointer TextMessagePartPtr; class DefaultRendererPrivate; } namespace KMime { class Content; } -class CacheHtmlWriter2; - class PartRendered { public: PartRendered(); virtual ~PartRendered(); inline QString alignText(); virtual QString html() = 0; virtual QMap embededParts() = 0; virtual QString extraHeader() = 0; protected: QVector > renderSubParts(MimeTreeParser::MessagePartPtr mp); }; class EmptyPartRendered : public PartRendered { public: EmptyPartRendered(); virtual ~EmptyPartRendered(); QString html() override; QMap embededParts() override; QString extraHeader() override; }; class WrapperPartRendered : public PartRendered { public: - WrapperPartRendered(CacheHtmlWriter2 *); + WrapperPartRendered(MessageViewer::CacheHtmlWriter*); virtual ~WrapperPartRendered(); QString html() override; QMap embededParts() override; QString extraHeader() override; private: QString mHtml; QString mHead; QMap mEmbeded; }; class HtmlOnlyPartRendered : public PartRendered { public: HtmlOnlyPartRendered(MimeTreeParser::MessagePartPtr part, const QString &html); virtual ~HtmlOnlyPartRendered(); QString html() override; QMap embededParts() override; QString extraHeader() override; protected: void setHtml(const QString &html); private: QString mHtml; bool mShowAttachmentBlock; KMime::Content *mAttachmentNode = nullptr; }; class TextPartRendered : public PartRendered { public: TextPartRendered(MimeTreeParser::TextMessagePartPtr part); virtual ~TextPartRendered(); QString html() override; QMap embededParts() override; QString extraHeader() override; private: QString mHtml; QVector > mSubList; bool mShowAttachmentBlock; KMime::Content *mAttachmentNode = nullptr; }; #endif