diff options
Diffstat (limited to 'framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp')
-rw-r--r-- | framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp | 488 |
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 | |||
62 | using namespace MimeTreeParser; | ||
63 | |||
64 | ObjectTreeParser::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 | |||
79 | ObjectTreeParser::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 | |||
95 | void 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 | |||
110 | ObjectTreeParser::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 | |||
124 | ObjectTreeParser::~ObjectTreeParser() | ||
125 | { | ||
126 | if (mDeleteNodeHelper) { | ||
127 | delete mNodeHelper; | ||
128 | mNodeHelper = nullptr; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | void ObjectTreeParser::setAllowAsync(bool allow) | ||
133 | { | ||
134 | Q_ASSERT(!mHasPendingAsyncJobs); | ||
135 | mAllowAsync = allow; | ||
136 | } | ||
137 | |||
138 | bool ObjectTreeParser::allowAsync() const | ||
139 | { | ||
140 | return mAllowAsync; | ||
141 | } | ||
142 | |||
143 | bool ObjectTreeParser::hasPendingAsyncJobs() const | ||
144 | { | ||
145 | return mHasPendingAsyncJobs; | ||
146 | } | ||
147 | |||
148 | QString ObjectTreeParser::plainTextContent() const | ||
149 | { | ||
150 | return mPlainTextContent; | ||
151 | } | ||
152 | |||
153 | QString ObjectTreeParser::htmlContent() const | ||
154 | { | ||
155 | return mHtmlContent; | ||
156 | } | ||
157 | |||
158 | void 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 | |||
172 | void 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 | |||
194 | MessagePartPtr ObjectTreeParser::parsedPart() const | ||
195 | { | ||
196 | return mParsedPart; | ||
197 | } | ||
198 | |||
199 | bool 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 | |||
257 | MessagePart::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 | |||
335 | Interface::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 | |||
381 | KMMsgSignatureState ProcessResult::inlineSignatureState() const | ||
382 | { | ||
383 | return mInlineSignatureState; | ||
384 | } | ||
385 | |||
386 | void ProcessResult::setInlineSignatureState(KMMsgSignatureState state) | ||
387 | { | ||
388 | mInlineSignatureState = state; | ||
389 | } | ||
390 | |||
391 | KMMsgEncryptionState ProcessResult::inlineEncryptionState() const | ||
392 | { | ||
393 | return mInlineEncryptionState; | ||
394 | } | ||
395 | |||
396 | void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state) | ||
397 | { | ||
398 | mInlineEncryptionState = state; | ||
399 | } | ||
400 | |||
401 | bool ProcessResult::neverDisplayInline() const | ||
402 | { | ||
403 | return mNeverDisplayInline; | ||
404 | } | ||
405 | |||
406 | void ProcessResult::setNeverDisplayInline(bool display) | ||
407 | { | ||
408 | mNeverDisplayInline = display; | ||
409 | } | ||
410 | |||
411 | bool ProcessResult::isImage() const | ||
412 | { | ||
413 | return mIsImage; | ||
414 | } | ||
415 | |||
416 | void ProcessResult::setIsImage(bool image) | ||
417 | { | ||
418 | mIsImage = image; | ||
419 | } | ||
420 | |||
421 | void 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 | |||
430 | void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart) | ||
431 | { | ||
432 | if (isFirstTextPart) { | ||
433 | mPlainTextContent += curNode->decodedText(); | ||
434 | mPlainTextContentCharset += NodeHelper::charset(curNode); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent) | ||
439 | { | ||
440 | mPlainTextContent = plainTextContent; | ||
441 | } | ||
442 | |||
443 | const 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 | |||
452 | QByteArray ObjectTreeParser::plainTextContentCharset() const | ||
453 | { | ||
454 | return mPlainTextContentCharset; | ||
455 | } | ||
456 | |||
457 | QByteArray ObjectTreeParser::htmlContentCharset() const | ||
458 | { | ||
459 | return mHtmlContentCharset; | ||
460 | } | ||
461 | |||
462 | bool ObjectTreeParser::showOnlyOneMimePart() const | ||
463 | { | ||
464 | return mShowOnlyOneMimePart; | ||
465 | } | ||
466 | |||
467 | void ObjectTreeParser::setShowOnlyOneMimePart(bool show) | ||
468 | { | ||
469 | mShowOnlyOneMimePart = show; | ||
470 | } | ||
471 | |||
472 | const AttachmentStrategy *ObjectTreeParser::attachmentStrategy() const | ||
473 | { | ||
474 | return mAttachmentStrategy; | ||
475 | } | ||
476 | |||
477 | HtmlWriter *ObjectTreeParser::htmlWriter() const | ||
478 | { | ||
479 | if (mHtmlWriter) { | ||
480 | return mHtmlWriter; | ||
481 | } | ||
482 | return mSource->htmlWriter(); | ||
483 | } | ||
484 | |||
485 | MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const | ||
486 | { | ||
487 | return mNodeHelper; | ||
488 | } | ||