Messagelib

objecttreeparser.cpp
1/*
2 objecttreeparser.cpp
3
4 This file is part of KMail, the KDE mail client.
5 SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
6 SPDX-FileCopyrightText: 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net>
8 SPDX-FileCopyrightText: 2015 Sandro Knauß <sknauss@kde.org>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12
13// MessageViewer includes
14
15#include "objecttreeparser.h"
16
17#include "bodypartformatterfactory.h"
18#include "messagepart.h"
19#include "nodehelper.h"
20#include "partnodebodypart.h"
21
22#include "bodyformatter/utils.h"
23#include "interfaces/bodypartformatter.h"
24#include "utils/util.h"
25
26#include <KMime/Headers>
27#include <KMime/Message>
28
29// Qt includes
30#include <QByteArray>
31
32using namespace MimeTreeParser;
33
34ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser)
35 : mSource(topLevelParser->mSource)
36 , mNodeHelper(topLevelParser->mNodeHelper)
37 , mTopLevelContent(topLevelParser->mTopLevelContent)
38 , mAllowAsync(topLevelParser->mAllowAsync)
39{
40 init();
41}
42
43ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source, MimeTreeParser::NodeHelper *nodeHelper)
44 : mSource(source)
45 , mNodeHelper(nodeHelper)
46
47{
48 init();
49}
50
51void ObjectTreeParser::init()
52{
53 Q_ASSERT(mSource);
54
55 if (!mNodeHelper) {
56 mNodeHelper = new NodeHelper();
57 mDeleteNodeHelper = true;
58 } else {
59 mDeleteNodeHelper = false;
60 }
61}
62
63ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other)
64 : mSource(other.mSource)
65 , mNodeHelper(other.nodeHelper())
66 , mTopLevelContent(other.mTopLevelContent)
67 , mHasPendingAsyncJobs(other.hasPendingAsyncJobs())
68 , mAllowAsync(other.allowAsync())
69 , mDeleteNodeHelper(false)
70{
71}
72
73ObjectTreeParser::~ObjectTreeParser()
74{
75 if (mDeleteNodeHelper) {
76 delete mNodeHelper;
77 mNodeHelper = nullptr;
78 }
79}
80
81void ObjectTreeParser::setAllowAsync(bool allow)
82{
83 Q_ASSERT(!mHasPendingAsyncJobs);
84 mAllowAsync = allow;
85}
86
87bool ObjectTreeParser::allowAsync() const
88{
89 return mAllowAsync;
90}
91
92bool ObjectTreeParser::hasPendingAsyncJobs() const
93{
94 return mHasPendingAsyncJobs;
95}
96
98{
99 return mPlainTextContent;
100}
101
103{
104 return mHtmlContent;
105}
106
107//-----------------------------------------------------------------------------
108
109void ObjectTreeParser::parseObjectTree(KMime::Content *node, bool parseOnlySingleNode)
110{
111 mTopLevelContent = node;
112 mParsedPart = parseObjectTreeInternal(node, parseOnlySingleNode);
113
114 if (mParsedPart) {
115 mParsedPart->fix();
116 if (auto mp = toplevelTextNode(mParsedPart)) {
117 if (auto _mp = mp.dynamicCast<TextMessagePart>()) {
118 extractNodeInfos(_mp->content(), true);
119 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) {
120 if (_mp->childParts().contains(Util::MultipartPlain)) {
121 extractNodeInfos(_mp->childParts()[Util::MultipartPlain]->content(), true);
122 }
123 }
124 setPlainTextContent(mp->text());
125 }
126 }
127}
128
129MessagePartPtr ObjectTreeParser::parsedPart() const
130{
131 return mParsedPart;
132}
133
134MessagePartPtr ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mimeType)
135{
136 const auto formatters = mSource->bodyPartFormatterFactory()->formattersForType(QString::fromUtf8(mimeType));
137 Q_ASSERT(!formatters.empty());
138 for (auto formatter : formatters) {
139 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper);
140 mNodeHelper->setNodeDisplayedEmbedded(node, true);
141
142 const MessagePart::Ptr result = formatter->process(part);
143 if (result.isNull()) {
144 continue;
145 }
146
147 result->setAttachmentContent(node);
148 return result;
149 }
150
151 Q_UNREACHABLE();
152 return {};
153}
154
155MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
156{
157 if (!node) {
158 return {};
159 }
160
161 // reset pending async jobs state (we'll rediscover pending jobs as we go)
162 mHasPendingAsyncJobs = false;
163
164 mNodeHelper->clearOverrideHeaders();
165
166 // reset "processed" flags for...
167 if (onlyOneMimePart) {
168 // ... this node and all descendants
169 mNodeHelper->setNodeUnprocessed(node, false);
170 if (!node->contents().isEmpty()) {
171 mNodeHelper->setNodeUnprocessed(node, true);
172 }
173 } else if (!node->parent()) {
174 // ...this node and all it's siblings and descendants
175 mNodeHelper->setNodeUnprocessed(node, true);
176 }
177
178 const bool isRoot = node->isTopLevel();
179 auto parsedPart = MessagePart::Ptr(new MessagePartList(this));
180 parsedPart->setIsRoot(isRoot);
181 KMime::Content *parent = node->parent();
182 auto contents = parent ? parent->contents() : KMime::Content::List();
183 if (contents.isEmpty()) {
184 contents.append(node);
185 }
186 int i = contents.indexOf(node);
187 if (i < 0) {
188 return parsedPart;
189 } else {
190 for (; i < contents.size(); ++i) {
191 node = contents.at(i);
192 if (mNodeHelper->nodeProcessed(node)) {
193 continue;
194 }
195
196 ProcessResult processResult(mNodeHelper);
197
198 QByteArray mimeType("text/plain");
199 if (auto ct = node->contentType(false)) {
200 mimeType = ct->mimeType();
201 }
202 // unfortunately there's many emails where we can't trust the attachment mimetype
203 // so try to see if we can find something better
204 if (mimeType == "application/octet-stream") {
206 mimeType = node->contentType(false)->mimeType();
207 }
208
209 const auto mp = processType(node, processResult, mimeType);
210 Q_ASSERT(mp);
211 parsedPart->appendSubPart(mp);
212 mNodeHelper->setNodeProcessed(node, false);
213
214 // adjust signed/encrypted flags if inline PGP was found
215 processResult.adjustCryptoStatesOfNode(node);
216
217 if (onlyOneMimePart) {
218 break;
219 }
220 }
221 }
222 return parsedPart;
223}
224
225KMMsgSignatureState ProcessResult::inlineSignatureState() const
226{
227 return mInlineSignatureState;
228}
229
230void ProcessResult::setInlineSignatureState(KMMsgSignatureState state)
231{
232 mInlineSignatureState = state;
233}
234
235KMMsgEncryptionState ProcessResult::inlineEncryptionState() const
236{
237 return mInlineEncryptionState;
238}
239
240void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state)
241{
242 mInlineEncryptionState = state;
243}
244
245bool ProcessResult::neverDisplayInline() const
246{
247 return mNeverDisplayInline;
248}
249
250void ProcessResult::setNeverDisplayInline(bool display)
251{
252 mNeverDisplayInline = display;
253}
254
255void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const
256{
257 if ((inlineSignatureState() != KMMsgNotSigned) || (inlineEncryptionState() != KMMsgNotEncrypted)) {
258 mNodeHelper->setSignatureState(node, inlineSignatureState());
259 mNodeHelper->setEncryptionState(node, inlineEncryptionState());
260 }
261}
262
263void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart)
264{
265 if (isFirstTextPart) {
266 mPlainTextContent += curNode->decodedText();
267 }
268}
269
270void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent)
271{
272 mPlainTextContent = plainTextContent;
273}
274
275QByteArray ObjectTreeParser::codecNameFor(KMime::Content *node) const
276{
277 Q_ASSERT(node);
278 if (!mSource->overrideCodecName().isEmpty()) {
279 return mSource->overrideCodecName();
280 }
281 return mNodeHelper->codecName(node);
282}
283
284MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const
285{
286 return mNodeHelper;
287}
QString decodedText(bool trimText, bool removeTrailingNewlines=false) const
const Headers::ContentType * contentType() const
Content * parent()
QList< Content * > contents()
QList< KMime::Content * > List
bool isTopLevel() const
QByteArray mimeType() const
QList< const Interface::BodyPartFormatter * > formattersForType(const QString &mimeType) const
Returns all suitable formatters for the given mimetype.
Interface for object tree sources.
static void magicSetType(KMime::Content *node, bool autoDecode=true)
Set the 'Content-Type' by mime-magic from the contents of the body.
Parses messages and generates HTML display code out of them.
void parseObjectTree(KMime::Content *node, bool parseOnlySingleNode=false)
Parse beginning at a given node and recursively parsing the children of that node and it's next sibli...
QString plainTextContent() const
The text of the message, ie.
QString htmlContent() const
Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
an implementation of the BodyPart interface using KMime::Content's
The ProcessResult class.
KCALUTILS_EXPORT QString mimeType()
@ MultipartPlain
A multipart/alternative message, the plain text part is currently displayed.
QCA_EXPORT void init()
bool isNull() const const
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:46:37 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.