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"