File indexing completed on 2025-01-19 04:46:51
0001 /* 0002 SPDX-FileCopyrightText: 2010 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ms_tnef_debug.h" 0008 0009 #include <MessageViewer/MessagePartRenderPlugin> 0010 #include <MessageViewer/MessagePartRendererBase> 0011 #include <MessageViewer/MessagePartRendererManager> 0012 #include <MessageViewer/MimeType> 0013 0014 #include <MessageCore/StringUtil> 0015 0016 #include <MessageViewer/HtmlWriter> 0017 #include <MimeTreeParser/MessagePart> 0018 #include <MimeTreeParser/NodeHelper> 0019 0020 #include <KCalendarCore/Event> 0021 #include <KCalendarCore/ICalFormat> 0022 #include <KCalendarCore/Incidence> 0023 #include <KCalendarCore/MemoryCalendar> 0024 0025 #include <KCalUtils/IncidenceFormatter> 0026 0027 #include <ktnef/formatter.h> 0028 #include <ktnef/ktnefattach.h> 0029 #include <ktnef/ktnefmessage.h> 0030 #include <ktnef/ktnefparser.h> 0031 0032 #include <KTextTemplate/Template> 0033 0034 #include <KIconLoader> 0035 #include <KLocalizedString> 0036 0037 #include <QFile> 0038 0039 namespace 0040 { 0041 class Formatter : public MessageViewer::MessagePartRendererBase 0042 { 0043 public: 0044 bool render(const MimeTreeParser::MessagePartPtr &msgPart, MessageViewer::HtmlWriter *htmlWriter, MessageViewer::RenderContext *context) const override 0045 { 0046 Q_UNUSED(context) 0047 auto mp = msgPart.dynamicCast<MimeTreeParser::AttachmentMessagePart>(); 0048 if (!mp || context->isHiddenHint(msgPart)) { 0049 return false; 0050 } 0051 0052 const QByteArray mimetype = mp->content()->contentType()->mimeType(); 0053 if (mimetype != QByteArrayLiteral("application/vnd.ms-tnef") && mimetype != QByteArrayLiteral("application/ms-tnef")) { 0054 return false; 0055 } 0056 0057 const auto fileName = mp->temporaryFilePath(); 0058 KTnef::KTNEFParser parser; 0059 if (!parser.openFile(fileName) || !parser.message()) { 0060 qCDebug(MS_TNEF_LOG) << "Could not parse" << fileName; 0061 return false; 0062 } 0063 0064 // Look for an invitation 0065 QString inviteStr; 0066 QFile f(fileName); 0067 QByteArray buf; 0068 if (!f.open(QIODevice::ReadOnly)) { 0069 qCWarning(MS_TNEF_LOG) << "Failed to read attachment part: " << f.errorString(); 0070 } else { 0071 buf = f.readAll(); 0072 f.close(); 0073 } 0074 if (!buf.isEmpty()) { 0075 const KCalendarCore::MemoryCalendar::Ptr cl(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone())); 0076 KCalUtils::InvitationFormatterHelper helper; 0077 const QString invite = KTnef::formatTNEFInvitation(buf, cl, &helper); 0078 KCalendarCore::ICalFormat format; 0079 const KCalendarCore::Incidence::Ptr inc = format.fromString(invite); 0080 const KCalendarCore::Event::Ptr event = inc.dynamicCast<KCalendarCore::Event>(); 0081 if (event && event->hasEndDate()) { 0082 // no enddate => not a valid invitation 0083 inviteStr = KCalUtils::IncidenceFormatter::extensiveDisplayStr(cl, inc); 0084 } 0085 } 0086 0087 auto c = MessageViewer::MessagePartRendererManager::self()->createContext(); 0088 c.insert(QStringLiteral("block"), msgPart.data()); 0089 c.insert(QStringLiteral("showOnlyOneMimePart"), context->showOnlyOneMimePart()); 0090 c.insert(QStringLiteral("content"), QVariant::fromValue<MessageViewer::KTextTemplateCallback>([&](KTextTemplate::OutputStream *stream) { 0091 const auto tnefatts = parser.message()->attachmentList(); 0092 if (tnefatts.isEmpty() && inviteStr.isEmpty()) { 0093 qCDebug(MS_TNEF_LOG) << "No attachments or invitation found in" << fileName; 0094 (*stream) << QStringLiteral(" <") << i18nc("TNEF attachment has no content", "empty") << QStringLiteral(">"); 0095 return; 0096 } 0097 0098 if (!inviteStr.isEmpty()) { 0099 (*stream) << inviteStr; 0100 } 0101 0102 const int numberOfTnef(tnefatts.count()); 0103 for (int i = 0; i < numberOfTnef; ++i) { 0104 KTnef::KTNEFAttach *att = tnefatts.at(i); 0105 QString label = att->displayName(); 0106 if (label.isEmpty()) { 0107 label = att->name(); 0108 } 0109 label = MessageCore::StringUtil::quoteHtmlChars(label, true); 0110 0111 const QString dir = mp->nodeHelper()->createTempDir(QLatin1StringView("ktnef-") + QString::number(i)); 0112 if (!parser.extractFileTo(att->name(), dir)) { 0113 qCDebug(MS_TNEF_LOG) << "No possible to extract file:" << att->name(); 0114 } 0115 0116 // falling back to internal TNEF attachment name if no filename is given for the attached file 0117 // this follows the logic of KTNEFParser::extractFileTo(...) 0118 QString attFileName = att->fileName(); 0119 if (attFileName.isEmpty()) { 0120 attFileName = att->name(); 0121 } 0122 mp->nodeHelper()->addTempFile(dir + QLatin1Char('/') + attFileName); 0123 const QString href = QLatin1StringView("file:") + dir + QLatin1Char('/') + attFileName; 0124 0125 const QString iconName = 0126 QUrl::fromLocalFile(MessageViewer::Util::iconPathForMimetype(att->mimeTag(), KIconLoader::Desktop, attFileName)).url(); 0127 0128 (*stream) << QStringLiteral("<div><a href=\"") << href << QStringLiteral("\"><img src=\"") << iconName 0129 << QStringLiteral("\" border=\"0\" style=\"max-width: 100%\"/>") << label << QStringLiteral("</a></div><br/>"); 0130 } 0131 })); 0132 0133 auto t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("textmessagepart.html")); 0134 KTextTemplate::OutputStream s(htmlWriter->stream()); 0135 t->render(&s, &c); 0136 return true; 0137 } 0138 }; 0139 0140 class Plugin : public QObject, public MessageViewer::MessagePartRenderPlugin 0141 { 0142 Q_OBJECT 0143 Q_INTERFACES(MessageViewer::MessagePartRenderPlugin) 0144 Q_PLUGIN_METADATA(IID "com.kde.messageviewer.bodypartformatter" FILE "application_ms-tnef.json") 0145 public: 0146 MessageViewer::MessagePartRendererBase *renderer(int index) override 0147 { 0148 return index == 0 ? new Formatter() : nullptr; 0149 } 0150 }; 0151 } 0152 0153 #include "application_ms-tnef.moc"