File indexing completed on 2025-01-19 04:46:49

0001 /*
0002   SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "gnupgwksformatter.h"
0008 #include "gnupgwks_debug.h"
0009 #include "gnupgwksmessagepart.h"
0010 
0011 #include <QObject>
0012 #include <QPalette>
0013 #include <QUrl>
0014 #include <QUrlQuery>
0015 #include <QVariant>
0016 
0017 #include <MessageCore/ColorUtil>
0018 #include <MessageViewer/HtmlWriter>
0019 #include <MessageViewer/Viewer>
0020 #include <MimeTreeParser/BodyPart>
0021 #include <MimeTreeParser/MessagePart>
0022 #include <MimeTreeParser/NodeHelper>
0023 
0024 #include <GrantleeTheme/GrantleeKi18nLocalizer>
0025 #include <GrantleeTheme/GrantleeThemeEngine>
0026 #include <KTextTemplate/Context>
0027 #include <KTextTemplate/Template>
0028 
0029 #include <QGpgME/DecryptJob>
0030 #include <QGpgME/Protocol>
0031 
0032 using namespace MimeTreeParser;
0033 using namespace MimeTreeParser::Interface;
0034 
0035 namespace
0036 {
0037 bool partHasMimeType(KMime::Content *part, const char *mt)
0038 {
0039     const auto ct = part->contentType(false);
0040     return ct && ct->isMimeType(mt);
0041 }
0042 }
0043 
0044 MessagePart::Ptr ApplicationGnuPGWKSFormatter::process(BodyPart &part) const
0045 {
0046     const auto ct = part.content()->contentType(false);
0047     if (ct) {
0048         if (ct->isMimeType("multipart/mixed")) {
0049             const auto subParts = part.content()->contents();
0050             if (subParts.size() == 2 && partHasMimeType(subParts[0], "text/plain") && partHasMimeType(subParts[1], "application/vnd.gnupg.wks")) {
0051                 return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), subParts.at(1), false));
0052             } else {
0053                 return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), subParts.at(0), false));
0054             }
0055         }
0056 
0057         if (ct->isMimeType("application/vnd.gnupg.wks")) {
0058             const auto content = part.content()->decodedContent();
0059             if (content.startsWith("-----BEGIN PGP MESSAGE")) {
0060                 auto decrypt = QGpgME::openpgp()->decryptJob();
0061                 QByteArray plainText;
0062                 auto result = decrypt->exec(part.content()->decodedContent(), plainText);
0063                 if (result.error()) {
0064                     qCWarning(GNUPGWKS_LOG) << "Decryption failed!" << result.error().asString();
0065                     return {};
0066                 }
0067                 part.content()->setBody(plainText);
0068             }
0069             return MessagePart::Ptr(new GnuPGWKSMessagePart(&part));
0070         }
0071     }
0072 
0073     return {};
0074 }
0075 
0076 bool ApplicationGnuPGWKSFormatter::render(const MimeTreeParser::MessagePartPtr &msgPart,
0077                                           MessageViewer::HtmlWriter *htmlWriter,
0078                                           MessageViewer::RenderContext *context) const
0079 {
0080     Q_UNUSED(context)
0081     auto mp = msgPart.dynamicCast<GnuPGWKSMessagePart>();
0082     if (!mp) {
0083         return false;
0084     }
0085 
0086     const QByteArray propertyName = "_GnuPGWKS" + mp->fingerprint().toLatin1();
0087     const bool hasError = (mp->nodeHelper()->property(propertyName.constData()).toString() == QLatin1StringView("error"));
0088     if (hasError) {
0089         mp->nodeHelper()->setProperty(propertyName.constData(), QVariant());
0090     }
0091     GrantleeTheme::Engine engine;
0092     engine.localizer()->setApplicationDomain(QByteArrayLiteral("messageviewer_application_gnupgwks_plugin"));
0093     auto loader = QSharedPointer<KTextTemplate::FileSystemTemplateLoader>::create();
0094     loader->setTemplateDirs({QStringLiteral(":/")});
0095     engine.addTemplateLoader(loader);
0096     KTextTemplate::Template tpl = engine.loadByName(QStringLiteral("gnupgwksmessagepart.html"));
0097     if (tpl->error()) {
0098         qCWarning(GNUPGWKS_LOG) << tpl->errorString();
0099     }
0100     KTextTemplate::Context ctx;
0101     ctx.setLocalizer(engine.localizer());
0102 
0103     QObject block;
0104 
0105     const auto baseUrl = QStringLiteral("gnupgwks?%1");
0106     block.setProperty("isRequest", mp->confirmationType() == GnuPGWKSMessagePart::ConfirmationRequest);
0107     block.setProperty("isResponse", mp->confirmationType() == GnuPGWKSMessagePart::ConfirmationResponse);
0108     QUrlQuery confirmQuery;
0109     confirmQuery.addQueryItem(QStringLiteral("action"), QStringLiteral("confirm"));
0110     confirmQuery.addQueryItem(QStringLiteral("fpr"), mp->fingerprint());
0111     block.setProperty("confirmUrl", mp->makeLink(baseUrl.arg(confirmQuery.toString(QUrl::FullyDecoded))));
0112     QUrlQuery keyQuery;
0113     keyQuery.addQueryItem(QStringLiteral("action"), QStringLiteral("show"));
0114     keyQuery.addQueryItem(QStringLiteral("fpr"), mp->fingerprint());
0115     block.setProperty("keyUrl", mp->makeLink(baseUrl.arg(keyQuery.toString(QUrl::FullyDecoded))));
0116     block.setProperty("hasError", hasError);
0117     ctx.insert(QStringLiteral("block"), &block);
0118 
0119     QObject style;
0120     QPalette p;
0121     p.setCurrentColorGroup(QPalette::Normal);
0122     style.setProperty("buttonBg", p.color(QPalette::Button).name());
0123     style.setProperty("buttonBorder", p.shadow().color().name());
0124     p.setCurrentColorGroup(QPalette::Active);
0125     style.setProperty("buttonBorderHl", p.shadow().color().name());
0126     p.setCurrentColorGroup(QPalette::Normal);
0127     style.setProperty("buttonFg", p.color(QPalette::ButtonText).name());
0128     style.setProperty("errorFg", MessageCore::ColorUtil::self()->pgpSignedBadTextColor().name());
0129     ctx.insert(QStringLiteral("style"), &style);
0130     KTextTemplate::OutputStream s(htmlWriter->stream());
0131     tpl->render(&s, &ctx);
0132     return true;
0133 }