File indexing completed on 2024-12-22 05:05:19

0001 // SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "messageparser.h"
0005 
0006 #include "attachmentmodel.h"
0007 #include "mimetreeparser_core_debug.h"
0008 #include "objecttreeparser.h"
0009 
0010 #include <KLocalizedString>
0011 
0012 #include <QElapsedTimer>
0013 
0014 namespace
0015 {
0016 
0017 template<typename T>
0018 const T *findHeader(KMime::Content *content, KMime::Content *protectedHeaderNode)
0019 {
0020     if (protectedHeaderNode) {
0021         auto header = protectedHeaderNode->header<T>();
0022         if (header) {
0023             return header;
0024         }
0025     }
0026 
0027     auto header = content->header<T>();
0028     if (header || !content->parent()) {
0029         return header;
0030     }
0031     return findHeader<T>(content->parent(), nullptr);
0032 }
0033 
0034 QString mailboxesToHtml(const KMime::Types::Mailbox::List &mailboxes)
0035 {
0036     QStringList html;
0037     for (const auto &mailbox : mailboxes) {
0038         if (mailbox.hasName() && mailbox.hasAddress()) {
0039             html << QStringLiteral("%1 <%2>").arg(mailbox.name().toHtmlEscaped(), QString::fromUtf8(mailbox.address()).toHtmlEscaped());
0040         } else if (mailbox.hasAddress()) {
0041             html << QString::fromUtf8(mailbox.address());
0042         } else {
0043             if (mailbox.hasName()) {
0044                 html << mailbox.name();
0045             } else {
0046                 Q_ASSERT_X(false, __FUNCTION__, "Mailbox does not contains email address nor name");
0047                 html << i18nc("Displayed when a CC, FROM or TO field in an email is empty", "Unknown");
0048             }
0049         }
0050     }
0051 
0052     return html.join(i18nc("list separator", ", "));
0053 }
0054 }
0055 
0056 class MessagePartPrivate
0057 {
0058 public:
0059     std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
0060     KMime::Message::Ptr mMessage;
0061     KMime::Content *protectedHeaderNode = nullptr;
0062     std::unique_ptr<KMime::Content> ownedContent;
0063 };
0064 
0065 MessageParser::MessageParser(QObject *parent)
0066     : QObject(parent)
0067     , d(std::unique_ptr<MessagePartPrivate>(new MessagePartPrivate))
0068 {
0069 }
0070 
0071 MessageParser::~MessageParser()
0072 {
0073 }
0074 
0075 KMime::Message::Ptr MessageParser::message() const
0076 {
0077     return d->mMessage;
0078 }
0079 
0080 void MessageParser::setMessage(const KMime::Message::Ptr message)
0081 {
0082     if (message == d->mMessage) {
0083         return;
0084     }
0085     if (!message) {
0086         qCWarning(MIMETREEPARSER_CORE_LOG) << Q_FUNC_INFO << "Empty message given";
0087         return;
0088     }
0089     d->mMessage = message;
0090 
0091     QElapsedTimer time;
0092     time.start();
0093     auto parser = std::make_shared<MimeTreeParser::ObjectTreeParser>();
0094     parser->parseObjectTree(message.data());
0095     qCDebug(MIMETREEPARSER_CORE_LOG) << "Message parsing took: " << time.elapsed();
0096     parser->decryptAndVerify();
0097     qCDebug(MIMETREEPARSER_CORE_LOG) << "Message parsing and decryption/verification: " << time.elapsed();
0098     d->mParser = parser;
0099     const auto contentParts = parser->collectContentParts();
0100     for (const auto &part : contentParts) {
0101         if (!part->node()) {
0102             continue;
0103         }
0104         const auto contentType = part->node()->contentType();
0105         if (contentType && contentType->hasParameter(QStringLiteral("protected-headers"))) {
0106             const auto contentDisposition = part->node()->contentDisposition();
0107 
0108             // Check for legacy format for protected-headers
0109             if (contentDisposition && contentDisposition->disposition() == KMime::Headers::CDinline) {
0110                 d->ownedContent = std::make_unique<KMime::Content>();
0111                 // we put the decoded content as encoded content of the new node
0112                 // as the header are inline in part->node()
0113                 d->ownedContent->setContent(part->node()->decodedContent());
0114                 d->ownedContent->parse();
0115                 d->protectedHeaderNode = d->ownedContent.get();
0116                 break;
0117             }
0118             d->protectedHeaderNode = part->node();
0119             break;
0120         }
0121     }
0122 
0123     Q_EMIT htmlChanged();
0124 }
0125 
0126 bool MessageParser::loaded() const
0127 {
0128     return bool{d->mParser};
0129 }
0130 
0131 QString MessageParser::structureAsString() const
0132 {
0133     if (!d->mParser) {
0134         return {};
0135     }
0136     return d->mParser->structureAsString();
0137 }
0138 
0139 PartModel *MessageParser::parts() const
0140 {
0141     if (!d->mParser) {
0142         return nullptr;
0143     }
0144     const auto model = new PartModel(d->mParser);
0145     return model;
0146 }
0147 
0148 AttachmentModel *MessageParser::attachments() const
0149 {
0150     if (!d->mParser) {
0151         return nullptr;
0152     }
0153     auto attachmentModel = new AttachmentModel(d->mParser);
0154     attachmentModel->setParent(const_cast<MessageParser *>(this));
0155     return attachmentModel;
0156 }
0157 
0158 QString MessageParser::subject() const
0159 {
0160     if (d->mMessage) {
0161         const auto header = findHeader<KMime::Headers::Subject>(d->mMessage.get(), d->protectedHeaderNode);
0162         if (header) {
0163             return header->asUnicodeString();
0164         }
0165     }
0166 
0167     return QString();
0168 }
0169 
0170 QString MessageParser::from() const
0171 {
0172     if (d->mMessage) {
0173         const auto header = findHeader<KMime::Headers::From>(d->mMessage.get(), d->protectedHeaderNode);
0174         if (!header) {
0175             return {};
0176         }
0177         return mailboxesToHtml(header->mailboxes());
0178     }
0179     return QString();
0180 }
0181 
0182 QString MessageParser::sender() const
0183 {
0184     if (d->mMessage) {
0185         const auto header = findHeader<KMime::Headers::Sender>(d->mMessage.get(), d->protectedHeaderNode);
0186         if (!header) {
0187             return {};
0188         }
0189         return mailboxesToHtml(header->mailboxes());
0190     }
0191 
0192     return QString();
0193 }
0194 
0195 QString MessageParser::to() const
0196 {
0197     if (d->mMessage) {
0198         const auto header = findHeader<KMime::Headers::To>(d->mMessage.get(), d->protectedHeaderNode);
0199         if (!header) {
0200             return {};
0201         }
0202         return mailboxesToHtml(header->mailboxes());
0203     }
0204     return QString();
0205 }
0206 
0207 QString MessageParser::cc() const
0208 {
0209     if (d->mMessage) {
0210         const auto header = findHeader<KMime::Headers::Cc>(d->mMessage.get(), d->protectedHeaderNode);
0211         if (!header) {
0212             return {};
0213         }
0214         return mailboxesToHtml(header->mailboxes());
0215     }
0216     return QString();
0217 }
0218 
0219 QString MessageParser::bcc() const
0220 {
0221     if (d->mMessage) {
0222         const auto header = findHeader<KMime::Headers::Bcc>(d->mMessage.get(), d->protectedHeaderNode);
0223         if (!header) {
0224             return {};
0225         }
0226         return mailboxesToHtml(header->mailboxes());
0227     }
0228     return QString();
0229 }
0230 
0231 QDateTime MessageParser::date() const
0232 {
0233     if (d->mMessage) {
0234         const auto header = findHeader<KMime::Headers::Date>(d->mMessage.get(), d->protectedHeaderNode);
0235         if (header) {
0236             return header->dateTime();
0237         }
0238     }
0239     return QDateTime();
0240 }
0241 
0242 #include "moc_messageparser.cpp"