summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp')
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp488
1 files changed, 0 insertions, 488 deletions
diff --git a/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp b/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp
deleted file mode 100644
index 4e0e3d92..00000000
--- a/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
1/*
2 objecttreeparser.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
9
10 KMail is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License, version 2, as
12 published by the Free Software Foundation.
13
14 KMail is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 In addition, as a special exception, the copyright holders give
24 permission to link the code of this program with any edition of
25 the Qt library by Trolltech AS, Norway (or with modified versions
26 of Qt that use the same license as Qt), and distribute linked
27 combinations including the two. You must obey the GNU General
28 Public License in all respects for all of the code used other than
29 Qt. If you modify this file, you may extend this exception to
30 your version of the file, but you are not obligated to do so. If
31 you do not wish to do so, delete this exception statement from
32 your version.
33*/
34
35// MessageViewer includes
36
37#include "objecttreeparser.h"
38
39#include "attachmentstrategy.h"
40#include "bodypartformatterbasefactory.h"
41#include "nodehelper.h"
42#include "messagepart.h"
43#include "partnodebodypart.h"
44
45#include "mimetreeparser_debug.h"
46
47#include "utils.h"
48#include "bodypartformatter.h"
49#include "htmlwriter.h"
50#include "util.h"
51
52#include <KMime/Headers>
53#include <KMime/Message>
54
55// KDE includes
56
57// Qt includes
58#include <QByteArray>
59#include <QTextCodec>
60#include <QUrl>
61
62using namespace MimeTreeParser;
63
64ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser,
65 bool showOnlyOneMimePart,
66 const AttachmentStrategy *strategy)
67 : mSource(topLevelParser->mSource),
68 mNodeHelper(topLevelParser->mNodeHelper),
69 mHtmlWriter(topLevelParser->mHtmlWriter),
70 mTopLevelContent(topLevelParser->mTopLevelContent),
71 mShowOnlyOneMimePart(showOnlyOneMimePart),
72 mHasPendingAsyncJobs(false),
73 mAllowAsync(topLevelParser->mAllowAsync),
74 mAttachmentStrategy(strategy)
75{
76 init();
77}
78
79ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source,
80 MimeTreeParser::NodeHelper *nodeHelper,
81 bool showOnlyOneMimePart,
82 const AttachmentStrategy *strategy)
83 : mSource(source),
84 mNodeHelper(nodeHelper),
85 mHtmlWriter(nullptr),
86 mTopLevelContent(nullptr),
87 mShowOnlyOneMimePart(showOnlyOneMimePart),
88 mHasPendingAsyncJobs(false),
89 mAllowAsync(false),
90 mAttachmentStrategy(strategy)
91{
92 init();
93}
94
95void ObjectTreeParser::init()
96{
97 Q_ASSERT(mSource);
98 if (!attachmentStrategy()) {
99 mAttachmentStrategy = mSource->attachmentStrategy();
100 }
101
102 if (!mNodeHelper) {
103 mNodeHelper = new NodeHelper();
104 mDeleteNodeHelper = true;
105 } else {
106 mDeleteNodeHelper = false;
107 }
108}
109
110ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other)
111 : mSource(other.mSource),
112 mNodeHelper(other.nodeHelper()), //TODO(Andras) hm, review what happens if mDeleteNodeHelper was true in the source
113 mHtmlWriter(other.mHtmlWriter),
114 mTopLevelContent(other.mTopLevelContent),
115 mShowOnlyOneMimePart(other.showOnlyOneMimePart()),
116 mHasPendingAsyncJobs(other.hasPendingAsyncJobs()),
117 mAllowAsync(other.allowAsync()),
118 mAttachmentStrategy(other.attachmentStrategy()),
119 mDeleteNodeHelper(false)
120{
121
122}
123
124ObjectTreeParser::~ObjectTreeParser()
125{
126 if (mDeleteNodeHelper) {
127 delete mNodeHelper;
128 mNodeHelper = nullptr;
129 }
130}
131
132void ObjectTreeParser::setAllowAsync(bool allow)
133{
134 Q_ASSERT(!mHasPendingAsyncJobs);
135 mAllowAsync = allow;
136}
137
138bool ObjectTreeParser::allowAsync() const
139{
140 return mAllowAsync;
141}
142
143bool ObjectTreeParser::hasPendingAsyncJobs() const
144{
145 return mHasPendingAsyncJobs;
146}
147
148QString ObjectTreeParser::plainTextContent() const
149{
150 return mPlainTextContent;
151}
152
153QString ObjectTreeParser::htmlContent() const
154{
155 return mHtmlContent;
156}
157
158void ObjectTreeParser::copyContentFrom(const ObjectTreeParser *other)
159{
160 mPlainTextContent += other->plainTextContent();
161 mHtmlContent += other->htmlContent();
162 if (!other->plainTextContentCharset().isEmpty()) {
163 mPlainTextContentCharset = other->plainTextContentCharset();
164 }
165 if (!other->htmlContentCharset().isEmpty()) {
166 mHtmlContentCharset = other->htmlContentCharset();
167 }
168}
169
170//-----------------------------------------------------------------------------
171
172void ObjectTreeParser::parseObjectTree(KMime::Content *node)
173{
174 mTopLevelContent = node;
175 mParsedPart = parseObjectTreeInternal(node, showOnlyOneMimePart());
176
177 if (mParsedPart) {
178 mParsedPart->fix();
179 mParsedPart->copyContentFrom();
180 if (auto mp = toplevelTextNode(mParsedPart)) {
181 if (auto _mp = mp.dynamicCast<TextMessagePart>()) {
182 extractNodeInfos(_mp->mNode, true);
183 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) {
184 if (_mp->mChildNodes.contains(Util::MultipartPlain)) {
185 extractNodeInfos(_mp->mChildNodes[Util::MultipartPlain], true);
186 }
187 }
188 setPlainTextContent(mp->text());
189 }
190
191 }
192}
193
194MessagePartPtr ObjectTreeParser::parsedPart() const
195{
196 return mParsedPart;
197}
198
199bool ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart)
200{
201 bool bRendered = false;
202 const auto sub = mSource->bodyPartFormatterFactory()->subtypeRegistry(mediaType.constData());
203 auto range = sub.equal_range(subType.constData());
204 for (auto it = range.first; it != range.second; ++it) {
205 const auto formatter = (*it).second;
206 if (!formatter) {
207 continue;
208 }
209 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper);
210 // Set the default display strategy for this body part relying on the
211 // identity of Interface::BodyPart::Display and AttachmentStrategy::Display
212 part.setDefaultDisplay((Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay(node));
213
214 mNodeHelper->setNodeDisplayedEmbedded(node, true);
215
216 const Interface::MessagePart::Ptr result = formatter->process(part);
217 if (!result) {
218 continue;
219 }
220
221 if (const auto mp = result.dynamicCast<MessagePart>()) {
222 mp->setAttachmentFlag(node);
223 mpRet = result;
224 bRendered = true;
225 break;
226 } else if (dynamic_cast<MimeTreeParser::Interface::MessagePart *>(result.data())) {
227 QObject *asyncResultObserver = allowAsync() ? mSource->sourceObject() : nullptr;
228 const auto r = formatter->format(&part, result->htmlWriter(), asyncResultObserver);
229 if (r == Interface::BodyPartFormatter::AsIcon) {
230 processResult.setNeverDisplayInline(true);
231 formatter->adaptProcessResult(processResult);
232 mNodeHelper->setNodeDisplayedEmbedded(node, false);
233 const Interface::MessagePart::Ptr mp = defaultHandling(node, processResult, onlyOneMimePart);
234 if (mp) {
235 if (auto _mp = mp.dynamicCast<MessagePart>()) {
236 _mp->setAttachmentFlag(node);
237 }
238 mpRet = mp;
239 }
240 bRendered = true;
241 break;
242 } else if (r == Interface::BodyPartFormatter::Ok) {
243 processResult.setNeverDisplayInline(true);
244 formatter->adaptProcessResult(processResult);
245 mpRet = result;
246 bRendered = true;
247 break;
248 }
249 continue;
250 } else {
251 continue;
252 }
253 }
254 return bRendered;
255}
256
257MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
258{
259 if (!node) {
260 return MessagePart::Ptr();
261 }
262
263 // reset pending async jobs state (we'll rediscover pending jobs as we go)
264 mHasPendingAsyncJobs = false;
265
266 // reset "processed" flags for...
267 if (onlyOneMimePart) {
268 // ... this node and all descendants
269 mNodeHelper->setNodeUnprocessed(node, false);
270 if (!node->contents().isEmpty()) {
271 mNodeHelper->setNodeUnprocessed(node, true);
272 }
273 } else if (!node->parent()) {
274 // ...this node and all it's siblings and descendants
275 mNodeHelper->setNodeUnprocessed(node, true);
276 }
277
278 const bool isRoot = node->isTopLevel();
279 auto parsedPart = MessagePart::Ptr(new MessagePartList(this));
280 parsedPart->setIsRoot(isRoot);
281 KMime::Content *parent = node->parent();
282 auto contents = parent ? parent->contents() : KMime::Content::List();
283 if (contents.isEmpty()) {
284 contents.append(node);
285 }
286 int i = contents.indexOf(const_cast<KMime::Content *>(node));
287 for (; i < contents.size(); ++i) {
288 node = contents.at(i);
289 if (mNodeHelper->nodeProcessed(node)) {
290 continue;
291 }
292
293 ProcessResult processResult(mNodeHelper);
294
295 QByteArray mediaType("text");
296 QByteArray subType("plain");
297 if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() &&
298 !node->contentType()->subType().isEmpty()) {
299 mediaType = node->contentType()->mediaType();
300 subType = node->contentType()->subType();
301 }
302
303 Interface::MessagePartPtr mp;
304 if (processType(node, processResult, mediaType, subType, mp, onlyOneMimePart)) {
305 if (mp) {
306 parsedPart->appendSubPart(mp);
307 }
308 } else if (processType(node, processResult, mediaType, "*", mp, onlyOneMimePart)) {
309 if (mp) {
310 parsedPart->appendSubPart(mp);
311 }
312 } else {
313 qCWarning(MIMETREEPARSER_LOG) << "THIS SHOULD NO LONGER HAPPEN:" << mediaType << '/' << subType;
314 const auto mp = defaultHandling(node, processResult, onlyOneMimePart);
315 if (mp) {
316 if (auto _mp = mp.dynamicCast<MessagePart>()) {
317 _mp->setAttachmentFlag(node);
318 }
319 parsedPart->appendSubPart(mp);
320 }
321 }
322 mNodeHelper->setNodeProcessed(node, false);
323
324 // adjust signed/encrypted flags if inline PGP was found
325 processResult.adjustCryptoStatesOfNode(node);
326
327 if (onlyOneMimePart) {
328 break;
329 }
330 }
331
332 return parsedPart;
333}
334
335Interface::MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node, ProcessResult &result, bool onlyOneMimePart)
336{
337 Interface::MessagePart::Ptr mp;
338 ProcessResult processResult(mNodeHelper);
339
340 if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream") &&
341 (node->contentType()->name().endsWith(QLatin1String("p7m")) ||
342 node->contentType()->name().endsWith(QLatin1String("p7s")) ||
343 node->contentType()->name().endsWith(QLatin1String("p7c"))
344 ) &&
345 processType(node, processResult, "application", "pkcs7-mime", mp, onlyOneMimePart)) {
346 return mp;
347 }
348
349 const auto _mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node, false, true, mSource->decryptMessage()));
350 result.setInlineSignatureState(_mp->signatureState());
351 result.setInlineEncryptionState(_mp->encryptionState());
352 _mp->setNeverDisplayInline(result.neverDisplayInline());
353 _mp->setIsImage(result.isImage());
354 mp = _mp;
355
356 // always show images in multipart/related when showing in html, not with an additional icon
357 auto preferredMode = mSource->preferredMode();
358 bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml);
359 if (result.isImage() && node->parent() &&
360 node->parent()->contentType()->subType() == "related" && isHtmlPreferred && !onlyOneMimePart) {
361 QString fileName = mNodeHelper->writeNodeToTempFile(node);
362 QString href = QUrl::fromLocalFile(fileName).url();
363 QByteArray cid = node->contentID()->identifier();
364 if (htmlWriter()) {
365 htmlWriter()->embedPart(cid, href);
366 }
367 nodeHelper()->setNodeDisplayedEmbedded(node, true);
368 mNodeHelper->setNodeDisplayedHidden(node, true);
369 return mp;
370 }
371
372 // Show it inline if showOnlyOneMimePart(), which means the user clicked the image
373 // in the message structure viewer manually, and therefore wants to see the full image
374 if (result.isImage() && onlyOneMimePart && !result.neverDisplayInline()) {
375 mNodeHelper->setNodeDisplayedEmbedded(node, true);
376 }
377
378 return mp;
379}
380
381KMMsgSignatureState ProcessResult::inlineSignatureState() const
382{
383 return mInlineSignatureState;
384}
385
386void ProcessResult::setInlineSignatureState(KMMsgSignatureState state)
387{
388 mInlineSignatureState = state;
389}
390
391KMMsgEncryptionState ProcessResult::inlineEncryptionState() const
392{
393 return mInlineEncryptionState;
394}
395
396void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state)
397{
398 mInlineEncryptionState = state;
399}
400
401bool ProcessResult::neverDisplayInline() const
402{
403 return mNeverDisplayInline;
404}
405
406void ProcessResult::setNeverDisplayInline(bool display)
407{
408 mNeverDisplayInline = display;
409}
410
411bool ProcessResult::isImage() const
412{
413 return mIsImage;
414}
415
416void ProcessResult::setIsImage(bool image)
417{
418 mIsImage = image;
419}
420
421void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const
422{
423 if ((inlineSignatureState() != KMMsgNotSigned) ||
424 (inlineEncryptionState() != KMMsgNotEncrypted)) {
425 mNodeHelper->setSignatureState(node, inlineSignatureState());
426 mNodeHelper->setEncryptionState(node, inlineEncryptionState());
427 }
428}
429
430void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart)
431{
432 if (isFirstTextPart) {
433 mPlainTextContent += curNode->decodedText();
434 mPlainTextContentCharset += NodeHelper::charset(curNode);
435 }
436}
437
438void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent)
439{
440 mPlainTextContent = plainTextContent;
441}
442
443const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const
444{
445 Q_ASSERT(node);
446 if (mSource->overrideCodec()) {
447 return mSource->overrideCodec();
448 }
449 return mNodeHelper->codec(node);
450}
451
452QByteArray ObjectTreeParser::plainTextContentCharset() const
453{
454 return mPlainTextContentCharset;
455}
456
457QByteArray ObjectTreeParser::htmlContentCharset() const
458{
459 return mHtmlContentCharset;
460}
461
462bool ObjectTreeParser::showOnlyOneMimePart() const
463{
464 return mShowOnlyOneMimePart;
465}
466
467void ObjectTreeParser::setShowOnlyOneMimePart(bool show)
468{
469 mShowOnlyOneMimePart = show;
470}
471
472const AttachmentStrategy *ObjectTreeParser::attachmentStrategy() const
473{
474 return mAttachmentStrategy;
475}
476
477HtmlWriter *ObjectTreeParser::htmlWriter() const
478{
479 if (mHtmlWriter) {
480 return mHtmlWriter;
481 }
482 return mSource->htmlWriter();
483}
484
485MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const
486{
487 return mNodeHelper;
488}