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 , mTopLevelContent(nullptr)
47 , mAllowAsync(false)
48{
49 init();
50}
51
52void ObjectTreeParser::init()
53{
54 Q_ASSERT(mSource);
55
56 if (!mNodeHelper) {
57 mNodeHelper = new NodeHelper();
58 mDeleteNodeHelper = true;
59 } else {
60 mDeleteNodeHelper = false;
61 }
62}
63
64ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other)
65 : mSource(other.mSource)
66 , mNodeHelper(other.nodeHelper())
67 , mTopLevelContent(other.mTopLevelContent)
68 , mHasPendingAsyncJobs(other.hasPendingAsyncJobs())
69 , mAllowAsync(other.allowAsync())
70 , mDeleteNodeHelper(false)
71{
72}
73
74ObjectTreeParser::~ObjectTreeParser()
75{
76 if (mDeleteNodeHelper) {
77 delete mNodeHelper;
78 mNodeHelper = nullptr;
79 }
80}
81
82void ObjectTreeParser::setAllowAsync(bool allow)
83{
84 Q_ASSERT(!mHasPendingAsyncJobs);
85 mAllowAsync = allow;
86}
87
88bool ObjectTreeParser::allowAsync() const
89{
90 return mAllowAsync;
91}
92
93bool ObjectTreeParser::hasPendingAsyncJobs() const
94{
95 return mHasPendingAsyncJobs;
96}
97
99{
100 return mPlainTextContent;
101}
102
104{
105 return mHtmlContent;
106}
107
108//-----------------------------------------------------------------------------
109
110void ObjectTreeParser::parseObjectTree(KMime::Content *node, bool parseOnlySingleNode)
111{
112 mTopLevelContent = node;
113 mParsedPart = parseObjectTreeInternal(node, parseOnlySingleNode);
114
115 if (mParsedPart) {
116 mParsedPart->fix();
117 if (auto mp = toplevelTextNode(mParsedPart)) {
118 if (auto _mp = mp.dynamicCast<TextMessagePart>()) {
119 extractNodeInfos(_mp->content(), true);
120 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) {
121 if (_mp->childParts().contains(Util::MultipartPlain)) {
122 extractNodeInfos(_mp->childParts()[Util::MultipartPlain]->content(), true);
123 }
124 }
125 setPlainTextContent(mp->text());
126 }
127 }
128}
129
130MessagePartPtr ObjectTreeParser::parsedPart() const
131{
132 return mParsedPart;
133}
134
135MessagePartPtr ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mimeType)
136{
137 const auto formatters = mSource->bodyPartFormatterFactory()->formattersForType(QString::fromUtf8(mimeType));
138 Q_ASSERT(!formatters.empty());
139 for (auto formatter : formatters) {
140 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper);
141 mNodeHelper->setNodeDisplayedEmbedded(node, true);
142
143 const MessagePart::Ptr result = formatter->process(part);
144 if (result.isNull()) {
145 continue;
146 }
147
148 result->setAttachmentContent(node);
149 return result;
150 }
151
152 Q_UNREACHABLE();
153 return {};
154}
155
156MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
157{
158 if (!node) {
159 return {};
160 }
161
162 // reset pending async jobs state (we'll rediscover pending jobs as we go)
163 mHasPendingAsyncJobs = false;
164
165 mNodeHelper->clearOverrideHeaders();
166
167 // reset "processed" flags for...
168 if (onlyOneMimePart) {
169 // ... this node and all descendants
170 mNodeHelper->setNodeUnprocessed(node, false);
171 if (!node->contents().isEmpty()) {
172 mNodeHelper->setNodeUnprocessed(node, true);
173 }
174 } else if (!node->parent()) {
175 // ...this node and all it's siblings and descendants
176 mNodeHelper->setNodeUnprocessed(node, true);
177 }
178
179 const bool isRoot = node->isTopLevel();
180 auto parsedPart = MessagePart::Ptr(new MessagePartList(this));
181 parsedPart->setIsRoot(isRoot);
182 KMime::Content *parent = node->parent();
183 auto contents = parent ? parent->contents() : KMime::Content::List();
184 if (contents.isEmpty()) {
185 contents.append(node);
186 }
187 int i = contents.indexOf(node);
188 if (i < 0) {
189 return parsedPart;
190 } else {
191 for (; i < contents.size(); ++i) {
192 node = contents.at(i);
193 if (mNodeHelper->nodeProcessed(node)) {
194 continue;
195 }
196
197 ProcessResult processResult(mNodeHelper);
198
199 QByteArray mimeType("text/plain");
200 if (auto ct = node->contentType(false)) {
201 mimeType = ct->mimeType();
202 }
203 // unfortunately there's many emails where we can't trust the attachment mimetype
204 // so try to see if we can find something better
205 if (mimeType == "application/octet-stream") {
207 mimeType = node->contentType(false)->mimeType();
208 }
209
210 const auto mp = processType(node, processResult, mimeType);
211 Q_ASSERT(mp);
212 parsedPart->appendSubPart(mp);
213 mNodeHelper->setNodeProcessed(node, false);
214
215 // adjust signed/encrypted flags if inline PGP was found
216 processResult.adjustCryptoStatesOfNode(node);
217
218 if (onlyOneMimePart) {
219 break;
220 }
221 }
222 }
223 return parsedPart;
224}
225
226KMMsgSignatureState ProcessResult::inlineSignatureState() const
227{
228 return mInlineSignatureState;
229}
230
231void ProcessResult::setInlineSignatureState(KMMsgSignatureState state)
232{
233 mInlineSignatureState = state;
234}
235
236KMMsgEncryptionState ProcessResult::inlineEncryptionState() const
237{
238 return mInlineEncryptionState;
239}
240
241void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state)
242{
243 mInlineEncryptionState = state;
244}
245
246bool ProcessResult::neverDisplayInline() const
247{
248 return mNeverDisplayInline;
249}
250
251void ProcessResult::setNeverDisplayInline(bool display)
252{
253 mNeverDisplayInline = display;
254}
255
256void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const
257{
258 if ((inlineSignatureState() != KMMsgNotSigned) || (inlineEncryptionState() != KMMsgNotEncrypted)) {
259 mNodeHelper->setSignatureState(node, inlineSignatureState());
260 mNodeHelper->setEncryptionState(node, inlineEncryptionState());
261 }
262}
263
264void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart)
265{
266 if (isFirstTextPart) {
267 mPlainTextContent += curNode->decodedText();
268 mPlainTextContentCharset += NodeHelper::charset(curNode);
269 }
270}
271
272void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent)
273{
274 mPlainTextContent = plainTextContent;
275}
276
277QByteArray ObjectTreeParser::codecNameFor(KMime::Content *node) const
278{
279 Q_ASSERT(node);
280 if (!mSource->overrideCodecName().isEmpty()) {
281 return mSource->overrideCodecName();
282 }
283 return mNodeHelper->codecName(node);
284}
285
287{
288 return mPlainTextContentCharset;
289}
290
291QByteArray ObjectTreeParser::htmlContentCharset() const
292{
293 return mHtmlContentCharset;
294}
295
296MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const
297{
298 return mNodeHelper;
299}
Headers::ContentType * contentType(bool create=true)
QString decodedText(bool trimText=false, bool removeTrailingNewlines=false)
Content * parent() const
bool isTopLevel() const
QList< KMime::Content * > List
QList< Content * > contents() 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.
virtual QByteArray overrideCodecName() const =0
The override codec that should be used for the mail.
static QByteArray charset(KMime::Content *node)
Returns the charset for the given node.
static void magicSetType(KMime::Content *node, bool autoDecode=true)
Set the 'Content-Type' by mime-magic from the contents of the body.
QByteArray codecName(KMime::Content *node) const
Get a codec suitable for this message part.
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.
QByteArray plainTextContentCharset() const
The original charset of MIME part the plain text was extracted from.
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 isEmpty() const const
bool isNull() const const
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.