diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp index 096a5c3a..30c9f061 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer.cpp @@ -1,1045 +1,1045 @@ /* 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/attachmentstrategy.h" #include "viewer/csshelperbase.h" #include "messagepartrenderermanager.h" #include "htmlwriter/bufferedhtmlwriter.h" #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(CSSHelperBase *cssHelper, const MessagePartRendererFactory *rendererFactory) : mCSSHelper(cssHelper) , mRendererFactory(rendererFactory) { } 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"))); + mp->nodeHelper()->asHREF(mp->message().data(), QStringLiteral("body"))); - c.insert(QStringLiteral("msgHeader"), mp->source()->createMessageHeader(mp->mMessage.data())); + c.insert(QStringLiteral("msgHeader"), mp->source()->createMessageHeader(mp->message().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); + bool isHtmlPreferred = (preferredMode == MimeTreeParser::Util::Html) || (preferredMode == MimeTreeParser::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); + QString bodyText = processHtml(mp->bodyHtml(), 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); + convert.setHtmlString(mp->bodyHtml()); QString plaintext = convert.generatePlainText(); plaintext.replace(QLatin1Char('\n'), QStringLiteral("
")); c.insert(QStringLiteral("plaintext"), plaintext); } - mp->source()->setHtmlMode(Util::Html, QList() << Util::Html); + mp->source()->setHtmlMode(MimeTreeParser::Util::Html, QList() << MimeTreeParser::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("cryptoProto"), QVariant::fromValue(mp->cryptoProto())); + if (!mp->decryptRecipients().empty()) { c.insert(QStringLiteral("decryptedRecipients"), - QVariant::fromValue(mp->mDecryptRecipients)); + QVariant::fromValue(mp->decryptRecipients())); } 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); + block.setProperty("noSecKey", mp->isNoSecKey()); 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; + auto cryptoProto = mp->cryptoProto(); 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 msgFrom(KEmailAddress::extractEmailAddress(mp->fromAddress())); 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]; + MimeMessagePart::Ptr part(mp->childParts().first()); + if (mp->childParts().contains(mode)) { + part = mp->childParts()[mode]; } render(part, htmlWriter); } void DefaultRendererPrivate::render(const CertMessagePart::Ptr &mp, HtmlWriter *htmlWriter) { - const GpgME::ImportResult &importResult(mp->mImportResult); + const GpgME::ImportResult &importResult(mp->importResult()); 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; } } 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 = mAttachmentStrategy; const bool defaultHidden(as && as->defaultDisplay(content) == AttachmentStrategy::None); auto preferredMode = source()->preferredMode(); - bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml); + bool isHtmlPreferred = (preferredMode == MimeTreeParser::Util::Html) || (preferredMode == MimeTreeParser::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) +MimeTreeParser::IconType 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 = mAttachmentStrategy; 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); + bool isHtmlPreferred = (preferredMode == MimeTreeParser::Util::Html) || (preferredMode == MimeTreeParser::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(CSSHelperBase *cssHelper) - : d(new MimeTreeParser::DefaultRendererPrivate(cssHelper, MessagePartRendererFactory::instance())) + : d(new DefaultRendererPrivate(cssHelper, MessagePartRendererFactory::instance())) { } DefaultRenderer::~DefaultRenderer() { delete d; } void DefaultRenderer::setShowOnlyOneMimePart(bool onlyOneMimePart) { d->mShowOnlyOneMimePart = onlyOneMimePart; } void DefaultRenderer::setAttachmentStrategy(const AttachmentStrategy *strategy) { d->mAttachmentStrategy = strategy; } void DefaultRenderer::render(const MimeTreeParser::MessagePart::Ptr &msgPart, HtmlWriter *writer) { d->mMsgPart = msgPart; d->renderFactory(d->mMsgPart, writer); } diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer.h b/messageviewer/src/messagepartthemes/default/defaultrenderer.h index 759783cc..f6c2dc4f 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer.h +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer.h @@ -1,49 +1,49 @@ /* 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 MessagePart; typedef QSharedPointer MessagePartPtr; } namespace MessageViewer { +class DefaultRendererPrivate; class HtmlWriter; class AttachmentStrategy; class CSSHelperBase; class DefaultRenderer { public: DefaultRenderer(CSSHelperBase *cssHelder); ~DefaultRenderer(); void setShowOnlyOneMimePart(bool onlyOneMimePart); void setAttachmentStrategy(const AttachmentStrategy *strategy); void render(const MimeTreeParser::MessagePartPtr &msgPart, HtmlWriter *writer); private: - MimeTreeParser::DefaultRendererPrivate *d; + DefaultRendererPrivate *d; }; } -#endif //__MIMETREEPARSER_MAILRENDERER_H__ +#endif //__MESSAGEVIEWER_MAILRENDERER_H__ diff --git a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h index c8b55454..2adef94f 100644 --- a/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h +++ b/messageviewer/src/messagepartthemes/default/defaultrenderer_p.h @@ -1,75 +1,69 @@ /* 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 AttachmentStrategy; class MessagePartRendererFactory; class CSSHelperBase; class HtmlWriter; -} - -namespace MimeTreeParser { -class DefaultRendererPrivate : public MessageViewer::RenderContext +class DefaultRendererPrivate : public RenderContext { public: DefaultRendererPrivate(CSSHelperBase *cssHelper, const MessagePartRendererFactory *rendererFactory); ~DefaultRendererPrivate(); CSSHelperBase *cssHelper() const override; - Interface::ObjectTreeSource *source() const; + MimeTreeParser::Interface::ObjectTreeSource *source() const; - void renderSubParts(const MessagePart::Ptr &msgPart, MessageViewer::HtmlWriter *htmlWriter) override; + void renderSubParts(const MimeTreeParser::MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter) override; - void render(const MessagePartList::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const MimeMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const EncapsulatedRfc822MessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const HtmlMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void renderEncrypted(const EncryptedMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void renderSigned(const SignedMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const SignedMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const EncryptedMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const AlternativeMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - void render(const CertMessagePart::Ptr &mp, MessageViewer::HtmlWriter *htmlWriter); - bool renderWithFactory(const QMetaObject *mo, const MessagePart::Ptr &msgPart, MessageViewer::HtmlWriter *writer) override; + void render(const MimeTreeParser::MessagePartList::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::MimeMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::EncapsulatedRfc822MessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::HtmlMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void renderEncrypted(const MimeTreeParser::EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void renderSigned(const MimeTreeParser::SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::SignedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::EncryptedMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::AlternativeMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + void render(const MimeTreeParser::CertMessagePart::Ptr &mp, HtmlWriter *htmlWriter); + bool renderWithFactory(const QMetaObject *mo, const MimeTreeParser::MessagePart::Ptr &msgPart, HtmlWriter *writer) override; using RenderContext::renderWithFactory; - void renderFactory(const MessagePart::Ptr &msgPart, MessageViewer::HtmlWriter *htmlWriter); + void renderFactory(const MimeTreeParser::MessagePart::Ptr &msgPart, HtmlWriter *htmlWriter); bool isHiddenHint(const MimeTreeParser::MessagePart::Ptr &msgPart) override; MimeTreeParser::IconType displayHint(const MimeTreeParser::MessagePart::Ptr &msgPart) override; bool mShowOnlyOneMimePart = false; - MessagePart::Ptr mMsgPart; + MimeTreeParser::MessagePart::Ptr mMsgPart; CSSHelperBase *mCSSHelper = nullptr; - const MessageViewer::MessagePartRendererFactory *mRendererFactory = nullptr; - const MessageViewer::AttachmentStrategy *mAttachmentStrategy = nullptr; + const MessagePartRendererFactory *mRendererFactory = nullptr; + const AttachmentStrategy *mAttachmentStrategy = nullptr; }; } #endif diff --git a/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp b/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp index 0ce36b7d..83c88108 100644 --- a/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/plugins/attachmentmessagepartrenderer.cpp @@ -1,85 +1,84 @@ /* 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 "interfaces/htmlwriter.h" #include #include #include using namespace MessageViewer; AttachmentMessagePartRenderer::AttachmentMessagePartRenderer() { } AttachmentMessagePartRenderer::~AttachmentMessagePartRenderer() { } bool AttachmentMessagePartRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart, HtmlWriter *htmlWriter, RenderContext *context) const { - auto mp = msgPart.dynamicCast(); + auto mp = msgPart.dynamicCast(); if (!mp) { return false; } KMime::Content *node = mp->content(); if (context->isHiddenHint(msgPart)) { return true; } 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); + MimeTreeParser::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 f3f765da..c6566b4f 100644 --- a/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp +++ b/messageviewer/src/messagepartthemes/default/plugins/textmessagepartrenderer.cpp @@ -1,66 +1,65 @@ /* 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 "interfaces/htmlwriter.h" #include #include using namespace MessageViewer; TextMessagePartRenderer::TextMessagePartRenderer() { } TextMessagePartRenderer::~TextMessagePartRenderer() { } bool TextMessagePartRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart, HtmlWriter *htmlWriter, RenderContext *context) const { Q_UNUSED(context); - auto mp = msgPart.dynamicCast(); + auto mp = msgPart.dynamicCast(); if (!mp) { return false; } 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/mimetreeparser/src/messagepart.cpp b/mimetreeparser/src/messagepart.cpp index e4ef633b..1fb22af4 100644 --- a/mimetreeparser/src/messagepart.cpp +++ b/mimetreeparser/src/messagepart.cpp @@ -1,1310 +1,1360 @@ /* 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 "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; } 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) { if (!node) { qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; return; } setContent(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::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; } //-----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; } +QString HtmlMessagePart::bodyHtml() const +{ + return mBodyHTML; +} + //-----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(); } } +const QMap &AlternativeMessagePart::childParts() const +{ + return mChildParts; +} + 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(); } +const GpgME::ImportResult &CertMessagePart::importResult() const +{ + return mImportResult; +} + //-----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(); } } +const QGpgME::Protocol *SignedMessagePart::cryptoProto() const +{ + return mCryptoProto; +} + +QString SignedMessagePart::fromAddress() const +{ + return mFromAddress; +} + //-----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::isNoSecKey() const +{ + return mNoSecKey; +} + 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(); } } +const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const +{ + return mCryptoProto; +} + +QString EncryptedMessagePart::fromAddress() const +{ + return mFromAddress; +} + +const std::vector> &EncryptedMessagePart::decryptRecipients() const +{ + return mDecryptRecipients; +} + 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 { } + +const KMime::Message::Ptr EncapsulatedRfc822MessagePart::message() const +{ + return mMessage; +} diff --git a/mimetreeparser/src/messagepart.h b/mimetreeparser/src/messagepart.h index d647c107..4df5da13 100644 --- a/mimetreeparser/src/messagepart.h +++ b/mimetreeparser/src/messagepart.h @@ -1,417 +1,429 @@ /* 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(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; 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 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; 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(); bool neverDisplayInline() const; void setNeverDisplayInline(bool displayInline); bool isImage() const; void setIsImage(bool image); 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; + QString bodyHtml() const; + 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; + + const QMap &childParts() const; + 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; + const GpgME::ImportResult &importResult() const; + 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; + + const KMime::Message::Ptr message() const; + 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 isNoSecKey READ isNoSecKey) 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 isNoSecKey() 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; + const QGpgME::Protocol *cryptoProto() const; + QString fromAddress() const; + + const std::vector> &decryptRecipients() const; + 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; + const QGpgME::Protocol *cryptoProto() const; + QString fromAddress() const; + 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__