summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-05-23 19:13:13 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-05-23 19:13:13 +0200
commitb968ea8ed364238c57c3e74cf2c122cb897cfbea (patch)
tree7a2dca2199906413a2d0b7d075ded0e4d5ffb69f /framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp
parentc1ca732bafc60f5c140ef5516e32bd46503bf68c (diff)
downloadkube-b968ea8ed364238c57c3e74cf2c122cb897cfbea.tar.gz
kube-b968ea8ed364238c57c3e74cf2c122cb897cfbea.zip
Builds but doesn't link, no formatters yet
Diffstat (limited to 'framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp')
-rw-r--r--framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp495
1 files changed, 495 insertions, 0 deletions
diff --git a/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp b/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp
new file mode 100644
index 00000000..b0d514b6
--- /dev/null
+++ b/framework/src/domain/mimetreeparser/otp/objecttreeparser.cpp
@@ -0,0 +1,495 @@
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 "messagepartrenderer.h"
51#include "util.h"
52
53#include <KMime/Headers>
54#include <KMime/Message>
55
56// KDE includes
57
58// Qt includes
59#include <QByteArray>
60#include <QTextCodec>
61#include <QUrl>
62
63using namespace MimeTreeParser;
64
65ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser,
66 bool showOnlyOneMimePart,
67 const AttachmentStrategy *strategy)
68 : mSource(topLevelParser->mSource),
69 mNodeHelper(topLevelParser->mNodeHelper),
70 mHtmlWriter(topLevelParser->mHtmlWriter),
71 mTopLevelContent(topLevelParser->mTopLevelContent),
72 mShowOnlyOneMimePart(showOnlyOneMimePart),
73 mHasPendingAsyncJobs(false),
74 mAllowAsync(topLevelParser->mAllowAsync),
75 mAttachmentStrategy(strategy)
76{
77 init();
78}
79
80ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source,
81 MimeTreeParser::NodeHelper *nodeHelper,
82 bool showOnlyOneMimePart,
83 const AttachmentStrategy *strategy)
84 : mSource(source),
85 mNodeHelper(nodeHelper),
86 mHtmlWriter(nullptr),
87 mTopLevelContent(nullptr),
88 mShowOnlyOneMimePart(showOnlyOneMimePart),
89 mHasPendingAsyncJobs(false),
90 mAllowAsync(false),
91 mAttachmentStrategy(strategy)
92{
93 init();
94}
95
96void ObjectTreeParser::init()
97{
98 Q_ASSERT(mSource);
99 if (!attachmentStrategy()) {
100 mAttachmentStrategy = mSource->attachmentStrategy();
101 }
102
103 if (!mNodeHelper) {
104 mNodeHelper = new NodeHelper();
105 mDeleteNodeHelper = true;
106 } else {
107 mDeleteNodeHelper = false;
108 }
109}
110
111ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other)
112 : mSource(other.mSource),
113 mNodeHelper(other.nodeHelper()), //TODO(Andras) hm, review what happens if mDeleteNodeHelper was true in the source
114 mHtmlWriter(other.mHtmlWriter),
115 mTopLevelContent(other.mTopLevelContent),
116 mShowOnlyOneMimePart(other.showOnlyOneMimePart()),
117 mHasPendingAsyncJobs(other.hasPendingAsyncJobs()),
118 mAllowAsync(other.allowAsync()),
119 mAttachmentStrategy(other.attachmentStrategy()),
120 mDeleteNodeHelper(false)
121{
122
123}
124
125ObjectTreeParser::~ObjectTreeParser()
126{
127 if (mDeleteNodeHelper) {
128 delete mNodeHelper;
129 mNodeHelper = nullptr;
130 }
131}
132
133void ObjectTreeParser::setAllowAsync(bool allow)
134{
135 Q_ASSERT(!mHasPendingAsyncJobs);
136 mAllowAsync = allow;
137}
138
139bool ObjectTreeParser::allowAsync() const
140{
141 return mAllowAsync;
142}
143
144bool ObjectTreeParser::hasPendingAsyncJobs() const
145{
146 return mHasPendingAsyncJobs;
147}
148
149QString ObjectTreeParser::plainTextContent() const
150{
151 return mPlainTextContent;
152}
153
154QString ObjectTreeParser::htmlContent() const
155{
156 return mHtmlContent;
157}
158
159void ObjectTreeParser::copyContentFrom(const ObjectTreeParser *other)
160{
161 mPlainTextContent += other->plainTextContent();
162 mHtmlContent += other->htmlContent();
163 if (!other->plainTextContentCharset().isEmpty()) {
164 mPlainTextContentCharset = other->plainTextContentCharset();
165 }
166 if (!other->htmlContentCharset().isEmpty()) {
167 mHtmlContentCharset = other->htmlContentCharset();
168 }
169}
170
171//-----------------------------------------------------------------------------
172
173void ObjectTreeParser::parseObjectTree(KMime::Content *node)
174{
175 mTopLevelContent = node;
176 mParsedPart = parseObjectTreeInternal(node, showOnlyOneMimePart());
177
178 if (mParsedPart) {
179 mParsedPart->fix();
180 mParsedPart->copyContentFrom();
181 if (auto mp = toplevelTextNode(mParsedPart)) {
182 if (auto _mp = mp.dynamicCast<TextMessagePart>()) {
183 extractNodeInfos(_mp->mNode, true);
184 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) {
185 if (_mp->mChildNodes.contains(Util::MultipartPlain)) {
186 extractNodeInfos(_mp->mChildNodes[Util::MultipartPlain], true);
187 }
188 }
189 setPlainTextContent(mp->text());
190 }
191
192 if (htmlWriter()) {
193 const auto renderer = mSource->messagePartTheme(mParsedPart);
194 if (renderer) {
195 mHtmlWriter->queue(renderer->html());
196 }
197 }
198 }
199}
200
201MessagePartPtr ObjectTreeParser::parsedPart() const
202{
203 return mParsedPart;
204}
205
206bool ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart)
207{
208 bool bRendered = false;
209 const auto sub = mSource->bodyPartFormatterFactory()->subtypeRegistry(mediaType.constData());
210 auto range = sub.equal_range(subType.constData());
211 for (auto it = range.first; it != range.second; ++it) {
212 const auto formatter = (*it).second;
213 if (!formatter) {
214 continue;
215 }
216 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper);
217 // Set the default display strategy for this body part relying on the
218 // identity of Interface::BodyPart::Display and AttachmentStrategy::Display
219 part.setDefaultDisplay((Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay(node));
220
221 mNodeHelper->setNodeDisplayedEmbedded(node, true);
222
223 const Interface::MessagePart::Ptr result = formatter->process(part);
224 if (!result) {
225 continue;
226 }
227
228 if (const auto mp = result.dynamicCast<MessagePart>()) {
229 mp->setAttachmentFlag(node);
230 mpRet = result;
231 bRendered = true;
232 break;
233 } else if (dynamic_cast<MimeTreeParser::Interface::MessagePart *>(result.data())) {
234 QObject *asyncResultObserver = allowAsync() ? mSource->sourceObject() : nullptr;
235 const auto r = formatter->format(&part, result->htmlWriter(), asyncResultObserver);
236 if (r == Interface::BodyPartFormatter::AsIcon) {
237 processResult.setNeverDisplayInline(true);
238 formatter->adaptProcessResult(processResult);
239 mNodeHelper->setNodeDisplayedEmbedded(node, false);
240 const Interface::MessagePart::Ptr mp = defaultHandling(node, processResult, onlyOneMimePart);
241 if (mp) {
242 if (auto _mp = mp.dynamicCast<MessagePart>()) {
243 _mp->setAttachmentFlag(node);
244 }
245 mpRet = mp;
246 }
247 bRendered = true;
248 break;
249 } else if (r == Interface::BodyPartFormatter::Ok) {
250 processResult.setNeverDisplayInline(true);
251 formatter->adaptProcessResult(processResult);
252 mpRet = result;
253 bRendered = true;
254 break;
255 }
256 continue;
257 } else {
258 continue;
259 }
260 }
261 return bRendered;
262}
263
264MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
265{
266 if (!node) {
267 return MessagePart::Ptr();
268 }
269
270 // reset pending async jobs state (we'll rediscover pending jobs as we go)
271 mHasPendingAsyncJobs = false;
272
273 // reset "processed" flags for...
274 if (onlyOneMimePart) {
275 // ... this node and all descendants
276 mNodeHelper->setNodeUnprocessed(node, false);
277 if (!node->contents().isEmpty()) {
278 mNodeHelper->setNodeUnprocessed(node, true);
279 }
280 } else if (!node->parent()) {
281 // ...this node and all it's siblings and descendants
282 mNodeHelper->setNodeUnprocessed(node, true);
283 }
284
285 const bool isRoot = node->isTopLevel();
286 auto parsedPart = MessagePart::Ptr(new MessagePartList(this));
287 parsedPart->setIsRoot(isRoot);
288 KMime::Content *parent = node->parent();
289 auto contents = parent ? parent->contents() : KMime::Content::List();
290 if (contents.isEmpty()) {
291 contents.append(node);
292 }
293 int i = contents.indexOf(const_cast<KMime::Content *>(node));
294 for (; i < contents.size(); ++i) {
295 node = contents.at(i);
296 if (mNodeHelper->nodeProcessed(node)) {
297 continue;
298 }
299
300 ProcessResult processResult(mNodeHelper);
301
302 QByteArray mediaType("text");
303 QByteArray subType("plain");
304 if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() &&
305 !node->contentType()->subType().isEmpty()) {
306 mediaType = node->contentType()->mediaType();
307 subType = node->contentType()->subType();
308 }
309
310 Interface::MessagePartPtr mp;
311 if (processType(node, processResult, mediaType, subType, mp, onlyOneMimePart)) {
312 if (mp) {
313 parsedPart->appendSubPart(mp);
314 }
315 } else if (processType(node, processResult, mediaType, "*", mp, onlyOneMimePart)) {
316 if (mp) {
317 parsedPart->appendSubPart(mp);
318 }
319 } else {
320 qCWarning(MIMETREEPARSER_LOG) << "THIS SHOULD NO LONGER HAPPEN:" << mediaType << '/' << subType;
321 const auto mp = defaultHandling(node, processResult, onlyOneMimePart);
322 if (mp) {
323 if (auto _mp = mp.dynamicCast<MessagePart>()) {
324 _mp->setAttachmentFlag(node);
325 }
326 parsedPart->appendSubPart(mp);
327 }
328 }
329 mNodeHelper->setNodeProcessed(node, false);
330
331 // adjust signed/encrypted flags if inline PGP was found
332 processResult.adjustCryptoStatesOfNode(node);
333
334 if (onlyOneMimePart) {
335 break;
336 }
337 }
338
339 return parsedPart;
340}
341
342Interface::MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node, ProcessResult &result, bool onlyOneMimePart)
343{
344 Interface::MessagePart::Ptr mp;
345 ProcessResult processResult(mNodeHelper);
346
347 if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream") &&
348 (node->contentType()->name().endsWith(QLatin1String("p7m")) ||
349 node->contentType()->name().endsWith(QLatin1String("p7s")) ||
350 node->contentType()->name().endsWith(QLatin1String("p7c"))
351 ) &&
352 processType(node, processResult, "application", "pkcs7-mime", mp, onlyOneMimePart)) {
353 return mp;
354 }
355
356 const auto _mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node, false, true, mSource->decryptMessage()));
357 result.setInlineSignatureState(_mp->signatureState());
358 result.setInlineEncryptionState(_mp->encryptionState());
359 _mp->setNeverDisplayInline(result.neverDisplayInline());
360 _mp->setIsImage(result.isImage());
361 mp = _mp;
362
363 // always show images in multipart/related when showing in html, not with an additional icon
364 auto preferredMode = mSource->preferredMode();
365 bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml);
366 if (result.isImage() && node->parent() &&
367 node->parent()->contentType()->subType() == "related" && isHtmlPreferred && !onlyOneMimePart) {
368 QString fileName = mNodeHelper->writeNodeToTempFile(node);
369 QString href = QUrl::fromLocalFile(fileName).url();
370 QByteArray cid = node->contentID()->identifier();
371 if (htmlWriter()) {
372 htmlWriter()->embedPart(cid, href);
373 }
374 nodeHelper()->setNodeDisplayedEmbedded(node, true);
375 mNodeHelper->setNodeDisplayedHidden(node, true);
376 return mp;
377 }
378
379 // Show it inline if showOnlyOneMimePart(), which means the user clicked the image
380 // in the message structure viewer manually, and therefore wants to see the full image
381 if (result.isImage() && onlyOneMimePart && !result.neverDisplayInline()) {
382 mNodeHelper->setNodeDisplayedEmbedded(node, true);
383 }
384
385 return mp;
386}
387
388KMMsgSignatureState ProcessResult::inlineSignatureState() const
389{
390 return mInlineSignatureState;
391}
392
393void ProcessResult::setInlineSignatureState(KMMsgSignatureState state)
394{
395 mInlineSignatureState = state;
396}
397
398KMMsgEncryptionState ProcessResult::inlineEncryptionState() const
399{
400 return mInlineEncryptionState;
401}
402
403void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state)
404{
405 mInlineEncryptionState = state;
406}
407
408bool ProcessResult::neverDisplayInline() const
409{
410 return mNeverDisplayInline;
411}
412
413void ProcessResult::setNeverDisplayInline(bool display)
414{
415 mNeverDisplayInline = display;
416}
417
418bool ProcessResult::isImage() const
419{
420 return mIsImage;
421}
422
423void ProcessResult::setIsImage(bool image)
424{
425 mIsImage = image;
426}
427
428void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const
429{
430 if ((inlineSignatureState() != KMMsgNotSigned) ||
431 (inlineEncryptionState() != KMMsgNotEncrypted)) {
432 mNodeHelper->setSignatureState(node, inlineSignatureState());
433 mNodeHelper->setEncryptionState(node, inlineEncryptionState());
434 }
435}
436
437void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart)
438{
439 if (isFirstTextPart) {
440 mPlainTextContent += curNode->decodedText();
441 mPlainTextContentCharset += NodeHelper::charset(curNode);
442 }
443}
444
445void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent)
446{
447 mPlainTextContent = plainTextContent;
448}
449
450const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const
451{
452 Q_ASSERT(node);
453 if (mSource->overrideCodec()) {
454 return mSource->overrideCodec();
455 }
456 return mNodeHelper->codec(node);
457}
458
459QByteArray ObjectTreeParser::plainTextContentCharset() const
460{
461 return mPlainTextContentCharset;
462}
463
464QByteArray ObjectTreeParser::htmlContentCharset() const
465{
466 return mHtmlContentCharset;
467}
468
469bool ObjectTreeParser::showOnlyOneMimePart() const
470{
471 return mShowOnlyOneMimePart;
472}
473
474void ObjectTreeParser::setShowOnlyOneMimePart(bool show)
475{
476 mShowOnlyOneMimePart = show;
477}
478
479const AttachmentStrategy *ObjectTreeParser::attachmentStrategy() const
480{
481 return mAttachmentStrategy;
482}
483
484HtmlWriter *ObjectTreeParser::htmlWriter() const
485{
486 if (mHtmlWriter) {
487 return mHtmlWriter;
488 }
489 return mSource->htmlWriter();
490}
491
492MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const
493{
494 return mNodeHelper;
495}