File indexing completed on 2025-01-19 04:46:51
0001 /* 0002 SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <MessageViewer/MessagePartRenderPlugin> 0008 #include <MessageViewer/MessagePartRendererBase> 0009 #include <MessageViewer/MessagePartRendererManager> 0010 0011 #include <MessageViewer/HtmlWriter> 0012 #include <MimeTreeParser/MessagePart> 0013 0014 #include <KPkPass/Barcode> 0015 #include <KPkPass/BoardingPass> 0016 0017 #include <KTextTemplate/MetaType> 0018 #include <KTextTemplate/Template> 0019 0020 #include <Prison/Barcode> 0021 0022 #include <QGuiApplication> 0023 #include <QImage> 0024 #include <QUrl> 0025 0026 static bool isPkPassContent(KMime::Content *content) 0027 { 0028 const auto ct = content->contentType(false); 0029 const QByteArray mimetype = ct ? ct->mimeType() : QByteArray(); 0030 if (mimetype == "application/vnd.apple.pkpass") { 0031 return true; 0032 } 0033 if (mimetype != "application/octet-stream" && mimetype != "application/zip") { 0034 return false; 0035 } 0036 if (ct && ct->name().endsWith(QLatin1StringView("pkpass"))) { 0037 return true; 0038 } 0039 const auto cd = content->contentDisposition(false); 0040 return cd && cd->filename().endsWith(QLatin1StringView("pkpass")); 0041 } 0042 0043 namespace 0044 { 0045 class Formatter : public MessageViewer::MessagePartRendererBase 0046 { 0047 public: 0048 bool render(const MimeTreeParser::MessagePartPtr &msgPart, MessageViewer::HtmlWriter *htmlWriter, MessageViewer::RenderContext *context) const override 0049 { 0050 Q_UNUSED(context) 0051 auto mp = msgPart.dynamicCast<MimeTreeParser::AttachmentMessagePart>(); 0052 if (!mp || context->isHiddenHint(msgPart) || !msgPart->content() || !isPkPassContent(msgPart->content())) { 0053 return false; 0054 } 0055 0056 std::unique_ptr<KPkPass::Pass> pass(KPkPass::Pass::fromData(msgPart->content()->decodedContent())); 0057 if (!pass) { 0058 return false; 0059 } 0060 const auto dir = mp->nodeHelper()->createTempDir(QStringLiteral("pkpass")); 0061 const auto logo = pass->logo(); 0062 if (!logo.isNull()) { 0063 const QString fileName = dir + QStringLiteral("/logo.png"); 0064 logo.save(fileName); 0065 pass->setProperty("logoUrl", QUrl::fromLocalFile(fileName)); 0066 mp->nodeHelper()->addTempFile(fileName); 0067 } 0068 const auto strip = pass->strip(); 0069 if (!strip.isNull()) { 0070 const QString fileName = dir + QStringLiteral("/strip.png"); 0071 strip.save(fileName); 0072 pass->setProperty("stripUrl", QUrl::fromLocalFile(fileName)); 0073 mp->nodeHelper()->addTempFile(fileName); 0074 } 0075 const auto background = pass->background(); 0076 if (!background.isNull()) { 0077 const QString fileName = dir + QStringLiteral("/background.png"); 0078 background.save(fileName); 0079 pass->setProperty("backgroundUrl", QUrl::fromLocalFile(fileName)); 0080 mp->nodeHelper()->addTempFile(fileName); 0081 } 0082 const auto footer = pass->footer(); 0083 if (!footer.isNull()) { 0084 const QString fileName = dir + QStringLiteral("/footer.png"); 0085 footer.save(fileName); 0086 pass->setProperty("footerUrl", QUrl::fromLocalFile(fileName)); 0087 mp->nodeHelper()->addTempFile(fileName); 0088 } 0089 const auto thumbnail = pass->thumbnail(); 0090 if (!thumbnail.isNull()) { 0091 const QString fileName = dir + QStringLiteral("/thumbnail.png"); 0092 thumbnail.save(fileName); 0093 pass->setProperty("thumbnailUrl", QUrl::fromLocalFile(fileName)); 0094 mp->nodeHelper()->addTempFile(fileName); 0095 } 0096 0097 const auto barcodes = pass->barcodes(); 0098 if (!barcodes.isEmpty()) { 0099 const auto barcode = barcodes.at(0); 0100 std::optional<Prison::Barcode> code; 0101 switch (barcode.format()) { 0102 case KPkPass::Barcode::QR: 0103 code = Prison::Barcode::create(Prison::QRCode); 0104 break; 0105 case KPkPass::Barcode::Aztec: 0106 code = Prison::Barcode::create(Prison::Aztec); 0107 break; 0108 case KPkPass::Barcode::PDF417: 0109 code = Prison::Barcode::create(Prison::PDF417); 0110 break; 0111 case KPkPass::Barcode::Code128: 0112 code = Prison::Barcode::create(Prison::Code128); 0113 break; 0114 default: 0115 break; 0116 } 0117 0118 if (code) { 0119 code->setData(barcode.message()); 0120 0121 const QString fileName = dir + QStringLiteral("/barcode.png"); 0122 code->toImage(code->preferredSize(qGuiApp->devicePixelRatio())).save(fileName); 0123 0124 pass->setProperty("barcodeUrl", QUrl::fromLocalFile(fileName)); 0125 mp->nodeHelper()->addTempFile(fileName); 0126 } 0127 } 0128 0129 // Grantlee can't handle QColor... 0130 if (pass->foregroundColor().isValid()) { 0131 pass->setProperty("foregroundColorName", pass->foregroundColor().name()); 0132 } 0133 if (pass->backgroundColor().isValid()) { 0134 pass->setProperty("backgroundColorName", pass->backgroundColor().name()); 0135 } 0136 if (pass->labelColor().isValid()) { 0137 pass->setProperty("labelColorName", pass->labelColor().name()); 0138 } 0139 0140 auto c = MessageViewer::MessagePartRendererManager::self()->createContext(); 0141 c.insert(QStringLiteral("block"), mp.data()); 0142 c.insert(QStringLiteral("pass"), pass.get()); 0143 KTextTemplate::Template t; 0144 if (qobject_cast<KPkPass::BoardingPass *>(pass.get())) { 0145 t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/pkpass/boardingpass.html")); 0146 } else if (pass->type() == KPkPass::Pass::EventTicket) { 0147 t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/pkpass/eventticket.html")); 0148 } else if (pass->type() == KPkPass::Pass::Generic) { 0149 t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/pkpass/generic.html")); 0150 } else { 0151 // unknown pass type we have no template for 0152 return false; 0153 } 0154 KTextTemplate::OutputStream s(htmlWriter->stream()); 0155 t->render(&s, &c); 0156 return true; 0157 } 0158 }; 0159 0160 class Plugin : public QObject, public MessageViewer::MessagePartRenderPlugin 0161 { 0162 Q_OBJECT 0163 Q_INTERFACES(MessageViewer::MessagePartRenderPlugin) 0164 Q_PLUGIN_METADATA(IID "com.kde.messageviewer.bodypartformatter" FILE "pkpass_plugin.json") 0165 public: 0166 explicit Plugin(QObject *parent = nullptr) 0167 : QObject(parent) 0168 { 0169 } 0170 0171 MessageViewer::MessagePartRendererBase *renderer(int index) override 0172 { 0173 return index == 0 ? new Formatter() : nullptr; 0174 } 0175 }; 0176 } 0177 0178 #include "pkpass_plugin.moc"