diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp index 83638b86..6838e324 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp @@ -1,912 +1,1032 @@ /* 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 "converthtmltoplaintext.h" #include "messagepartrendererbase.h" #include "messagepartrendererfactory.h" #include "htmlblock.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 #include using namespace MimeTreeParser; using namespace MessageViewer; Q_DECLARE_METATYPE(GpgME::DecryptionResult::Recipient) Q_DECLARE_METATYPE(GpgME::Key) 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; } -DefaultRendererPrivate::DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory) +DefaultRendererPrivate::DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory, bool showOnlyOneMimePart) : mMsgPart(msgPart) , mCSSHelper(cssHelper) , mRendererFactory(rendererFactory) + , mShowOnlyOneMimePart(showOnlyOneMimePart) { renderFactory(mMsgPart, writer); } DefaultRendererPrivate::~DefaultRendererPrivate() { } 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()) { 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 = MessagePartRendererManager::self()->loadByName(QStringLiteral(":/encapsulatedrfc822messagepart.html")); Grantlee::Context c = MessagePartRendererManager::self()->createContext(); QObject block; c.insert(QStringLiteral("block"), &block); block.setProperty("link", mp->nodeHelper()->asHREF(mp->mMessage.data(), QStringLiteral("body"))); c.insert(QStringLiteral("msgHeader"), mp->source()->createMessageHeader(mp->mMessage.data())); c.insert(QStringLiteral("content"), QVariant::fromValue([this, mp, htmlWriter](Grantlee::OutputStream *) { renderSubParts(mp, htmlWriter); })); 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->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()) { c.insert(QStringLiteral("content"), QVariant::fromValue([this, mp, htmlWriter](Grantlee::OutputStream *) { HTMLBlock::Ptr rBlock; if (mp->content() && mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter)); } renderSubParts(mp, htmlWriter); })); } else if (!metaData.inProgress) { c.insert(QStringLiteral("content"), QVariant::fromValue([this, mp, htmlWriter](Grantlee::OutputStream *) { renderWithFactory(mp, htmlWriter); })); } c.insert(QStringLiteral("cryptoProto"), QVariant::fromValue(mp->mCryptoProto)); if (!mp->mDecryptRecipients.empty()) { c.insert(QStringLiteral("decryptedRecipients"), QVariant::fromValue(mp->mDecryptRecipients)); } c.insert(QStringLiteral("block"), &block); 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); 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) { c.insert(QStringLiteral("content"), QVariant::fromValue([this, mp, htmlWriter](Grantlee::OutputStream *) { HTMLBlock::Ptr rBlock; if (mp->isRoot()) { rBlock = HTMLBlock::Ptr(new RootBlock(htmlWriter)); } renderSubParts(mp, htmlWriter); })); } else if (!metaData.inProgress) { c.insert(QStringLiteral("content"), QVariant::fromValue([this, mp, htmlWriter](Grantlee::OutputStream *) { renderWithFactory(mp, htmlWriter); })); } c.insert(QStringLiteral("cryptoProto"), QVariant::fromValue(cryptoProto)); c.insert(QStringLiteral("block"), &block); 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) { renderWithFactory(mp, htmlWriter); } } 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) { renderWithFactory(mp, htmlWriter); } } 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); } bool DefaultRendererPrivate::renderWithFactory(const QMetaObject *mo, const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) { if (!mRendererFactory) { return false; } for (auto r : mRendererFactory->renderersForPart(mo, msgPart)) { if (r->render(msgPart, htmlWriter, this)) { return true; } } return false; } void DefaultRendererPrivate::renderFactory(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) { const QString className = QString::fromUtf8(msgPart->metaObject()->className()); if (renderWithFactory(msgPart, htmlWriter)) { return; } if (className == QStringLiteral("MimeTreeParser::MessagePartList")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::MimeMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::EncapsulatedRfc822MessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::HtmlMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::SignedMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::EncryptedMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::AlternativeMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else if (className == QStringLiteral("MimeTreeParser::CertMessagePart")) { auto mp = msgPart.dynamicCast(); if (mp) { render(mp, htmlWriter); } } else { qCWarning(MESSAGEVIEWER_LOG) << "We got a unkonwn classname, using default behaviour for " << className; } } -DefaultRenderer::DefaultRenderer(const MimeTreeParser::MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, MimeTreeParser::HtmlWriter *writer) - : d(new MimeTreeParser::DefaultRendererPrivate(msgPart, cssHelper, writer, MessagePartRendererFactory::instance())) +bool DefaultRendererPrivate::isHiddenHint(const MimeTreeParser::MessagePart::Ptr &msgPart) +{ + auto mp = msgPart.dynamicCast(); + auto content = msgPart->content(); + + if (!mp) { + auto _mp = msgPart.dynamicCast(); + if (_mp) { + return msgPart->nodeHelper()->isNodeDisplayedHidden(content); + } else { + return false; + } + } + + if (mShowOnlyOneMimePart) { + return false; + } + + const AttachmentStrategy *const as = msgPart->source()->attachmentStrategy(); + const bool defaultHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None); + auto preferredMode = source()->preferredMode(); + bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + + QByteArray mediaType("text"); + if (content->contentType(false) && !content->contentType()->mediaType().isEmpty() + && !content->contentType()->subType().isEmpty()) { + mediaType = content->contentType()->mediaType(); + } + const bool isTextPart = (mediaType == QByteArrayLiteral("text")); + + bool defaultAsIcon = true; + if (!mp->neverDisplayInline()) { + if (as) { + defaultAsIcon = as->defaultDisplay(content) == AttachmentStrategy::AsIcon; + } + } + + // neither image nor text -> show as icon + if (!mp->isImage() && !isTextPart) { + defaultAsIcon = true; + } + + bool hidden(false); + if (isTextPart) { + hidden = defaultHidden; + } else { + if (mp->isImage() && isHtmlPreferred + && content->parent() && content->parent()->contentType(false)->subType() == "related") { + hidden = true; + } else { + hidden = defaultHidden && content->parent(); + hidden |= defaultAsIcon && defaultHidden; + } + } + msgPart->nodeHelper()->setNodeDisplayedHidden(content, hidden); + return hidden; +} + +MimeTreeParser::IconType MimeTreeParser::DefaultRendererPrivate::displayHint(const MimeTreeParser::MessagePart::Ptr& msgPart) +{ + auto mp = msgPart.dynamicCast(); + auto content = msgPart->content(); + if (!mp) { + return MimeTreeParser::IconType::NoIcon; + } + const AttachmentStrategy *const as = msgPart->source()->attachmentStrategy(); + const bool defaultHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None); + const bool showOnlyOneMimePart(mShowOnlyOneMimePart); + auto preferredMode = source()->preferredMode(); + bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + + QByteArray mediaType("text"); + if (content->contentType(false) && !content->contentType()->mediaType().isEmpty() + && !content->contentType()->subType().isEmpty()) { + mediaType = content->contentType()->mediaType(); + } + const bool isTextPart = (mediaType == QByteArrayLiteral("text")); + + bool defaultAsIcon = true; + if (!mp->neverDisplayInline()) { + if (as) { + defaultAsIcon = as->defaultDisplay(content) == AttachmentStrategy::AsIcon; + } + } + if (mp->isImage() && showOnlyOneMimePart && !mp->neverDisplayInline()) { + defaultAsIcon = false; + } + + // neither image nor text -> show as icon + if (!mp->isImage() && !isTextPart) { + defaultAsIcon = true; + } + + if (isTextPart) { + if (as && as->defaultDisplay(content) != AttachmentStrategy::Inline) { + return MimeTreeParser::IconExternal; + } + return MimeTreeParser::NoIcon; + } else { + if (mp->isImage() && isHtmlPreferred + && content->parent() && content->parent()->contentType(false)->subType() == "related") { + return MimeTreeParser::IconInline; + } + + if (defaultHidden && !showOnlyOneMimePart && content->parent()) { + return MimeTreeParser::IconInline; + } + + if (defaultAsIcon) { + return MimeTreeParser::IconExternal; + } else if (mp->isImage()) { + return MimeTreeParser::IconInline; + } + } + + return MimeTreeParser::NoIcon; +} + +DefaultRenderer::DefaultRenderer(const MimeTreeParser::MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, MimeTreeParser::HtmlWriter *writer, bool showOnlyOneMimePart) + : d(new MimeTreeParser::DefaultRendererPrivate(msgPart, cssHelper, writer, MessagePartRendererFactory::instance(), showOnlyOneMimePart)) { } DefaultRenderer::~DefaultRenderer() { delete d; } diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer.h b/messageviewer/src/messagepartthemes/default/defaultrenderer.h index 67fb0135..ba68059b 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer.h +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer.h @@ -1,45 +1,45 @@ /* 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_H__ #define __MESSAGEVIEWER_DEFAULTRENDERER_H__ #include namespace MimeTreeParser { class DefaultRendererPrivate; class HtmlWriter; class MessagePart; typedef QSharedPointer MessagePartPtr; } namespace MessageViewer { class CSSHelperBase; class DefaultRenderer { public: - DefaultRenderer(const MimeTreeParser::MessagePartPtr &msgPart, CSSHelperBase *cssHelder, MimeTreeParser::HtmlWriter *writer); + DefaultRenderer(const MimeTreeParser::MessagePartPtr &msgPart, CSSHelperBase *cssHelder, MimeTreeParser::HtmlWriter *writer, bool showOnlyOneMimePart); ~DefaultRenderer(); private: MimeTreeParser::DefaultRendererPrivate *d; }; } #endif //__MIMETREEPARSER_MAILRENDERER_H__ diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h index acb8a6ae..a2fe7acc 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h @@ -1,68 +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 "messagepartrendererbase.h" #include using namespace MimeTreeParser; using namespace MessageViewer; namespace MessageViewer { class MessagePartRendererFactory; class CSSHelperBase; class HtmlWriter; } namespace MimeTreeParser { class DefaultRendererPrivate : public MessageViewer::RenderContext { public: - DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory); + DefaultRendererPrivate(const MessagePart::Ptr &msgPart, CSSHelperBase *cssHelper, HtmlWriter *writer, const MessagePartRendererFactory *rendererFactory, bool showOnlyOneMimePart); ~DefaultRendererPrivate(); CSSHelperBase *cssHelper() const override; Interface::ObjectTreeSource *source() const; void renderSubParts(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) override; 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); bool renderWithFactory(const QMetaObject *mo, const MessagePart::Ptr &msgPart, HtmlWriter *writer) override; using RenderContext::renderWithFactory; void renderFactory(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter); + bool isHiddenHint(const MimeTreeParser::MessagePart::Ptr &msgPart) override; + MimeTreeParser::IconType displayHint(const MimeTreeParser::MessagePart::Ptr &msgPart) override; + + bool mShowOnlyOneMimePart; MessagePart::Ptr mMsgPart; CSSHelperBase *mCSSHelper = nullptr; const MessageViewer::MessagePartRendererFactory *mRendererFactory = nullptr; }; } #endif diff --git a/messageviewer/src/messagepartthemes/default/messagepartrendererbase.h b/messageviewer/src/messagepartthemes/default/messagepartrendererbase.h index 33d725b9..809db871 100644 --- a/messageviewer/src/messagepartthemes/default/messagepartrendererbase.h +++ b/messageviewer/src/messagepartthemes/default/messagepartrendererbase.h @@ -1,81 +1,84 @@ /* Copyright (c) 2017 Sandro Knauß KMail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. KMail 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __MESSAGEVIEWER_MESSAGEPARTRENDERERBASE_H__ #define __MESSAGEVIEWER_MESSAGEPARTRENDERERBASE_H__ #include "messageviewer_export.h" #include #include #include #include namespace MimeTreeParser { class HtmlWriter; } namespace MessageViewer { class CSSHelperBase; class MESSAGEVIEWER_EXPORT RenderContext { public: virtual ~RenderContext(); virtual CSSHelperBase *cssHelper() const = 0; template inline bool renderWithFactory(const MimeTreeParser::MessagePart::Ptr &msgPart, MimeTreeParser::HtmlWriter *writer) { return renderWithFactory(&T::staticMetaObject, msgPart, writer); } inline bool renderWithFactory(const MimeTreeParser::MessagePart::Ptr &msgPart, MimeTreeParser::HtmlWriter *writer) { return renderWithFactory(msgPart->metaObject(), msgPart, writer); } virtual void renderSubParts(const MimeTreeParser::MessagePart::Ptr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter) = 0; + virtual bool isHiddenHint(const MimeTreeParser::MessagePart::Ptr &msgPart) = 0; + virtual MimeTreeParser::IconType displayHint(const MimeTreeParser::MessagePart::Ptr &msgPart) = 0; + protected: virtual bool renderWithFactory(const QMetaObject *mo, const MimeTreeParser::MessagePart::Ptr &msgPart, MimeTreeParser::HtmlWriter *writer) = 0; }; class MESSAGEVIEWER_EXPORT MessagePartRendererBase { public: MessagePartRendererBase(); virtual ~MessagePartRendererBase(); virtual bool render(const MimeTreeParser::MessagePart::Ptr &, MimeTreeParser::HtmlWriter *htmlWriter, RenderContext *context) const = 0; }; } #endif diff --git a/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp b/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp index 2b509182..d3607b3d 100644 --- a/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp @@ -1,85 +1,85 @@ /* 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 "attachmentmessagepartrenderer.h" #include "quotehtml.h" #include "utils/mimetype.h" #include "../defaultrenderer_p.h" #include "../messagepartrenderermanager.h" #include #include #include #include #include using namespace MessageViewer; AttachmentMessagePartRenderer::AttachmentMessagePartRenderer() { } AttachmentMessagePartRenderer::~AttachmentMessagePartRenderer() { } bool AttachmentMessagePartRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, RenderContext *context) const { auto mp = msgPart.dynamicCast(); if (!mp) { return false; } KMime::Content *node = mp->content(); - if (mp->isHidden()) { + if (context->isHiddenHint(msgPart)) { return true; } - const auto tmpAsIcon = mp->asIcon(); + const auto tmpAsIcon = context->displayHint(msgPart); if (tmpAsIcon == MimeTreeParser::NoIcon) { return context->renderWithFactory(mp, htmlWriter); } Grantlee::Template t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral( ":/asiconpart.html")); Grantlee::Context c = MessageViewer::MessagePartRendererManager::self()->createContext(); c.insert(QStringLiteral("block"), msgPart.data()); msgPart->setProperty("inline", (tmpAsIcon == MimeTreeParser::IconInline)); QString iconPath; if (tmpAsIcon == MimeTreeParser::IconInline) { iconPath = mp->temporaryFilePath(); } else { iconPath = MessageViewer::Util::iconPathForContent(node, KIconLoader::Desktop); if (iconPath.right(14) == QLatin1String("mime_empty.png")) { NodeHelper::magicSetType(node); iconPath = MessageViewer::Util::iconPathForContent(node, KIconLoader::Desktop); } } msgPart->setProperty("iconPath", QUrl::fromLocalFile(iconPath).url()); Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); return true; } diff --git a/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp b/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp index beeba072..09d28bb7 100644 --- a/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp @@ -1,66 +1,66 @@ /* 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 "textmessagepartrenderer.h" #include "quotehtml.h" #include "../defaultrenderer_p.h" #include "../messagepartrenderermanager.h" #include #include #include #include using namespace MessageViewer; TextMessagePartRenderer::TextMessagePartRenderer() { } TextMessagePartRenderer::~TextMessagePartRenderer() { } bool TextMessagePartRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, RenderContext *context) const { Q_UNUSED(context); auto mp = msgPart.dynamicCast(); if (!mp) { return false; } - if (mp->isHidden()) { + if (context->isHiddenHint(msgPart)) { return true; } Grantlee::Template t; Grantlee::Context c = MessagePartRendererManager::self()->createContext(); c.insert(QStringLiteral("block"), msgPart.data()); c.insert(QStringLiteral("content"), QVariant::fromValue([mp, htmlWriter, context](Grantlee::OutputStream *) { context->renderSubParts(mp, htmlWriter); })); t = MessagePartRendererManager::self()->loadByName(QStringLiteral(":/textmessagepart.html")); Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); return true; } diff --git a/messageviewer/src/viewer/objecttreeemptysource.cpp b/messageviewer/src/viewer/objecttreeemptysource.cpp index b1eff831..9b7144e2 100644 --- a/messageviewer/src/viewer/objecttreeemptysource.cpp +++ b/messageviewer/src/viewer/objecttreeemptysource.cpp @@ -1,146 +1,146 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "objecttreeemptysource.h" #include "viewer/viewer_p.h" #include "viewer/csshelperbase.h" #include #include #include #include "messagepartthemes/default/defaultrenderer.h" using namespace MessageViewer; namespace MessageViewer { class EmptySourcePrivate { public: EmptySourcePrivate() : mAllowDecryption(false) { } bool mAllowDecryption; }; } EmptySource::EmptySource() : MimeTreeParser::Interface::ObjectTreeSource() , d(new MessageViewer::EmptySourcePrivate) { } EmptySource::~EmptySource() { delete d; } bool EmptySource::decryptMessage() const { return d->mAllowDecryption; } bool EmptySource::htmlLoadExternal() const { return false; } bool EmptySource::showSignatureDetails() const { return false; } void EmptySource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) { Q_UNUSED(mode); Q_UNUSED(availableModes); } MimeTreeParser::Util::HtmlMode EmptySource::preferredMode() const { return MimeTreeParser::Util::Html; } void EmptySource::setAllowDecryption(bool allowDecryption) { d->mAllowDecryption = allowDecryption; } int EmptySource::levelQuote() const { return 1; } const QTextCodec *EmptySource::overrideCodec() { return nullptr; } QString EmptySource::createMessageHeader(KMime::Message *message) { Q_UNUSED(message); return QString(); //do nothing } const MimeTreeParser::AttachmentStrategy *EmptySource::attachmentStrategy() { return MimeTreeParser::AttachmentStrategy::smart(); } MimeTreeParser::HtmlWriter *EmptySource::htmlWriter() { return nullptr; } CSSHelperBase *EmptySource::cssHelper() { return nullptr; } bool EmptySource::autoImportKeys() const { return true; } bool EmptySource::showEmoticons() const { return false; } bool EmptySource::showExpandQuotesMark() const { return false; } const MimeTreeParser::BodyPartFormatterFactory *EmptySource::bodyPartFormatterFactory() { return MimeTreeParser::BodyPartFormatterFactory::instance(); } bool EmptySource::isPrinting() const { return false; } -void EmptySource::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter) +void EmptySource::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, bool showOnlyOneMimePart) { - DefaultRenderer(msgPart, cssHelper(), htmlWriter); + DefaultRenderer(msgPart, cssHelper(), htmlWriter, showOnlyOneMimePart); } diff --git a/messageviewer/src/viewer/objecttreeemptysource.h b/messageviewer/src/viewer/objecttreeemptysource.h index 5df9671e..3d498de6 100644 --- a/messageviewer/src/viewer/objecttreeemptysource.h +++ b/messageviewer/src/viewer/objecttreeemptysource.h @@ -1,61 +1,61 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H #define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H #include #include "messageviewer_export.h" class QString; namespace MessageViewer { class CSSHelperBase; /** An ObjectTreeSource that does not work on anything */ class EmptySourcePrivate; class MESSAGEVIEWER_EXPORT EmptySource : public MimeTreeParser::Interface::ObjectTreeSource { public: EmptySource(); ~EmptySource(); bool decryptMessage() const override; bool htmlLoadExternal() const override; bool showSignatureDetails() const override; void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) override; MimeTreeParser::Util::HtmlMode preferredMode() const override; void setAllowDecryption(bool allowDecryption); int levelQuote() const override; const QTextCodec *overrideCodec() override; QString createMessageHeader(KMime::Message *message) override; const MimeTreeParser::AttachmentStrategy *attachmentStrategy() override; MimeTreeParser::HtmlWriter *htmlWriter() override; virtual CSSHelperBase *cssHelper(); bool autoImportKeys() const override; bool showEmoticons() const override; bool showExpandQuotesMark() const override; const MimeTreeParser::BodyPartFormatterFactory *bodyPartFormatterFactory() override; - void render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter) override; + void render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, bool showOnlyOneMimePart) override; bool isPrinting() const override; private: EmptySourcePrivate *const d; }; } #endif diff --git a/messageviewer/src/viewer/objecttreeviewersource.cpp b/messageviewer/src/viewer/objecttreeviewersource.cpp index dbb36d7c..2e47e85a 100644 --- a/messageviewer/src/viewer/objecttreeviewersource.cpp +++ b/messageviewer/src/viewer/objecttreeviewersource.cpp @@ -1,135 +1,135 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "objecttreeviewersource.h" #include "csshelper.h" #include #include #include "messagepartthemes/default/defaultrenderer.h" #include "viewer/viewer_p.h" #include "widgets/htmlstatusbar.h" #include "settings/messageviewersettings.h" using namespace MessageViewer; MailViewerSource::MailViewerSource(ViewerPrivate *viewer) : MimeTreeParser::Interface::ObjectTreeSource() , mViewer(viewer) { } MailViewerSource::~MailViewerSource() { } bool MailViewerSource::decryptMessage() const { return mViewer->decryptMessage(); } bool MailViewerSource::htmlLoadExternal() const { return mViewer->htmlLoadExternal(); } bool MailViewerSource::showSignatureDetails() const { return mViewer->mShowSignatureDetails; } void MailViewerSource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) { mViewer->mColorBar->setAvailableModes(availableModes); mViewer->mColorBar->setMode(mode); } MimeTreeParser::Util::HtmlMode MailViewerSource::preferredMode() const { switch (mViewer->displayFormatMessageOverwrite()) { case MessageViewer::Viewer::UseGlobalSetting: case MessageViewer::Viewer::Unknown: return mViewer->htmlMailGlobalSetting() ? MimeTreeParser::Util::Html : MimeTreeParser::Util ::Normal; case MessageViewer::Viewer::Html: return MimeTreeParser::Util::MultipartHtml; case MessageViewer::Viewer::Text: return MimeTreeParser::Util::MultipartPlain; case MessageViewer::Viewer::ICal: return MimeTreeParser::Util::MultipartIcal; } Q_ASSERT(true); return MimeTreeParser::Util::Html; } int MailViewerSource::levelQuote() const { return mViewer->mLevelQuote; } const QTextCodec *MailViewerSource::overrideCodec() { return mViewer->overrideCodec(); } QString MailViewerSource::createMessageHeader(KMime::Message *message) { return mViewer->writeMsgHeader(message); } const MimeTreeParser::AttachmentStrategy *MailViewerSource::attachmentStrategy() { return mViewer->attachmentStrategy(); } MimeTreeParser::HtmlWriter *MailViewerSource::htmlWriter() { return mViewer->htmlWriter(); } bool MailViewerSource::autoImportKeys() const { return MessageViewer::MessageViewerSettings::self()->autoImportKeys(); } bool MailViewerSource::showEmoticons() const { return mViewer->showEmoticons(); } bool MailViewerSource::showExpandQuotesMark() const { return MessageViewer::MessageViewerSettings::self()->showExpandQuotesMark(); } const MimeTreeParser::BodyPartFormatterFactory *MailViewerSource::bodyPartFormatterFactory() { return MimeTreeParser::BodyPartFormatterFactory::instance(); } bool MailViewerSource::isPrinting() const { return mViewer->mPrinting; } -void MailViewerSource::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter) +void MailViewerSource::render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, bool showOnlyOneMimePart) { - DefaultRenderer(msgPart, mViewer->cssHelper(), htmlWriter); + DefaultRenderer(msgPart, mViewer->cssHelper(), htmlWriter, showOnlyOneMimePart); } diff --git a/messageviewer/src/viewer/objecttreeviewersource.h b/messageviewer/src/viewer/objecttreeviewersource.h index 726b848b..d8961795 100644 --- a/messageviewer/src/viewer/objecttreeviewersource.h +++ b/messageviewer/src/viewer/objecttreeviewersource.h @@ -1,58 +1,58 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAILVIEWER_OBJECTTREEVIEWERSOURCE_H #define MAILVIEWER_OBJECTTREEVIEWERSOURCE_H #include class QString; namespace MessageViewer { class ViewerPrivate; /** An ObjectTreeParser source working on a MailViewer object */ class MailViewerSource : public MimeTreeParser::Interface::ObjectTreeSource { public: explicit MailViewerSource(ViewerPrivate *viewer); ~MailViewerSource(); bool decryptMessage() const override; bool htmlLoadExternal() const override; bool showSignatureDetails() const override; void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) override; MimeTreeParser::Util::HtmlMode preferredMode() const override; int levelQuote() const override; const QTextCodec *overrideCodec() override; QString createMessageHeader(KMime::Message *message) override; const MimeTreeParser::AttachmentStrategy *attachmentStrategy() override; MimeTreeParser::HtmlWriter *htmlWriter() override; bool autoImportKeys() const override; bool showEmoticons() const override; bool showExpandQuotesMark() const override; const MimeTreeParser::BodyPartFormatterFactory *bodyPartFormatterFactory() override; - void render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter) override; + void render(const MimeTreeParser::MessagePartPtr &msgPart, MimeTreeParser::HtmlWriter *htmlWriter, bool showOnlyOneMimePart) override; bool isPrinting() const override; private: ViewerPrivate *mViewer = nullptr; }; } #endif diff --git a/mimetreeparser/autotests/setupenv.h b/mimetreeparser/autotests/setupenv.h index 02803ce4..d08e614f 100644 --- a/mimetreeparser/autotests/setupenv.h +++ b/mimetreeparser/autotests/setupenv.h @@ -1,174 +1,175 @@ /* Copyright (C) 2010 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Copyright (c) 2010 Leo Franchi 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 MESSAGECORE_TESTS_UTIL_H #define MESSAGECORE_TESTS_UTIL_H #include #include #include #include #include #include namespace MimeTreeParser { namespace Test { /** * setup a environment variables for tests: * * set LC_ALL to C * * set KDEHOME */ void setupEnv(); // We can't use EmptySource, since we need to control some emelnets of the source for tests to also test // loadExternal and htmlMail. class TestObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource { public: TestObjectTreeSource(MimeTreeParser::HtmlWriter *writer) : mWriter(writer) , mAttachmentStrategy(QStringLiteral("smart")) , mPreferredMode(Util::Html) , mHtmlLoadExternal(false) , mDecryptMessage(false) { } MimeTreeParser::HtmlWriter *htmlWriter() override { return mWriter; } bool htmlLoadExternal() const override { return mHtmlLoadExternal; } void setHtmlLoadExternal(bool loadExternal) { mHtmlLoadExternal = loadExternal; } void setAttachmentStrategy(QString strategy) { mAttachmentStrategy = strategy; } const AttachmentStrategy *attachmentStrategy() override { return AttachmentStrategy::create(mAttachmentStrategy); } bool autoImportKeys() const override { return true; } bool showEmoticons() const override { return false; } bool showExpandQuotesMark() const override { return false; } const BodyPartFormatterFactory *bodyPartFormatterFactory() override { return &mBodyPartFormatterFactory; } bool decryptMessage() const override { return mDecryptMessage; } void setAllowDecryption(bool allowDecryption) { mDecryptMessage = allowDecryption; } void setShowSignatureDetails(bool showSignatureDetails) { mShowSignatureDetails = showSignatureDetails; } bool showSignatureDetails() const override { return mShowSignatureDetails; } void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) override { Q_UNUSED(mode); Q_UNUSED(availableModes); } MimeTreeParser::Util::HtmlMode preferredMode() const override { return mPreferredMode; } void setPreferredMode(MimeTreeParser::Util::HtmlMode mode) { mPreferredMode = mode; } int levelQuote() const override { return 1; } const QTextCodec *overrideCodec() override { return nullptr; } QString createMessageHeader(KMime::Message *message) override { Q_UNUSED(message); return QString(); //do nothing } - void render(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) override + void render(const MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter, bool showOnlyOneMimePart) override { Q_UNUSED(msgPart); Q_UNUSED(htmlWriter); + Q_UNUSED(showOnlyOneMimePart); } bool isPrinting() const override { return false; } private: MimeTreeParser::HtmlWriter *mWriter = nullptr; QString mAttachmentStrategy; BodyPartFormatterFactory mBodyPartFormatterFactory; MimeTreeParser::Util::HtmlMode mPreferredMode; bool mHtmlLoadExternal = false; bool mDecryptMessage = false; bool mShowSignatureDetails = false; }; } } #endif diff --git a/mimetreeparser/src/interfaces/objecttreesource.h b/mimetreeparser/src/interfaces/objecttreesource.h index 96cde1a8..625ade2a 100644 --- a/mimetreeparser/src/interfaces/objecttreesource.h +++ b/mimetreeparser/src/interfaces/objecttreesource.h @@ -1,101 +1,101 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__ #define __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__ #include "mimetreeparser_export.h" #include #include #include class QTextCodec; namespace MimeTreeParser { class HtmlWriter; class AttachmentStrategy; class BodyPartFormatterFactory; class MessagePart; typedef QSharedPointer MessagePartPtr; } namespace MimeTreeParser { namespace Interface { /** * Interface for object tree sources. * @author Andras Mantia */ class MIMETREEPARSER_EXPORT ObjectTreeSource { public: ObjectTreeSource(); virtual ~ObjectTreeSource(); /** * Sets the type of mail that is currently displayed. Applications can display this * information to the user, for example KMail displays a HTML status bar. * Note: This is not called when the mode is "Normal". */ virtual void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) = 0; /** Return the mode that is the preferred to display */ virtual MimeTreeParser::Util::HtmlMode preferredMode() const = 0; /** Return true if an encrypted mail should be decrypted */ virtual bool decryptMessage() const = 0; /** Return true if external sources should be loaded in a html mail */ virtual bool htmlLoadExternal() const = 0; /** Return true to include the signature details in the generated html */ virtual bool showSignatureDetails() const = 0; virtual int levelQuote() const = 0; /** The override codec that should be used for the mail */ virtual const QTextCodec *overrideCodec() = 0; virtual QString createMessageHeader(KMime::Message *message) = 0; /** Return the wanted attachment startegy */ virtual const AttachmentStrategy *attachmentStrategy() = 0; /** Return the html write object */ virtual HtmlWriter *htmlWriter() = 0; /** should keys be imported automatically **/ virtual bool autoImportKeys() const = 0; virtual bool showEmoticons() const = 0; virtual bool showExpandQuotesMark() const = 0; virtual const BodyPartFormatterFactory *bodyPartFormatterFactory() = 0; - virtual void render(const MessagePartPtr &msgPart, HtmlWriter *htmlWriter) = 0; + virtual void render(const MessagePartPtr &msgPart, HtmlWriter *htmlWriter, bool showOnlyOneMimePart) = 0; virtual bool isPrinting() const = 0; private: Q_DISABLE_COPY(ObjectTreeSource) }; } } #endif diff --git a/mimetreeparser/src/messagepart.cpp b/mimetreeparser/src/messagepart.cpp index e38663f5..103bf44f 100644 --- a/mimetreeparser/src/messagepart.cpp +++ b/mimetreeparser/src/messagepart.cpp @@ -1,1423 +1,1323 @@ /* Copyright (c) 2015 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 "messagepart.h" #include "mimetreeparser_debug.h" #include "attachmentstrategy.h" #include "cryptohelper.h" #include "objecttreeparser.h" #include "job/qgpgmejobexecutor.h" #include "memento/cryptobodypartmemento.h" #include "memento/decryptverifybodypartmemento.h" #include "memento/verifydetachedbodypartmemento.h" #include "memento/verifyopaquebodypartmemento.h" #include "bodyformatter/utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace MimeTreeParser; //------MessagePart----------------------- namespace MimeTreeParser { class MessagePartPrivate { public: MessagePart *mParentPart = nullptr; QVector mBlocks; KMime::Content *mNode = nullptr; KMime::Content *mAttachmentNode = nullptr; QString mText; PartMetaData mMetaData; bool mRoot = false; }; } MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text) : mOtp(otp) , d(new MessagePartPrivate) { d->mText = text; } MessagePart::~MessagePart() = default; MessagePart *MessagePart::parentPart() const { return d->mParentPart; } void MessagePart::setParentPart(MessagePart *parentPart) { d->mParentPart = parentPart; } QString MessagePart::htmlContent() const { return text(); } QString MessagePart::plaintextContent() const { return text(); } PartMetaData *MessagePart::partMetaData() const { return &d->mMetaData; } Interface::BodyPartMemento *MessagePart::memento() const { return nodeHelper()->bodyPartMemento(content(), "__plugin__"); } void MessagePart::setMemento(Interface::BodyPartMemento *memento) { nodeHelper()->setBodyPartMemento(content(), "__plugin__", memento); } KMime::Content *MessagePart::content() const { return d->mNode; } void MessagePart::setContent(KMime::Content *node) { d->mNode = node; } KMime::Content *MessagePart::attachmentContent() const { return d->mAttachmentNode; } void MessagePart::setAttachmentContent(KMime::Content *node) { d->mAttachmentNode = node; } bool MessagePart::isAttachment() const { return d->mAttachmentNode; } QString MessagePart::attachmentIndex() const { return attachmentContent()->index().toString(); } QString MessagePart::attachmentLink() const { return mOtp->nodeHelper()->asHREF(content(), QStringLiteral("body")); } QString MessagePart::makeLink(const QString &path) const { // FIXME: use a PRNG for the first arg, instead of a serial number static int serial = 0; if (path.isEmpty()) { return {}; } return QStringLiteral("x-kmail:/bodypart/%1/%2/%3") .arg(serial++).arg(content()->index().toString()) .arg(QString::fromLatin1(QUrl::toPercentEncoding(path, "/"))); } void MessagePart::setIsRoot(bool root) { d->mRoot = root; } bool MessagePart::isRoot() const { return d->mRoot; } QString MessagePart::text() const { return d->mText; } void MessagePart::setText(const QString &text) { d->mText = text; } bool MessagePart::isHtml() const { return false; } bool MessagePart::isHidden() const { return false; } Interface::ObjectTreeSource *MessagePart::source() const { Q_ASSERT(mOtp); return mOtp->mSource; } NodeHelper *MessagePart::nodeHelper() const { Q_ASSERT(mOtp); return mOtp->nodeHelper(); } void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart) { auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart); d->mRoot = subMessagePart->isRoot(); foreach (const auto &part, subMessagePart->subParts()) { appendSubPart(part); } } QString MessagePart::renderInternalText() const { QString text; foreach (const auto &mp, subParts()) { text += mp->text(); } return text; } void MessagePart::fix() const { foreach (const auto &mp, subParts()) { const auto m = mp.dynamicCast(); if (m) { m->fix(); } } } void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart) { messagePart->setParentPart(this); d->mBlocks.append(messagePart); } const QVector &MessagePart::subParts() const { return d->mBlocks; } bool MessagePart::hasSubParts() const { return !d->mBlocks.isEmpty(); } void MessagePart::clearSubParts() { d->mBlocks.clear(); } //-----MessagePartList---------------------- MessagePartList::MessagePartList(ObjectTreeParser *otp) : MessagePart(otp, QString()) { } MessagePartList::~MessagePartList() { } QString MessagePartList::text() const { return renderInternalText(); } QString MessagePartList::plaintextContent() const { return QString(); } QString MessagePartList::htmlContent() const { return QString(); } //-----TextMessageBlock---------------------- TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool decryptMessage) : MessagePartList(otp) , mDrawFrame(drawFrame) , mDecryptMessage(decryptMessage) , mIsHidden(false) { if (!node) { qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; return; } setContent(node); mIsHidden = mOtp->nodeHelper()->isNodeDisplayedHidden(node); parseContent(); } TextMessagePart::~TextMessagePart() { } bool TextMessagePart::decryptMessage() const { return mDecryptMessage; } void TextMessagePart::parseContent() { const auto aCodec = mOtp->codecFor(content()); const QString &fromAddress = mOtp->nodeHelper()->fromAsString(content()); mSignatureState = KMMsgNotSigned; mEncryptionState = KMMsgNotEncrypted; const auto blocks = prepareMessageForDecryption(content()->decodedContent()); const auto cryptProto = QGpgME::openpgp(); if (!blocks.isEmpty()) { /* The (overall) signature/encrypted status is broken * if one unencrypted part is at the beginning or in the middle * because mailmain adds an unencrypted part at the end this should not break the overall status * * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than * the status is set to unencryped */ bool fullySignedOrEncrypted = true; bool fullySignedOrEncryptedTmp = true; for (const auto &block : blocks) { if (!fullySignedOrEncryptedTmp) { fullySignedOrEncrypted = false; } if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) { fullySignedOrEncryptedTmp = false; appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(block.text())))); } else if (block.type() == PgpMessageBlock) { EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); mp->setDecryptMessage(decryptMessage()); mp->setIsEncrypted(true); appendSubPart(mp); if (!decryptMessage()) { continue; } mp->startDecryption(block.text(), aCodec); if (mp->partMetaData()->inProgress) { continue; } } else if (block.type() == ClearsignedBlock) { SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); appendSubPart(mp); mp->startVerification(block.text(), aCodec); } else { continue; } const auto mp = subParts().last().staticCast(); const PartMetaData *messagePart(mp->partMetaData()); if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { mp->setText(aCodec->toUnicode(block.text())); } if (messagePart->isEncrypted) { mEncryptionState = KMMsgPartiallyEncrypted; } if (messagePart->isSigned) { mSignatureState = KMMsgPartiallySigned; } } //Do we have an fully Signed/Encrypted Message? if (fullySignedOrEncrypted) { if (mSignatureState == KMMsgPartiallySigned) { mSignatureState = KMMsgFullySigned; } if (mEncryptionState == KMMsgPartiallyEncrypted) { mEncryptionState = KMMsgFullyEncrypted; } } } } KMMsgEncryptionState TextMessagePart::encryptionState() const { return mEncryptionState; } KMMsgSignatureState TextMessagePart::signatureState() const { return mSignatureState; } bool TextMessagePart::isHidden() const { return mIsHidden; } bool TextMessagePart::showLink() const { return !temporaryFilePath().isEmpty(); } bool TextMessagePart::showTextFrame() const { return mDrawFrame; } void TextMessagePart::setShowTextFrame(bool showFrame) { mDrawFrame = showFrame; } QString TextMessagePart::label() const { const QString name = content()->contentType()->name(); QString label = name.isEmpty() ? NodeHelper::fileName(content()) : name; if (label.isEmpty()) { label = i18nc("display name for an unnamed attachment", "Unnamed"); } return label; } QString TextMessagePart::comment() const { const QString comment = content()->contentDescription()->asUnicodeString(); if (comment == label()) { return {}; } return comment; } QString TextMessagePart::temporaryFilePath() const { return nodeHelper()->writeNodeToTempFile(content()); } //-----AttachmentMessageBlock---------------------- AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool decryptMessage) : TextMessagePart(otp, node, drawFrame, decryptMessage) , mIsImage(false) , mNeverDisplayInline(false) { } AttachmentMessagePart::~AttachmentMessagePart() { } bool AttachmentMessagePart::neverDisplayInline() const { return mNeverDisplayInline; } void AttachmentMessagePart::setNeverDisplayInline(bool displayInline) { mNeverDisplayInline = displayInline; } bool AttachmentMessagePart::isImage() const { return mIsImage; } void AttachmentMessagePart::setIsImage(bool image) { mIsImage = image; } -IconType AttachmentMessagePart::asIcon() const -{ - const AttachmentStrategy *const as = mOtp->attachmentStrategy(); - const bool defaultHidden(as && as->defaultDisplay(content()) == AttachmentStrategy::None); - const bool showOnlyOneMimePart(mOtp->showOnlyOneMimePart()); - auto preferredMode = source()->preferredMode(); - bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); - - QByteArray mediaType("text"); - if (content()->contentType(false) && !content()->contentType()->mediaType().isEmpty() - && !content()->contentType()->subType().isEmpty()) { - mediaType = content()->contentType()->mediaType(); - } - const bool isTextPart = (mediaType == QByteArrayLiteral("text")); - - bool defaultAsIcon = true; - if (!neverDisplayInline()) { - if (as) { - defaultAsIcon = as->defaultDisplay(content()) == AttachmentStrategy::AsIcon; - } - } - if (isImage() && showOnlyOneMimePart && !neverDisplayInline()) { - defaultAsIcon = false; - } - - // neither image nor text -> show as icon - if (!isImage() && !isTextPart) { - defaultAsIcon = true; - } - - if (isTextPart) { - if (as && as->defaultDisplay(content()) != AttachmentStrategy::Inline) { - return MimeTreeParser::IconExternal; - } - return MimeTreeParser::NoIcon; - } else { - if (isImage() && isHtmlPreferred - && content()->parent() && content()->parent()->contentType()->subType() == "related") { - return MimeTreeParser::IconInline; - } - - if (defaultHidden && !showOnlyOneMimePart && content()->parent()) { - return MimeTreeParser::IconInline; - } - - if (defaultAsIcon) { - return MimeTreeParser::IconExternal; - } else if (isImage()) { - return MimeTreeParser::IconInline; - } else { - return MimeTreeParser::NoIcon; - } - } -} - -bool AttachmentMessagePart::isHidden() const -{ - if (mOtp->showOnlyOneMimePart()) { - return false; // never hide when only showing one part, otherwise you'll see nothing - } - const AttachmentStrategy *const as = mOtp->attachmentStrategy(); - const bool defaultHidden(as && as->defaultDisplay(content()) == AttachmentStrategy::None); - auto preferredMode = source()->preferredMode(); - bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); - - QByteArray mediaType("text"); - if (content()->contentType(false) && !content()->contentType()->mediaType().isEmpty() - && !content()->contentType()->subType().isEmpty()) { - mediaType = content()->contentType()->mediaType(); - } - const bool isTextPart = (mediaType == QByteArrayLiteral("text")); - - bool defaultAsIcon = true; - if (!neverDisplayInline()) { - if (as) { - defaultAsIcon = as->defaultDisplay(content()) == AttachmentStrategy::AsIcon; - } - } - - // neither image nor text -> show as icon - if (!isImage() && !isTextPart) { - defaultAsIcon = true; - } - - bool hidden(false); - if (isTextPart) { - hidden = defaultHidden; - } else { - if (isImage() && isHtmlPreferred - && content()->parent() && content()->parent()->contentType()->subType() == "related") { - hidden = true; - } else { - hidden = defaultHidden && content()->parent(); - hidden |= defaultAsIcon && defaultHidden; - } - } - mOtp->nodeHelper()->setNodeDisplayedHidden(content(), hidden); - return hidden; -} - //-----HtmlMessageBlock---------------------- HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node, Interface::ObjectTreeSource *source) : MessagePart(otp, QString()) , mSource(source) { if (!node) { qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; return; } setContent(node); const QByteArray partBody(node->decodedContent()); mBodyHTML = mOtp->codecFor(node)->toUnicode(partBody); mCharset = NodeHelper::charset(node); } HtmlMessagePart::~HtmlMessagePart() { } void HtmlMessagePart::fix() const { mOtp->mHtmlContent += mBodyHTML; mOtp->mHtmlContentCharset = mCharset; } QString HtmlMessagePart::text() const { return mBodyHTML; } bool HtmlMessagePart::isHtml() const { return true; } //-----MimeMessageBlock---------------------- MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart) : MessagePart(otp, QString()) , mOnlyOneMimePart(onlyOneMimePart) { if (!node) { qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; return; } setContent(node); parseInternal(node, mOnlyOneMimePart); } MimeMessagePart::~MimeMessagePart() { } QString MimeMessagePart::text() const { return renderInternalText(); } QString MimeMessagePart::plaintextContent() const { return QString(); } QString MimeMessagePart::htmlContent() const { return QString(); } //-----AlternativeMessagePart---------------------- AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode) : MessagePart(otp, QString()) , mPreferredMode(preferredMode) { setContent(node); KMime::Content *dataIcal = findTypeInDirectChilds(node, "text/calendar"); KMime::Content *dataHtml = findTypeInDirectChilds(node, "text/html"); KMime::Content *dataText = findTypeInDirectChilds(node, "text/plain"); if (!dataHtml) { // If we didn't find the HTML part as the first child of the multipart/alternative, it might // be that this is a HTML message with images, and text/plain and multipart/related are the // immediate children of this multipart/alternative node. // In this case, the HTML node is a child of multipart/related. dataHtml = findTypeInDirectChilds(node, "multipart/related"); // Still not found? Stupid apple mail actually puts the attachments inside of the // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed // here. // Do this only when prefering HTML mail, though, since otherwise the attachments are hidden // when displaying plain text. if (!dataHtml) { dataHtml = findTypeInDirectChilds(node, "multipart/mixed"); } } if (dataIcal) { mChildNodes[Util::MultipartIcal] = dataIcal; } if (dataText) { mChildNodes[Util::MultipartPlain] = dataText; } if (dataHtml) { mChildNodes[Util::MultipartHtml] = dataHtml; } if (mChildNodes.isEmpty()) { qCWarning(MIMETREEPARSER_LOG) << "no valid nodes"; return; } QMapIterator i(mChildNodes); while (i.hasNext()) { i.next(); mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true)); } } AlternativeMessagePart::~AlternativeMessagePart() { } Util::HtmlMode AlternativeMessagePart::preferredMode() const { return mPreferredMode; } QList AlternativeMessagePart::availableModes() { return mChildParts.keys(); } QString AlternativeMessagePart::text() const { if (mChildParts.contains(Util::MultipartPlain)) { return mChildParts[Util::MultipartPlain]->text(); } return QString(); } void AlternativeMessagePart::fix() const { if (mChildParts.contains(Util::MultipartPlain)) { mChildParts[Util::MultipartPlain]->fix(); } const auto mode = preferredMode(); if (mode != Util::MultipartPlain && mChildParts.contains(mode)) { mChildParts[mode]->fix(); } } bool AlternativeMessagePart::isHtml() const { return mChildParts.contains(Util::MultipartHtml); } QString AlternativeMessagePart::plaintextContent() const { return text(); } QString AlternativeMessagePart::htmlContent() const { if (mChildParts.contains(Util::MultipartHtml)) { return mChildParts[Util::MultipartHtml]->text(); } else { return plaintextContent(); } } //-----CertMessageBlock---------------------- CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport) : MessagePart(otp, QString()) , mAutoImport(autoImport) , mCryptoProto(cryptoProto) { if (!node) { qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; return; } setContent(node); if (!mAutoImport) { return; } const QByteArray certData = node->decodedContent(); QGpgME::ImportJob *import = mCryptoProto->importJob(); QGpgMEJobExecutor executor; mImportResult = executor.exec(import, certData); } CertMessagePart::~CertMessagePart() { } QString CertMessagePart::text() const { return QString(); } //-----SignedMessageBlock--------------------- SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, const QString &text, const QGpgME::Protocol *cryptoProto, const QString &fromAddress, KMime::Content *node) : MessagePart(otp, text) , mCryptoProto(cryptoProto) , mFromAddress(fromAddress) { setContent(node); partMetaData()->technicalProblem = (mCryptoProto == nullptr); partMetaData()->isSigned = true; partMetaData()->isGoodSignature = false; partMetaData()->keyTrust = GpgME::Signature::Unknown; partMetaData()->status = i18n("Wrong Crypto Plug-In."); partMetaData()->status_code = GPGME_SIG_STAT_NONE; } SignedMessagePart::~SignedMessagePart() { } void SignedMessagePart::setIsSigned(bool isSigned) { partMetaData()->isSigned = isSigned; } bool SignedMessagePart::isSigned() const { return partMetaData()->isSigned; } bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode) { NodeHelper *nodeHelper = mOtp->nodeHelper(); partMetaData()->isSigned = false; partMetaData()->technicalProblem = (mCryptoProto == nullptr); partMetaData()->keyTrust = GpgME::Signature::Unknown; partMetaData()->status = i18n("Wrong Crypto Plug-In."); partMetaData()->status_code = GPGME_SIG_STAT_NONE; const QByteArray mementoName = "verification"; CryptoBodyPartMemento *m = dynamic_cast(nodeHelper->bodyPartMemento(content(), mementoName)); Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong if (!m && mCryptoProto) { if (!signature.isEmpty()) { QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob(); if (job) { m = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data); } } else { QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob(); if (job) { m = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data); } } if (m) { if (mOtp->allowAsync()) { QObject::connect(m, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update); if (m->start()) { partMetaData()->inProgress = true; mOtp->mHasPendingAsyncJobs = true; } } else { m->exec(); } nodeHelper->setBodyPartMemento(content(), mementoName, m); } } else if (m && m->isRunning()) { partMetaData()->inProgress = true; mOtp->mHasPendingAsyncJobs = true; } else { partMetaData()->inProgress = false; mOtp->mHasPendingAsyncJobs = false; } if (m && !partMetaData()->inProgress) { if (!signature.isEmpty()) { mVerifiedText = data; } setVerificationResult(m, textNode); } if (!m && !partMetaData()->inProgress) { QString errorMsg; QString cryptPlugLibName; QString cryptPlugDisplayName; if (mCryptoProto) { cryptPlugLibName = mCryptoProto->name(); cryptPlugDisplayName = mCryptoProto->displayName(); } if (!mCryptoProto) { if (cryptPlugDisplayName.isEmpty()) { errorMsg = i18n("No appropriate crypto plug-in was found."); } else { errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'", "No %1 plug-in was found.", cryptPlugDisplayName); } } else { errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.", cryptPlugLibName); } partMetaData()->errorText = i18n("The message is signed, but the " "validity of the signature cannot be " "verified.
" "Reason: %1", errorMsg); } return partMetaData()->isSigned; } static int signatureToStatus(const GpgME::Signature &sig) { switch (sig.status().code()) { case GPG_ERR_NO_ERROR: return GPGME_SIG_STAT_GOOD; case GPG_ERR_BAD_SIGNATURE: return GPGME_SIG_STAT_BAD; case GPG_ERR_NO_PUBKEY: return GPGME_SIG_STAT_NOKEY; case GPG_ERR_NO_DATA: return GPGME_SIG_STAT_NOSIG; case GPG_ERR_SIG_EXPIRED: return GPGME_SIG_STAT_GOOD_EXP; case GPG_ERR_KEY_EXPIRED: return GPGME_SIG_STAT_GOOD_EXPKEY; default: return GPGME_SIG_STAT_ERROR; } } QString prettifyDN(const char *uid) { return QGpgME::DN(uid).prettyDN(); } void SignedMessagePart::sigStatusToMetaData() { GpgME::Key key; if (partMetaData()->isSigned) { GpgME::Signature signature = mSignatures.front(); partMetaData()->status_code = signatureToStatus(signature); partMetaData()->isGoodSignature = partMetaData()->status_code & GPGME_SIG_STAT_GOOD; // save extended signature status flags partMetaData()->sigSummary = signature.summary(); if (partMetaData()->isGoodSignature && !key.keyID()) { // Search for the key by its fingerprint so that we can check for // trust etc. QGpgME::KeyListJob *job = mCryptoProto->keyListJob(false, false, false); // local, no sigs if (!job) { qCDebug(MIMETREEPARSER_LOG) << "The Crypto backend does not support listing keys. "; } else { std::vector found_keys; // As we are local it is ok to make this synchronous GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(signature.fingerprint())), false, found_keys); if (res.error()) { qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint(); } if (found_keys.size() > 1) { // Should not Happen qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); } if (found_keys.size() != 1) { // Should not Happen at this point qCDebug(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); } else { key = found_keys[0]; } delete job; } } if (key.keyID()) { partMetaData()->keyId = key.keyID(); } if (partMetaData()->keyId.isEmpty()) { partMetaData()->keyId = signature.fingerprint(); } partMetaData()->keyTrust = signature.validity(); if (key.numUserIDs() > 0 && key.userID(0).id()) { partMetaData()->signer = prettifyDN(key.userID(0).id()); } for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { // The following if /should/ always result in TRUE but we // won't trust implicitely the plugin that gave us these data. if (key.userID(iMail).email()) { QString email = QString::fromUtf8(key.userID(iMail).email()); // ### work around gpgme 0.3.QString text() const override;x / cryptplug bug where the // ### email addresses are specified as angle-addr, not addr-spec: if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { email = email.mid(1, email.length() - 2); } if (!email.isEmpty()) { partMetaData()->signerMailAddresses.append(email); } } } if (signature.creationTime()) { partMetaData()->creationTime.setTime_t(signature.creationTime()); } else { partMetaData()->creationTime = QDateTime(); } if (partMetaData()->signer.isEmpty()) { if (key.numUserIDs() > 0 && key.userID(0).name()) { partMetaData()->signer = prettifyDN(key.userID(0).name()); } if (!partMetaData()->signerMailAddresses.empty()) { if (partMetaData()->signer.isEmpty()) { partMetaData()->signer = partMetaData()->signerMailAddresses.front(); } else { partMetaData()->signer += QLatin1String(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>'); } } } } } void SignedMessagePart::startVerification(const QByteArray &text, const QTextCodec *aCodec) { startVerificationDetached(text, nullptr, QByteArray()); if (!content() && partMetaData()->isSigned) { setText(aCodec->toUnicode(mVerifiedText)); } } void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature) { partMetaData()->isEncrypted = false; partMetaData()->isDecryptable = false; if (textNode) { parseInternal(textNode, false); } okVerify(text, signature, textNode); if (!partMetaData()->isSigned) { partMetaData()->creationTime = QDateTime(); } } void SignedMessagePart::setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode) { { const auto vm = dynamic_cast(m); if (vm) { mSignatures = vm->verifyResult().signatures(); } } { const auto vm = dynamic_cast(m); if (vm) { mVerifiedText = vm->plainText(); mSignatures = vm->verifyResult().signatures(); } } { const auto vm = dynamic_cast(m); if (vm) { mVerifiedText = vm->plainText(); mSignatures = vm->verifyResult().signatures(); } } partMetaData()->auditLogError = m->auditLogError(); partMetaData()->auditLog = m->auditLogAsHtml(); partMetaData()->isSigned = !mSignatures.empty(); if (partMetaData()->isSigned) { sigStatusToMetaData(); if (content()) { mOtp->nodeHelper()->setSignatureState(content(), KMMsgFullySigned); if (!textNode) { mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData()); if (!mVerifiedText.isEmpty()) { auto tempNode = new KMime::Content(); tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData())); tempNode->parse(); if (!tempNode->head().isEmpty()) { tempNode->contentDescription()->from7BitString("signed data"); } mOtp->nodeHelper()->attachExtraContent(content(), tempNode); parseInternal(tempNode, false); } } } } } QString SignedMessagePart::plaintextContent() const { if (!content()) { return MessagePart::text(); } else { return QString(); } } QString SignedMessagePart::htmlContent() const { if (!content()) { return MessagePart::text(); } else { return QString(); } } //-----CryptMessageBlock--------------------- EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, const QString &text, const QGpgME::Protocol *cryptoProto, const QString &fromAddress, KMime::Content *node) : MessagePart(otp, text) , mPassphraseError(false) , mNoSecKey(false) , mCryptoProto(cryptoProto) , mFromAddress(fromAddress) , mDecryptMessage(false) { setContent(node); partMetaData()->technicalProblem = (mCryptoProto == nullptr); partMetaData()->isSigned = false; partMetaData()->isGoodSignature = false; partMetaData()->isEncrypted = false; partMetaData()->isDecryptable = false; partMetaData()->keyTrust = GpgME::Signature::Unknown; partMetaData()->status = i18n("Wrong Crypto Plug-In."); partMetaData()->status_code = GPGME_SIG_STAT_NONE; } EncryptedMessagePart::~EncryptedMessagePart() { } void EncryptedMessagePart::setDecryptMessage(bool decrypt) { mDecryptMessage = decrypt; } bool EncryptedMessagePart::decryptMessage() const { return mDecryptMessage; } void EncryptedMessagePart::setIsEncrypted(bool encrypted) { partMetaData()->isEncrypted = encrypted; } bool EncryptedMessagePart::isEncrypted() const { return partMetaData()->isEncrypted; } bool EncryptedMessagePart::isDecryptable() const { return partMetaData()->isDecryptable; } bool EncryptedMessagePart::passphraseError() const { return mPassphraseError; } void EncryptedMessagePart::startDecryption(const QByteArray &text, const QTextCodec *aCodec) { KMime::Content *content = new KMime::Content; content->setBody(text); content->parse(); startDecryption(content); if (!partMetaData()->inProgress && partMetaData()->isDecryptable) { if (hasSubParts()) { auto _mp = (subParts()[0]).dynamicCast(); if (_mp) { _mp->setText(aCodec->toUnicode(mDecryptedData)); } else { setText(aCodec->toUnicode(mDecryptedData)); } } else { setText(aCodec->toUnicode(mDecryptedData)); } } } bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) { mPassphraseError = false; partMetaData()->inProgress = false; partMetaData()->errorText.clear(); partMetaData()->auditLogError = GpgME::Error(); partMetaData()->auditLog.clear(); bool bDecryptionOk = false; bool cannotDecrypt = false; NodeHelper *nodeHelper = mOtp->nodeHelper(); Q_ASSERT(decryptMessage()); // Check whether the memento contains a result from last time: const DecryptVerifyBodyPartMemento *m = dynamic_cast(nodeHelper->bodyPartMemento(&data, "decryptverify")); Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong if (!m && mCryptoProto) { QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob(); if (!job) { cannotDecrypt = true; } else { const QByteArray ciphertext = data.decodedContent(); DecryptVerifyBodyPartMemento *newM = new DecryptVerifyBodyPartMemento(job, ciphertext); if (mOtp->allowAsync()) { QObject::connect(newM, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update); if (newM->start()) { partMetaData()->inProgress = true; mOtp->mHasPendingAsyncJobs = true; } else { m = newM; } } else { newM->exec(); m = newM; } nodeHelper->setBodyPartMemento(&data, "decryptverify", newM); } } else if (m && m->isRunning()) { partMetaData()->inProgress = true; mOtp->mHasPendingAsyncJobs = true; m = nullptr; } if (m) { const QByteArray &plainText = m->plainText(); const GpgME::DecryptionResult &decryptResult = m->decryptResult(); const GpgME::VerificationResult &verifyResult = m->verifyResult(); partMetaData()->isSigned = verifyResult.signatures().size() > 0; if (verifyResult.signatures().size() > 0) { auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, MessagePart::text(), mCryptoProto, mFromAddress, content())); subPart->setVerificationResult(m, nullptr); appendSubPart(subPart); } mDecryptRecipients.clear(); bDecryptionOk = !decryptResult.error(); // std::stringstream ss; // ss << decryptResult << '\n' << verifyResult; // qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str(); for (const auto &recipient : decryptResult.recipients()) { if (!recipient.status()) { bDecryptionOk = true; } GpgME::Key key; QGpgME::KeyListJob *job = mCryptoProto->keyListJob(false, false, false); // local, no sigs if (!job) { qCDebug(MIMETREEPARSER_LOG) << "The Crypto backend does not support listing keys. "; } else { std::vector found_keys; // As we are local it is ok to make this synchronous GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(recipient.keyID())), false, found_keys); if (res.error()) { qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << recipient.keyID(); } if (found_keys.size() > 1) { // Should not Happen qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << recipient.keyID(); } if (found_keys.size() != 1) { // Should not Happen at this point qCDebug(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << recipient.keyID(); } else { key = found_keys[0]; } } mDecryptRecipients.push_back(std::make_pair(recipient, key)); } if (!bDecryptionOk && partMetaData()->isSigned) { //Only a signed part partMetaData()->isEncrypted = false; bDecryptionOk = true; mDecryptedData = plainText; } else { mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; partMetaData()->isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA; partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString()); if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) { partMetaData()->keyId = decryptResult.recipient(0).keyID(); } if (bDecryptionOk) { mDecryptedData = plainText; } else { mNoSecKey = true; foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); } if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly mPassphraseError = true; } } } } if (!bDecryptionOk) { QString cryptPlugLibName; if (mCryptoProto) { cryptPlugLibName = mCryptoProto->name(); } if (!mCryptoProto) { partMetaData()->errorText = i18n("No appropriate crypto plug-in was found."); } else if (cannotDecrypt) { partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName); } else if (!passphraseError()) { partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1String("
") + i18n("Error: %1", partMetaData()->errorText); } } return bDecryptionOk; } void EncryptedMessagePart::startDecryption(KMime::Content *data) { if (!content() && !data) { return; } if (!data) { data = content(); } partMetaData()->isEncrypted = true; bool bOkDecrypt = okDecryptMIME(*data); if (partMetaData()->inProgress) { return; } partMetaData()->isDecryptable = bOkDecrypt; if (!partMetaData()->isDecryptable) { setText(QString::fromUtf8(mDecryptedData.constData())); } if (partMetaData()->isEncrypted && !decryptMessage()) { partMetaData()->isDecryptable = true; } if (content() && !partMetaData()->isSigned) { mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData()); if (decryptMessage()) { auto tempNode = new KMime::Content(); tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData())); tempNode->parse(); if (!tempNode->head().isEmpty()) { tempNode->contentDescription()->from7BitString("encrypted data"); } mOtp->nodeHelper()->attachExtraContent(content(), tempNode); parseInternal(tempNode, false); } } } QString EncryptedMessagePart::plaintextContent() const { if (!content()) { return MessagePart::text(); } else { return QString(); } } QString EncryptedMessagePart::htmlContent() const { if (!content()) { return MessagePart::text(); } else { return QString(); } } QString EncryptedMessagePart::text() const { if (hasSubParts()) { auto _mp = (subParts()[0]).dynamicCast(); if (_mp) { return _mp->text(); } else { return MessagePart::text(); } } else { return MessagePart::text(); } } EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) : MessagePart(otp, QString()) , mMessage(message) { setContent(node); partMetaData()->isEncrypted = false; partMetaData()->isSigned = false; partMetaData()->isEncapsulatedRfc822Message = true; mOtp->nodeHelper()->setNodeDisplayedEmbedded(node, true); mOtp->nodeHelper()->setPartMetaData(node, *partMetaData()); if (!mMessage) { qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!"; return; } // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists, // since the user can click the link and expect to have normal attachment operations there. mOtp->nodeHelper()->writeNodeToTempFile(message.data()); parseInternal(message.data(), false); } EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() { } QString EncapsulatedRfc822MessagePart::text() const { return renderInternalText(); } void EncapsulatedRfc822MessagePart::fix() const { } diff --git a/mimetreeparser/src/messagepart.h b/mimetreeparser/src/messagepart.h index 824978ee..c53ac1b2 100644 --- a/mimetreeparser/src/messagepart.h +++ b/mimetreeparser/src/messagepart.h @@ -1,425 +1,425 @@ /* Copyright (c) 2015 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 __MIMETREEPARSER_MESSAGEPART_H__ #define __MIMETREEPARSER_MESSAGEPART_H__ #include "mimetreeparser_export.h" #include "mimetreeparser/bodypartformatter.h" #include "mimetreeparser/util.h" #include #include #include #include #include #include #include #include class QTextCodec; namespace GpgME { class ImportResult; } namespace QGpgME { class Protocol; } namespace KMime { class Content; } namespace MimeTreeParser { class ObjectTreeParser; class CryptoBodyPartMemento; class MessagePartPrivate; class MultiPartAlternativeBodyPartFormatter; namespace Interface { class ObjectTreeSource; } class MIMETREEPARSER_EXPORT MessagePart : public QObject { Q_OBJECT Q_PROPERTY(QString plaintextContent READ plaintextContent) Q_PROPERTY(QString htmlContent READ htmlContent) Q_PROPERTY(bool isAttachment READ isAttachment) Q_PROPERTY(bool root READ isRoot) Q_PROPERTY(bool isHtml READ isHtml) Q_PROPERTY(bool isHidden READ isHidden) Q_PROPERTY(QString attachmentIndex READ attachmentIndex CONSTANT) Q_PROPERTY(QString link READ attachmentLink CONSTANT) public: typedef QSharedPointer Ptr; MessagePart(ObjectTreeParser *otp, const QString &text); ~MessagePart(); void setParentPart(MessagePart *parentPart); MessagePart *parentPart() const; virtual QString text() const; void setText(const QString &text); virtual QString plaintextContent() const; virtual QString htmlContent() const; /** The KMime::Content* node that's represented by this part. * Can be @c nullptr, e.g. for sub-parts of an inline signed body part. */ KMime::Content *content() const; void setContent(KMime::Content *node); /** The KMime::Content* node that's the source of this part. * This is not necessarily the same as content(), for example for * broken-up multipart nodes. */ KMime::Content *attachmentContent() const; void setAttachmentContent(KMime::Content *node); bool isAttachment() const; /** @see KMime::Content::index() */ QString attachmentIndex() const; /** @see NodeHelper::asHREF */ QString attachmentLink() const; /** Returns a string respresentation of an URL that can be used * to invoke a BodyPartURLHandler for this body part. */ QString makeLink(const QString &path) const; void setIsRoot(bool root); bool isRoot() const; virtual bool isHtml() const; virtual bool isHidden() const; PartMetaData *partMetaData() const; Interface::BodyPartMemento *memento() const; void setMemento(Interface::BodyPartMemento *memento); /* only a function that should be removed if the refactoring is over */ virtual void fix() const; void appendSubPart(const MessagePart::Ptr &messagePart); const QVector &subParts() const; bool hasSubParts() const; void clearSubParts(); Interface::ObjectTreeSource *source() const; NodeHelper *nodeHelper() const; protected: void parseInternal(KMime::Content *node, bool onlyOneMimePart); QString renderInternalText() const; ObjectTreeParser *mOtp = nullptr; private: std::unique_ptr d; }; class MIMETREEPARSER_EXPORT MimeMessagePart : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; MimeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart); virtual ~MimeMessagePart(); QString text() const override; QString plaintextContent() const override; QString htmlContent() const override; private: bool mOnlyOneMimePart; friend class AlternativeMessagePart; }; class MIMETREEPARSER_EXPORT MessagePartList : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; MessagePartList(MimeTreeParser::ObjectTreeParser *otp); virtual ~MessagePartList(); QString text() const override; QString plaintextContent() const override; QString htmlContent() const override; }; enum IconType { NoIcon = 0, IconExternal, IconInline }; class MIMETREEPARSER_EXPORT TextMessagePart : public MessagePartList { Q_OBJECT Q_PROPERTY(bool showTextFrame READ showTextFrame CONSTANT) Q_PROPERTY(bool showLink READ showLink CONSTANT) Q_PROPERTY(QString label READ label CONSTANT) Q_PROPERTY(QString comment READ comment CONSTANT) public: typedef QSharedPointer Ptr; TextMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool decryptMessage); virtual ~TextMessagePart(); KMMsgSignatureState signatureState() const; KMMsgEncryptionState encryptionState() const; bool decryptMessage() const; bool isHidden() const override; bool showLink() const; bool showTextFrame() const; void setShowTextFrame(bool showFrame); /** The attachment filename, or the closest approximation thereof we have. */ QString label() const; /** A description of this attachment, if provided. */ QString comment() const; /** Temporary file containing the part content. */ QString temporaryFilePath() const; private: void parseContent(); KMMsgSignatureState mSignatureState; KMMsgEncryptionState mEncryptionState; bool mDrawFrame; bool mDecryptMessage; bool mIsHidden; friend class ObjectTreeParser; }; class MIMETREEPARSER_EXPORT AttachmentMessagePart : public TextMessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; AttachmentMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool decryptMessage); virtual ~AttachmentMessagePart(); - IconType asIcon() const; + //IconType asIcon() const; bool neverDisplayInline() const; void setNeverDisplayInline(bool displayInline); bool isImage() const; void setIsImage(bool image); - bool isHidden() const override; + //bool isHidden() const override; private: bool mIsImage; bool mNeverDisplayInline; }; class MIMETREEPARSER_EXPORT HtmlMessagePart : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; HtmlMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, MimeTreeParser::Interface::ObjectTreeSource *source); virtual ~HtmlMessagePart(); QString text() const override; void fix() const override; bool isHtml() const override; private: Interface::ObjectTreeSource *mSource; QString mBodyHTML; QByteArray mCharset; friend class DefaultRendererPrivate; }; class MIMETREEPARSER_EXPORT AlternativeMessagePart : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; AlternativeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode); virtual ~AlternativeMessagePart(); QString text() const override; Util::HtmlMode preferredMode() const; bool isHtml() const override; QString plaintextContent() const override; QString htmlContent() const override; QList availableModes(); void fix() const override; private: Util::HtmlMode mPreferredMode; QMap mChildNodes; QMap mChildParts; friend class DefaultRendererPrivate; friend class ObjectTreeParser; friend class MultiPartAlternativeBodyPartFormatter; }; class MIMETREEPARSER_EXPORT CertMessagePart : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; CertMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport); virtual ~CertMessagePart(); QString text() const override; private: bool mAutoImport; GpgME::ImportResult mImportResult; const QGpgME::Protocol *mCryptoProto; friend class DefaultRendererPrivate; }; class MIMETREEPARSER_EXPORT EncapsulatedRfc822MessagePart : public MessagePart { Q_OBJECT public: typedef QSharedPointer Ptr; EncapsulatedRfc822MessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message); virtual ~EncapsulatedRfc822MessagePart(); QString text() const override; void fix() const override; private: const KMime::Message::Ptr mMessage; friend class DefaultRendererPrivate; }; class MIMETREEPARSER_EXPORT EncryptedMessagePart : public MessagePart { Q_OBJECT Q_PROPERTY(bool decryptMessage READ decryptMessage WRITE setDecryptMessage) Q_PROPERTY(bool isEncrypted READ isEncrypted) Q_PROPERTY(bool passphraseError READ passphraseError) public: typedef QSharedPointer Ptr; EncryptedMessagePart(ObjectTreeParser *otp, const QString &text, const QGpgME::Protocol *cryptoProto, const QString &fromAddress, KMime::Content *node); virtual ~EncryptedMessagePart(); QString text() const override; void setDecryptMessage(bool decrypt); bool decryptMessage() const; void setIsEncrypted(bool encrypted); bool isEncrypted() const; bool isDecryptable() const; bool passphraseError() const; void startDecryption(const QByteArray &text, const QTextCodec *aCodec); void startDecryption(KMime::Content *data = nullptr); QByteArray mDecryptedData; QString plaintextContent() const override; QString htmlContent() const override; private: /** Handles the dectyptioon of a given content * returns true if the decryption was successfull * if used in async mode, check if mMetaData.inProgress is true, it inicates a running decryption process. */ bool okDecryptMIME(KMime::Content &data); protected: bool mPassphraseError; bool mNoSecKey; const QGpgME::Protocol *mCryptoProto; QString mFromAddress; bool mDecryptMessage; QByteArray mVerifiedText; std::vector > mDecryptRecipients; friend class DefaultRendererPrivate; friend class EncryptedBodyPartFormatter; }; class MIMETREEPARSER_EXPORT SignedMessagePart : public MessagePart { Q_OBJECT Q_PROPERTY(bool isSigned READ isSigned) public: typedef QSharedPointer Ptr; SignedMessagePart(ObjectTreeParser *otp, const QString &text, const QGpgME::Protocol *cryptoProto, const QString &fromAddress, KMime::Content *node); virtual ~SignedMessagePart(); void setIsSigned(bool isSigned); bool isSigned() const; void startVerification(const QByteArray &text, const QTextCodec *aCodec); void startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature); QByteArray mDecryptedData; std::vector mSignatures; QString plaintextContent() const override; QString htmlContent() const override; private: /** Handles the verification of data * If signature is empty it is handled as inline signature otherwise as detached signature mode. * Returns true if the verfication was successfull and the block is signed. * If used in async mode, check if mMetaData.inProgress is true, it inicates a running verification process. */ bool okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode); void sigStatusToMetaData(); void setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode); protected: const QGpgME::Protocol *mCryptoProto; QString mFromAddress; QByteArray mVerifiedText; friend EncryptedMessagePart; friend class DefaultRendererPrivate; }; } #endif //__MIMETREEPARSER_MESSAGEPART_H__