diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-05-23 19:13:13 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-05-23 19:13:13 +0200 |
commit | b968ea8ed364238c57c3e74cf2c122cb897cfbea (patch) | |
tree | 7a2dca2199906413a2d0b7d075ded0e4d5ffb69f /framework/src/domain/mimetreeparser/otp/nodehelper.cpp | |
parent | c1ca732bafc60f5c140ef5516e32bd46503bf68c (diff) | |
download | kube-b968ea8ed364238c57c3e74cf2c122cb897cfbea.tar.gz kube-b968ea8ed364238c57c3e74cf2c122cb897cfbea.zip |
Builds but doesn't link, no formatters yet
Diffstat (limited to 'framework/src/domain/mimetreeparser/otp/nodehelper.cpp')
-rw-r--r-- | framework/src/domain/mimetreeparser/otp/nodehelper.cpp | 1069 |
1 files changed, 1069 insertions, 0 deletions
diff --git a/framework/src/domain/mimetreeparser/otp/nodehelper.cpp b/framework/src/domain/mimetreeparser/otp/nodehelper.cpp new file mode 100644 index 00000000..8e224f1b --- /dev/null +++ b/framework/src/domain/mimetreeparser/otp/nodehelper.cpp | |||
@@ -0,0 +1,1069 @@ | |||
1 | /* | ||
2 | Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net | ||
3 | Copyright (c) 2009 Andras Mantia <andras@kdab.net> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License along | ||
16 | with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | ||
19 | |||
20 | #include "nodehelper.h" | ||
21 | #include "mimetreeparser_debug.h" | ||
22 | #include "partmetadata.h" | ||
23 | #include "bodypart.h" | ||
24 | #include "attachmenttemporaryfilesdirs.h" | ||
25 | |||
26 | #include <KMime/Content> | ||
27 | #include <KMime/Message> | ||
28 | #include <KMime/Headers> | ||
29 | |||
30 | #include <QTemporaryFile> | ||
31 | #include <KLocalizedString> | ||
32 | #include <kcharsets.h> | ||
33 | |||
34 | #include <QUrl> | ||
35 | #include <QDir> | ||
36 | #include <QTextCodec> | ||
37 | |||
38 | #include <string> | ||
39 | #include <sstream> | ||
40 | #include <algorithm> | ||
41 | #include <KCharsets> | ||
42 | #include <QMimeDatabase> | ||
43 | #include <QMimeType> | ||
44 | #include <QFileDevice> | ||
45 | |||
46 | namespace MimeTreeParser | ||
47 | { | ||
48 | |||
49 | QStringList replySubjPrefixes(QStringList() << QStringLiteral("Re\\s*:") << QStringLiteral("Re\\[\\d+\\]:") << QStringLiteral("Re\\d+:")); | ||
50 | QStringList forwardSubjPrefixes(QStringList() << QStringLiteral("Fwd:") << QStringLiteral("FW:")); | ||
51 | |||
52 | NodeHelper::NodeHelper() : | ||
53 | mAttachmentFilesDir(new AttachmentTemporaryFilesDirs()) | ||
54 | { | ||
55 | //TODO(Andras) add methods to modify these prefixes | ||
56 | |||
57 | mLocalCodec = QTextCodec::codecForLocale(); | ||
58 | |||
59 | // In the case of Japan. Japanese locale name is "eucjp" but | ||
60 | // The Japanese mail systems normally used "iso-2022-jp" of locale name. | ||
61 | // We want to change locale name from eucjp to iso-2022-jp at KMail only. | ||
62 | |||
63 | // (Introduction to i18n, 6.6 Limit of Locale technology): | ||
64 | // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP | ||
65 | // is the standard for Internet, and Shift-JIS is the encoding | ||
66 | // for Windows and Macintosh. | ||
67 | if (mLocalCodec) { | ||
68 | const QByteArray codecNameLower = mLocalCodec->name().toLower(); | ||
69 | if (codecNameLower == "eucjp" | ||
70 | #if defined Q_OS_WIN || defined Q_OS_MACX | ||
71 | || codecNameLower == "shift-jis" // OK? | ||
72 | #endif | ||
73 | ) { | ||
74 | mLocalCodec = QTextCodec::codecForName("jis7"); | ||
75 | // QTextCodec *cdc = QTextCodec::codecForName("jis7"); | ||
76 | // QTextCodec::setCodecForLocale(cdc); | ||
77 | // KLocale::global()->setEncoding(cdc->mibEnum()); | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | NodeHelper::~NodeHelper() | ||
83 | { | ||
84 | if (mAttachmentFilesDir) { | ||
85 | mAttachmentFilesDir->forceCleanTempFiles(); | ||
86 | delete mAttachmentFilesDir; | ||
87 | mAttachmentFilesDir = nullptr; | ||
88 | } | ||
89 | clear(); | ||
90 | } | ||
91 | |||
92 | void NodeHelper::setNodeProcessed(KMime::Content *node, bool recurse) | ||
93 | { | ||
94 | if (!node) { | ||
95 | return; | ||
96 | } | ||
97 | mProcessedNodes.append(node); | ||
98 | qCDebug(MIMETREEPARSER_LOG) << "Node processed: " << node->index().toString() << node->contentType()->as7BitString(); | ||
99 | //<< " decodedContent" << node->decodedContent(); | ||
100 | if (recurse) { | ||
101 | const auto contents = node->contents(); | ||
102 | for (KMime::Content *c : contents) { | ||
103 | setNodeProcessed(c, true); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | void NodeHelper::setNodeUnprocessed(KMime::Content *node, bool recurse) | ||
109 | { | ||
110 | if (!node) { | ||
111 | return; | ||
112 | } | ||
113 | mProcessedNodes.removeAll(node); | ||
114 | |||
115 | //avoid double addition of extra nodes, eg. encrypted attachments | ||
116 | const QMap<KMime::Content *, QList<KMime::Content *> >::iterator it = mExtraContents.find(node); | ||
117 | if (it != mExtraContents.end()) { | ||
118 | Q_FOREACH (KMime::Content *c, it.value()) { | ||
119 | KMime::Content *p = c->parent(); | ||
120 | if (p) { | ||
121 | p->removeContent(c); | ||
122 | } | ||
123 | } | ||
124 | qDeleteAll(it.value()); | ||
125 | qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); | ||
126 | mExtraContents.erase(it); | ||
127 | } | ||
128 | |||
129 | qCDebug(MIMETREEPARSER_LOG) << "Node UNprocessed: " << node; | ||
130 | if (recurse) { | ||
131 | const auto contents = node->contents(); | ||
132 | for (KMime::Content *c : contents) { | ||
133 | setNodeUnprocessed(c, true); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | bool NodeHelper::nodeProcessed(KMime::Content *node) const | ||
139 | { | ||
140 | if (!node) { | ||
141 | return true; | ||
142 | } | ||
143 | return mProcessedNodes.contains(node); | ||
144 | } | ||
145 | |||
146 | static void clearBodyPartMemento(QMap<QByteArray, Interface::BodyPartMemento *> &bodyPartMementoMap) | ||
147 | { | ||
148 | for (QMap<QByteArray, Interface::BodyPartMemento *>::iterator | ||
149 | it = bodyPartMementoMap.begin(), end = bodyPartMementoMap.end(); | ||
150 | it != end; ++it) { | ||
151 | Interface::BodyPartMemento *memento = it.value(); | ||
152 | memento->detach(); | ||
153 | delete memento; | ||
154 | } | ||
155 | bodyPartMementoMap.clear(); | ||
156 | } | ||
157 | |||
158 | void NodeHelper::clear() | ||
159 | { | ||
160 | mProcessedNodes.clear(); | ||
161 | mEncryptionState.clear(); | ||
162 | mSignatureState.clear(); | ||
163 | mOverrideCodecs.clear(); | ||
164 | std::for_each(mBodyPartMementoMap.begin(), mBodyPartMementoMap.end(), | ||
165 | &clearBodyPartMemento); | ||
166 | mBodyPartMementoMap.clear(); | ||
167 | QMap<KMime::Content *, QList<KMime::Content *> >::ConstIterator end(mExtraContents.constEnd()); | ||
168 | |||
169 | for (QMap<KMime::Content *, QList<KMime::Content *> >::ConstIterator it = mExtraContents.constBegin(); it != end; ++it) { | ||
170 | Q_FOREACH (KMime::Content *c, it.value()) { | ||
171 | KMime::Content *p = c->parent(); | ||
172 | if (p) { | ||
173 | p->removeContent(c); | ||
174 | } | ||
175 | } | ||
176 | qDeleteAll(it.value()); | ||
177 | qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); | ||
178 | } | ||
179 | mExtraContents.clear(); | ||
180 | mDisplayEmbeddedNodes.clear(); | ||
181 | mDisplayHiddenNodes.clear(); | ||
182 | } | ||
183 | |||
184 | void NodeHelper::setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state) | ||
185 | { | ||
186 | mEncryptionState[node] = state; | ||
187 | } | ||
188 | |||
189 | KMMsgEncryptionState NodeHelper::encryptionState(const KMime::Content *node) const | ||
190 | { | ||
191 | return mEncryptionState.value(node, KMMsgNotEncrypted); | ||
192 | } | ||
193 | |||
194 | void NodeHelper::setSignatureState(const KMime::Content *node, const KMMsgSignatureState state) | ||
195 | { | ||
196 | mSignatureState[node] = state; | ||
197 | } | ||
198 | |||
199 | KMMsgSignatureState NodeHelper::signatureState(const KMime::Content *node) const | ||
200 | { | ||
201 | return mSignatureState.value(node, KMMsgNotSigned); | ||
202 | } | ||
203 | |||
204 | PartMetaData NodeHelper::partMetaData(KMime::Content *node) | ||
205 | { | ||
206 | return mPartMetaDatas.value(node, PartMetaData()); | ||
207 | } | ||
208 | |||
209 | void NodeHelper::setPartMetaData(KMime::Content *node, const PartMetaData &metaData) | ||
210 | { | ||
211 | mPartMetaDatas.insert(node, metaData); | ||
212 | } | ||
213 | |||
214 | QString NodeHelper::writeNodeToTempFile(KMime::Content *node) | ||
215 | { | ||
216 | // If the message part is already written to a file, no point in doing it again. | ||
217 | // This function is called twice actually, once from the rendering of the attachment | ||
218 | // in the body and once for the header. | ||
219 | QUrl existingFileName = tempFileUrlFromNode(node); | ||
220 | if (!existingFileName.isEmpty()) { | ||
221 | return existingFileName.toLocalFile(); | ||
222 | } | ||
223 | |||
224 | QString fname = createTempDir(persistentIndex(node)); | ||
225 | if (fname.isEmpty()) { | ||
226 | return QString(); | ||
227 | } | ||
228 | |||
229 | QString fileName = NodeHelper::fileName(node); | ||
230 | // strip off a leading path | ||
231 | int slashPos = fileName.lastIndexOf(QLatin1Char('/')); | ||
232 | if (-1 != slashPos) { | ||
233 | fileName = fileName.mid(slashPos + 1); | ||
234 | } | ||
235 | if (fileName.isEmpty()) { | ||
236 | fileName = QStringLiteral("unnamed"); | ||
237 | } | ||
238 | fname += QLatin1Char('/') + fileName; | ||
239 | |||
240 | qCDebug(MIMETREEPARSER_LOG) << "Create temp file: " << fname; | ||
241 | QByteArray data = node->decodedContent(); | ||
242 | if (node->contentType()->isText() && !data.isEmpty()) { | ||
243 | // convert CRLF to LF before writing text attachments to disk | ||
244 | data = KMime::CRLFtoLF(data); | ||
245 | } | ||
246 | QFile f(fname); | ||
247 | if (!f.open(QIODevice::ReadWrite)) { | ||
248 | qCWarning(MIMETREEPARSER_LOG) << "Failed to write note to file:" << f.errorString(); | ||
249 | return QString(); | ||
250 | } | ||
251 | f.write(data); | ||
252 | mAttachmentFilesDir->addTempFile(fname); | ||
253 | // make file read-only so that nobody gets the impression that he might | ||
254 | // edit attached files (cf. bug #52813) | ||
255 | f.setPermissions(QFileDevice::ReadUser); | ||
256 | f.close(); | ||
257 | |||
258 | return fname; | ||
259 | } | ||
260 | |||
261 | QUrl NodeHelper::tempFileUrlFromNode(const KMime::Content *node) | ||
262 | { | ||
263 | if (!node) { | ||
264 | return QUrl(); | ||
265 | } | ||
266 | |||
267 | const QString index = persistentIndex(node); | ||
268 | |||
269 | foreach (const QString &path, mAttachmentFilesDir->temporaryFiles()) { | ||
270 | const int right = path.lastIndexOf(QLatin1Char('/')); | ||
271 | int left = path.lastIndexOf(QLatin1String(".index."), right); | ||
272 | if (left != -1) { | ||
273 | left += 7; | ||
274 | } | ||
275 | |||
276 | QStringRef storedIndex(&path, left, right - left); | ||
277 | if (left != -1 && storedIndex == index) { | ||
278 | return QUrl::fromLocalFile(path); | ||
279 | } | ||
280 | } | ||
281 | return QUrl(); | ||
282 | } | ||
283 | |||
284 | QString NodeHelper::createTempDir(const QString ¶m) | ||
285 | { | ||
286 | QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/messageviewer_XXXXXX") + QLatin1String(".index.") + param); | ||
287 | tempFile->open(); | ||
288 | const QString fname = tempFile->fileName(); | ||
289 | delete tempFile; | ||
290 | |||
291 | QFile fFile(fname); | ||
292 | if (!(fFile.permissions() & QFileDevice::WriteUser)) { | ||
293 | // Not there or not writable | ||
294 | if (!QDir().mkpath(fname) || | ||
295 | !fFile.setPermissions(QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser)) { | ||
296 | return QString(); //failed create | ||
297 | } | ||
298 | } | ||
299 | |||
300 | Q_ASSERT(!fname.isNull()); | ||
301 | |||
302 | mAttachmentFilesDir->addTempDir(fname); | ||
303 | return fname; | ||
304 | } | ||
305 | |||
306 | void NodeHelper::forceCleanTempFiles() | ||
307 | { | ||
308 | mAttachmentFilesDir->forceCleanTempFiles(); | ||
309 | delete mAttachmentFilesDir; | ||
310 | mAttachmentFilesDir = nullptr; | ||
311 | } | ||
312 | |||
313 | void NodeHelper::removeTempFiles() | ||
314 | { | ||
315 | //Don't delete it it will delete in class | ||
316 | mAttachmentFilesDir->removeTempFiles(); | ||
317 | mAttachmentFilesDir = new AttachmentTemporaryFilesDirs(); | ||
318 | } | ||
319 | |||
320 | void NodeHelper::addTempFile(const QString &file) | ||
321 | { | ||
322 | mAttachmentFilesDir->addTempFile(file); | ||
323 | } | ||
324 | |||
325 | bool NodeHelper::isInEncapsulatedMessage(KMime::Content *node) | ||
326 | { | ||
327 | const KMime::Content *const topLevel = node->topLevel(); | ||
328 | const KMime::Content *cur = node; | ||
329 | while (cur && cur != topLevel) { | ||
330 | const bool parentIsMessage = cur->parent() && cur->parent()->contentType(false) && | ||
331 | cur->parent()->contentType()->mimeType().toLower() == "message/rfc822"; | ||
332 | if (parentIsMessage && cur->parent() != topLevel) { | ||
333 | return true; | ||
334 | } | ||
335 | cur = cur->parent(); | ||
336 | } | ||
337 | return false; | ||
338 | } | ||
339 | |||
340 | QByteArray NodeHelper::charset(KMime::Content *node) | ||
341 | { | ||
342 | if (node->contentType(false)) { | ||
343 | return node->contentType(false)->charset(); | ||
344 | } else { | ||
345 | return node->defaultCharset(); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | KMMsgEncryptionState NodeHelper::overallEncryptionState(KMime::Content *node) const | ||
350 | { | ||
351 | KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown; | ||
352 | if (!node) { | ||
353 | return myState; | ||
354 | } | ||
355 | |||
356 | KMime::Content *parent = node->parent(); | ||
357 | auto contents = parent ? parent->contents() : KMime::Content::List(); | ||
358 | if (contents.isEmpty()) { | ||
359 | contents.append(node); | ||
360 | } | ||
361 | int i = contents.indexOf(const_cast<KMime::Content *>(node)); | ||
362 | for (; i < contents.size(); ++i) { | ||
363 | auto next = contents.at(i); | ||
364 | KMMsgEncryptionState otherState = encryptionState(next); | ||
365 | |||
366 | // NOTE: children are tested ONLY when parent is not encrypted | ||
367 | if (otherState == KMMsgNotEncrypted && !next->contents().isEmpty()) { | ||
368 | otherState = overallEncryptionState(next->contents().at(0)); | ||
369 | } | ||
370 | |||
371 | if (otherState == KMMsgNotEncrypted && !extraContents(next).isEmpty()) { | ||
372 | otherState = overallEncryptionState(extraContents(next).at(0)); | ||
373 | } | ||
374 | |||
375 | if (next == node) { | ||
376 | myState = otherState; | ||
377 | } | ||
378 | |||
379 | switch (otherState) { | ||
380 | case KMMsgEncryptionStateUnknown: | ||
381 | break; | ||
382 | case KMMsgNotEncrypted: | ||
383 | if (myState == KMMsgFullyEncrypted) { | ||
384 | myState = KMMsgPartiallyEncrypted; | ||
385 | } else if (myState != KMMsgPartiallyEncrypted) { | ||
386 | myState = KMMsgNotEncrypted; | ||
387 | } | ||
388 | break; | ||
389 | case KMMsgPartiallyEncrypted: | ||
390 | myState = KMMsgPartiallyEncrypted; | ||
391 | break; | ||
392 | case KMMsgFullyEncrypted: | ||
393 | if (myState != KMMsgFullyEncrypted) { | ||
394 | myState = KMMsgPartiallyEncrypted; | ||
395 | } | ||
396 | break; | ||
397 | case KMMsgEncryptionProblematic: | ||
398 | break; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgEncryptionState:" << myState; | ||
403 | |||
404 | return myState; | ||
405 | } | ||
406 | |||
407 | KMMsgSignatureState NodeHelper::overallSignatureState(KMime::Content *node) const | ||
408 | { | ||
409 | KMMsgSignatureState myState = KMMsgSignatureStateUnknown; | ||
410 | if (!node) { | ||
411 | return myState; | ||
412 | } | ||
413 | |||
414 | KMime::Content *parent = node->parent(); | ||
415 | auto contents = parent ? parent->contents() : KMime::Content::List(); | ||
416 | if (contents.isEmpty()) { | ||
417 | contents.append(node); | ||
418 | } | ||
419 | int i = contents.indexOf(const_cast<KMime::Content *>(node)); | ||
420 | for (; i < contents.size(); ++i) { | ||
421 | auto next = contents.at(i); | ||
422 | KMMsgSignatureState otherState = signatureState(next); | ||
423 | |||
424 | // NOTE: children are tested ONLY when parent is not encrypted | ||
425 | if (otherState == KMMsgNotSigned && !next->contents().isEmpty()) { | ||
426 | otherState = overallSignatureState(next->contents().at(0)); | ||
427 | } | ||
428 | |||
429 | if (otherState == KMMsgNotSigned && !extraContents(next).isEmpty()) { | ||
430 | otherState = overallSignatureState(extraContents(next).at(0)); | ||
431 | } | ||
432 | |||
433 | if (next == node) { | ||
434 | myState = otherState; | ||
435 | } | ||
436 | |||
437 | switch (otherState) { | ||
438 | case KMMsgSignatureStateUnknown: | ||
439 | break; | ||
440 | case KMMsgNotSigned: | ||
441 | if (myState == KMMsgFullySigned) { | ||
442 | myState = KMMsgPartiallySigned; | ||
443 | } else if (myState != KMMsgPartiallySigned) { | ||
444 | myState = KMMsgNotSigned; | ||
445 | } | ||
446 | break; | ||
447 | case KMMsgPartiallySigned: | ||
448 | myState = KMMsgPartiallySigned; | ||
449 | break; | ||
450 | case KMMsgFullySigned: | ||
451 | if (myState != KMMsgFullySigned) { | ||
452 | myState = KMMsgPartiallySigned; | ||
453 | } | ||
454 | break; | ||
455 | case KMMsgSignatureProblematic: | ||
456 | break; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgSignatureState:" << myState; | ||
461 | |||
462 | return myState; | ||
463 | } | ||
464 | |||
465 | void NodeHelper::magicSetType(KMime::Content *node, bool aAutoDecode) | ||
466 | { | ||
467 | const QByteArray body = (aAutoDecode) ? node->decodedContent() : node->body(); | ||
468 | QMimeDatabase db; | ||
469 | QMimeType mime = db.mimeTypeForData(body); | ||
470 | |||
471 | QString mimetype = mime.name(); | ||
472 | node->contentType()->setMimeType(mimetype.toLatin1()); | ||
473 | } | ||
474 | |||
475 | // static | ||
476 | QString NodeHelper::replacePrefixes(const QString &str, | ||
477 | const QStringList &prefixRegExps, | ||
478 | bool replace, | ||
479 | const QString &newPrefix) | ||
480 | { | ||
481 | bool recognized = false; | ||
482 | // construct a big regexp that | ||
483 | // 1. is anchored to the beginning of str (sans whitespace) | ||
484 | // 2. matches at least one of the part regexps in prefixRegExps | ||
485 | QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*") | ||
486 | .arg(prefixRegExps.join(QStringLiteral(")|(?:"))); | ||
487 | QRegExp rx(bigRegExp, Qt::CaseInsensitive); | ||
488 | if (!rx.isValid()) { | ||
489 | qCWarning(MIMETREEPARSER_LOG) << "bigRegExp = \"" | ||
490 | << bigRegExp << "\"\n" | ||
491 | << "prefix regexp is invalid!"; | ||
492 | // try good ole Re/Fwd: | ||
493 | recognized = str.startsWith(newPrefix); | ||
494 | } else { // valid rx | ||
495 | QString tmp = str; | ||
496 | if (rx.indexIn(tmp) == 0) { | ||
497 | recognized = true; | ||
498 | if (replace) { | ||
499 | return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1Char(' ')); | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | if (!recognized) { | ||
504 | return newPrefix + QLatin1Char(' ') + str; | ||
505 | } else { | ||
506 | return str; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | QString NodeHelper::cleanSubject(KMime::Message *message) | ||
511 | { | ||
512 | return cleanSubject(message, replySubjPrefixes + forwardSubjPrefixes, | ||
513 | true, QString()).trimmed(); | ||
514 | } | ||
515 | |||
516 | QString NodeHelper::cleanSubject(KMime::Message *message, | ||
517 | const QStringList &prefixRegExps, | ||
518 | bool replace, | ||
519 | const QString &newPrefix) | ||
520 | { | ||
521 | QString cleanStr; | ||
522 | if (message) { | ||
523 | cleanStr = | ||
524 | NodeHelper::replacePrefixes( | ||
525 | message->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix); | ||
526 | } | ||
527 | return cleanStr; | ||
528 | } | ||
529 | |||
530 | void NodeHelper::setOverrideCodec(KMime::Content *node, const QTextCodec *codec) | ||
531 | { | ||
532 | if (!node) { | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | mOverrideCodecs[node] = codec; | ||
537 | } | ||
538 | |||
539 | const QTextCodec *NodeHelper::codec(KMime::Content *node) | ||
540 | { | ||
541 | if (! node) { | ||
542 | return mLocalCodec; | ||
543 | } | ||
544 | |||
545 | const QTextCodec *c = mOverrideCodecs.value(node, nullptr); | ||
546 | if (!c) { | ||
547 | // no override-codec set for this message, try the CT charset parameter: | ||
548 | QByteArray charset = node->contentType()->charset(); | ||
549 | |||
550 | // utf-8 is a superset of us-ascii, so we don't loose anything, if we it insead | ||
551 | // utf-8 is nowadays that widely, that it is a good guess to use it to fix issus with broken clients. | ||
552 | if (charset.toLower() == "us-ascii") { | ||
553 | charset = "utf-8"; | ||
554 | } | ||
555 | c = codecForName(charset); | ||
556 | } | ||
557 | if (!c) { | ||
558 | // no charset means us-ascii (RFC 2045), so using local encoding should | ||
559 | // be okay | ||
560 | c = mLocalCodec; | ||
561 | } | ||
562 | return c; | ||
563 | } | ||
564 | |||
565 | const QTextCodec *NodeHelper::codecForName(const QByteArray &_str) | ||
566 | { | ||
567 | if (_str.isEmpty()) { | ||
568 | return nullptr; | ||
569 | } | ||
570 | QByteArray codec = _str.toLower(); | ||
571 | return KCharsets::charsets()->codecForName(QLatin1String(codec)); | ||
572 | } | ||
573 | |||
574 | QString NodeHelper::fileName(const KMime::Content *node) | ||
575 | { | ||
576 | QString name = const_cast<KMime::Content *>(node)->contentDisposition()->filename(); | ||
577 | if (name.isEmpty()) { | ||
578 | name = const_cast<KMime::Content *>(node)->contentType()->name(); | ||
579 | } | ||
580 | |||
581 | name = name.trimmed(); | ||
582 | return name; | ||
583 | } | ||
584 | |||
585 | //FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalBodyPartMemento replacement | ||
586 | Interface::BodyPartMemento *NodeHelper::bodyPartMemento(KMime::Content *node, | ||
587 | const QByteArray &which) const | ||
588 | { | ||
589 | const QMap< QString, QMap<QByteArray, Interface::BodyPartMemento *> >::const_iterator nit | ||
590 | = mBodyPartMementoMap.find(persistentIndex(node)); | ||
591 | if (nit == mBodyPartMementoMap.end()) { | ||
592 | return nullptr; | ||
593 | } | ||
594 | const QMap<QByteArray, Interface::BodyPartMemento *>::const_iterator it = | ||
595 | nit->find(which.toLower()); | ||
596 | return it != nit->end() ? it.value() : nullptr; | ||
597 | } | ||
598 | |||
599 | //FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalSetBodyPartMemento replacement | ||
600 | void NodeHelper::setBodyPartMemento(KMime::Content *node, const QByteArray &which, | ||
601 | Interface::BodyPartMemento *memento) | ||
602 | { | ||
603 | QMap<QByteArray, Interface::BodyPartMemento *> &mementos | ||
604 | = mBodyPartMementoMap[persistentIndex(node)]; | ||
605 | |||
606 | const QByteArray whichLower = which.toLower(); | ||
607 | const QMap<QByteArray, Interface::BodyPartMemento *>::iterator it = | ||
608 | mementos.lowerBound(whichLower); | ||
609 | |||
610 | if (it != mementos.end() && it.key() == whichLower) { | ||
611 | delete it.value(); | ||
612 | if (memento) { | ||
613 | it.value() = memento; | ||
614 | } else { | ||
615 | mementos.erase(it); | ||
616 | } | ||
617 | } else { | ||
618 | mementos.insert(whichLower, memento); | ||
619 | } | ||
620 | } | ||
621 | |||
622 | bool NodeHelper::isNodeDisplayedEmbedded(KMime::Content *node) const | ||
623 | { | ||
624 | qCDebug(MIMETREEPARSER_LOG) << "IS NODE: " << mDisplayEmbeddedNodes.contains(node); | ||
625 | return mDisplayEmbeddedNodes.contains(node); | ||
626 | } | ||
627 | |||
628 | void NodeHelper::setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded) | ||
629 | { | ||
630 | qCDebug(MIMETREEPARSER_LOG) << "SET NODE: " << node << displayedEmbedded; | ||
631 | if (displayedEmbedded) { | ||
632 | mDisplayEmbeddedNodes.insert(node); | ||
633 | } else { | ||
634 | mDisplayEmbeddedNodes.remove(node); | ||
635 | } | ||
636 | } | ||
637 | |||
638 | bool NodeHelper::isNodeDisplayedHidden(KMime::Content *node) const | ||
639 | { | ||
640 | return mDisplayHiddenNodes.contains(node); | ||
641 | } | ||
642 | |||
643 | void NodeHelper::setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden) | ||
644 | { | ||
645 | if (displayedHidden) { | ||
646 | mDisplayHiddenNodes.insert(node); | ||
647 | } else { | ||
648 | mDisplayEmbeddedNodes.remove(node); | ||
649 | } | ||
650 | } | ||
651 | |||
652 | /*! | ||
653 | Creates a persistent index string that bridges the gap between the | ||
654 | permanent nodes and the temporary ones. | ||
655 | |||
656 | Used internally for robust indexing. | ||
657 | */ | ||
658 | QString NodeHelper::persistentIndex(const KMime::Content *node) const | ||
659 | { | ||
660 | if (!node) { | ||
661 | return QString(); | ||
662 | } | ||
663 | |||
664 | QString indexStr = node->index().toString(); | ||
665 | if (indexStr.isEmpty()) { | ||
666 | QMapIterator<KMime::Message::Content *, QList<KMime::Content *> > it(mExtraContents); | ||
667 | while (it.hasNext()) { | ||
668 | it.next(); | ||
669 | const auto &extraNodes = it.value(); | ||
670 | for (int i = 0; i < extraNodes.size(); i++) { | ||
671 | if (extraNodes[i] == node) { | ||
672 | indexStr = QString::fromLatin1("e%1").arg(i); | ||
673 | const QString parentIndex = persistentIndex(it.key()); | ||
674 | if (!parentIndex.isEmpty()) { | ||
675 | indexStr = QString::fromLatin1("%1:%2").arg(parentIndex, indexStr); | ||
676 | } | ||
677 | return indexStr; | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | } else { | ||
682 | const KMime::Content *const topLevel = node->topLevel(); | ||
683 | //if the node is an extra node, prepend the index of the extra node to the url | ||
684 | QMapIterator<KMime::Message::Content *, QList<KMime::Content *> > it(mExtraContents); | ||
685 | while (it.hasNext()) { | ||
686 | it.next(); | ||
687 | const QList<KMime::Content *> &extraNodes = extraContents(it.key()); | ||
688 | for (int i = 0; i < extraNodes.size(); ++i) { | ||
689 | KMime::Content *const extraNode = extraNodes[i]; | ||
690 | if (topLevel == extraNode) { | ||
691 | indexStr.prepend(QStringLiteral("e%1:").arg(i)); | ||
692 | const QString parentIndex = persistentIndex(it.key()); | ||
693 | if (!parentIndex.isEmpty()) { | ||
694 | indexStr = QStringLiteral("%1:%2").arg(parentIndex, indexStr); | ||
695 | } | ||
696 | return indexStr; | ||
697 | } | ||
698 | } | ||
699 | } | ||
700 | } | ||
701 | |||
702 | return indexStr; | ||
703 | } | ||
704 | |||
705 | KMime::Content *NodeHelper::contentFromIndex(KMime::Content *node, const QString &persistentIndex) const | ||
706 | { | ||
707 | KMime::Content *c = node->topLevel(); | ||
708 | if (c) { | ||
709 | const QStringList pathParts = persistentIndex.split(QLatin1Char(':'), QString::SkipEmptyParts); | ||
710 | const int pathPartsSize(pathParts.size()); | ||
711 | for (int i = 0; i < pathPartsSize; ++i) { | ||
712 | const QString &path = pathParts[i]; | ||
713 | if (path.startsWith(QLatin1Char('e'))) { | ||
714 | const QList<KMime::Content *> &extraParts = mExtraContents.value(c); | ||
715 | const int idx = path.midRef(1, -1).toInt(); | ||
716 | c = (idx < extraParts.size()) ? extraParts[idx] : nullptr; | ||
717 | } else { | ||
718 | c = c->content(KMime::ContentIndex(path)); | ||
719 | } | ||
720 | if (!c) { | ||
721 | break; | ||
722 | } | ||
723 | } | ||
724 | } | ||
725 | return c; | ||
726 | } | ||
727 | |||
728 | QString NodeHelper::asHREF(const KMime::Content *node, const QString &place) const | ||
729 | { | ||
730 | return QStringLiteral("attachment:%1?place=%2").arg(persistentIndex(node), place); | ||
731 | } | ||
732 | |||
733 | KMime::Content *NodeHelper::fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &url) const | ||
734 | { | ||
735 | if (url.isEmpty()) { | ||
736 | return mMessage.data(); | ||
737 | } | ||
738 | |||
739 | if (!url.isLocalFile()) { | ||
740 | return contentFromIndex(mMessage.data(), url.adjusted(QUrl::StripTrailingSlash).path()); | ||
741 | } else { | ||
742 | const QString path = url.toLocalFile(); | ||
743 | // extract from /<path>/qttestn28554.index.2.3:0:2/unnamed -> "2.3:0:2" | ||
744 | // start of the index is something that is not a number followed by a dot: \D. | ||
745 | // index is only made of numbers,"." and ":": ([0-9.:]+) | ||
746 | // index is the last part of the folder name: / | ||
747 | const QRegExp rIndex(QStringLiteral("\\D\\.([e0-9.:]+)/")); | ||
748 | |||
749 | //search the occurence at most at the end | ||
750 | if (rIndex.lastIndexIn(path) != -1) { | ||
751 | return contentFromIndex(mMessage.data(), rIndex.cap(1)); | ||
752 | } | ||
753 | return mMessage.data(); | ||
754 | } | ||
755 | } | ||
756 | |||
757 | QString NodeHelper::fixEncoding(const QString &encoding) | ||
758 | { | ||
759 | QString returnEncoding = encoding; | ||
760 | // According to http://www.iana.org/assignments/character-sets, uppercase is | ||
761 | // preferred in MIME headers | ||
762 | const QString returnEncodingToUpper = returnEncoding.toUpper(); | ||
763 | if (returnEncodingToUpper.contains(QStringLiteral("ISO "))) { | ||
764 | returnEncoding = returnEncodingToUpper; | ||
765 | returnEncoding.replace(QLatin1String("ISO "), QStringLiteral("ISO-")); | ||
766 | } | ||
767 | return returnEncoding; | ||
768 | } | ||
769 | |||
770 | //----------------------------------------------------------------------------- | ||
771 | QString NodeHelper::encodingForName(const QString &descriptiveName) | ||
772 | { | ||
773 | QString encoding = KCharsets::charsets()->encodingForName(descriptiveName); | ||
774 | return NodeHelper::fixEncoding(encoding); | ||
775 | } | ||
776 | |||
777 | QStringList NodeHelper::supportedEncodings(bool usAscii) | ||
778 | { | ||
779 | QStringList encodingNames = KCharsets::charsets()->availableEncodingNames(); | ||
780 | QStringList encodings; | ||
781 | QMap<QString, bool> mimeNames; | ||
782 | QStringList::ConstIterator constEnd(encodingNames.constEnd()); | ||
783 | for (QStringList::ConstIterator it = encodingNames.constBegin(); | ||
784 | it != constEnd; ++it) { | ||
785 | QTextCodec *codec = KCharsets::charsets()->codecForName(*it); | ||
786 | QString mimeName = (codec) ? QString::fromLatin1(codec->name()).toLower() : (*it); | ||
787 | if (!mimeNames.contains(mimeName)) { | ||
788 | encodings.append(KCharsets::charsets()->descriptionForEncoding(*it)); | ||
789 | mimeNames.insert(mimeName, true); | ||
790 | } | ||
791 | } | ||
792 | encodings.sort(); | ||
793 | if (usAscii) { | ||
794 | encodings.prepend(KCharsets::charsets()->descriptionForEncoding(QStringLiteral("us-ascii"))); | ||
795 | } | ||
796 | return encodings; | ||
797 | } | ||
798 | |||
799 | QString NodeHelper::fromAsString(KMime::Content *node) const | ||
800 | { | ||
801 | if (auto topLevel = dynamic_cast<KMime::Message *>(node->topLevel())) { | ||
802 | return topLevel->from()->asUnicodeString(); | ||
803 | } else { | ||
804 | auto realNode = std::find_if(mExtraContents.cbegin(), mExtraContents.cend(), | ||
805 | [node](const QList<KMime::Content *> &nodes) { | ||
806 | return nodes.contains(node); | ||
807 | }); | ||
808 | if (realNode != mExtraContents.cend()) { | ||
809 | return fromAsString(realNode.key()); | ||
810 | } | ||
811 | } | ||
812 | |||
813 | return QString(); | ||
814 | } | ||
815 | |||
816 | void NodeHelper::attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content) | ||
817 | { | ||
818 | qCDebug(MIMETREEPARSER_LOG) << "mExtraContents added for" << topLevelNode << " extra content: " << content; | ||
819 | mExtraContents[topLevelNode].append(content); | ||
820 | } | ||
821 | |||
822 | QList< KMime::Content * > NodeHelper::extraContents(KMime::Content *topLevelnode) const | ||
823 | { | ||
824 | return mExtraContents.value(topLevelnode); | ||
825 | } | ||
826 | |||
827 | void NodeHelper::mergeExtraNodes(KMime::Content *node) | ||
828 | { | ||
829 | if (!node) { | ||
830 | return; | ||
831 | } | ||
832 | |||
833 | const QList<KMime::Content * > extraNodes = extraContents(node); | ||
834 | for (KMime::Content *extra : extraNodes) { | ||
835 | if (node->bodyIsMessage()) { | ||
836 | qCWarning(MIMETREEPARSER_LOG) << "Asked to attach extra content to a kmime::message, this does not make sense. Attaching to:" << node << | ||
837 | node->encodedContent() << "\n====== with =======\n" << extra << extra->encodedContent(); | ||
838 | continue; | ||
839 | } | ||
840 | KMime::Content *c = new KMime::Content(node); | ||
841 | c->setContent(extra->encodedContent()); | ||
842 | c->parse(); | ||
843 | node->addContent(c); | ||
844 | } | ||
845 | |||
846 | Q_FOREACH (KMime::Content *child, node->contents()) { | ||
847 | mergeExtraNodes(child); | ||
848 | } | ||
849 | } | ||
850 | |||
851 | void NodeHelper::cleanFromExtraNodes(KMime::Content *node) | ||
852 | { | ||
853 | if (!node) { | ||
854 | return; | ||
855 | } | ||
856 | const QList<KMime::Content * > extraNodes = extraContents(node); | ||
857 | for (KMime::Content *extra : extraNodes) { | ||
858 | QByteArray s = extra->encodedContent(); | ||
859 | const auto children = node->contents(); | ||
860 | for (KMime::Content *c : children) { | ||
861 | if (c->encodedContent() == s) { | ||
862 | node->removeContent(c); | ||
863 | } | ||
864 | } | ||
865 | } | ||
866 | Q_FOREACH (KMime::Content *child, node->contents()) { | ||
867 | cleanFromExtraNodes(child); | ||
868 | } | ||
869 | } | ||
870 | |||
871 | KMime::Message *NodeHelper::messageWithExtraContent(KMime::Content *topLevelNode) | ||
872 | { | ||
873 | /*The merge is done in several steps: | ||
874 | 1) merge the extra nodes into topLevelNode | ||
875 | 2) copy the modified (merged) node tree into a new node tree | ||
876 | 3) restore the original node tree in topLevelNode by removing the extra nodes from it | ||
877 | |||
878 | The reason is that extra nodes are assigned by pointer value to the nodes in the original tree. | ||
879 | */ | ||
880 | if (!topLevelNode) { | ||
881 | return nullptr; | ||
882 | } | ||
883 | |||
884 | mergeExtraNodes(topLevelNode); | ||
885 | |||
886 | KMime::Message *m = new KMime::Message; | ||
887 | m->setContent(topLevelNode->encodedContent()); | ||
888 | m->parse(); | ||
889 | |||
890 | cleanFromExtraNodes(topLevelNode); | ||
891 | // qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITH EXTRA: " << m->encodedContent(); | ||
892 | // qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITHOUT EXTRA: " << topLevelNode->encodedContent(); | ||
893 | |||
894 | return m; | ||
895 | } | ||
896 | |||
897 | KMime::Content *NodeHelper::decryptedNodeForContent(KMime::Content *content) const | ||
898 | { | ||
899 | const QList<KMime::Content *> xc = extraContents(content); | ||
900 | if (!xc.empty()) { | ||
901 | if (xc.size() == 1) { | ||
902 | return xc.front(); | ||
903 | } else { | ||
904 | qCWarning(MIMETREEPARSER_LOG) << "WTF, encrypted node has multiple extra contents?"; | ||
905 | } | ||
906 | } | ||
907 | return nullptr; | ||
908 | } | ||
909 | |||
910 | bool NodeHelper::unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders, | ||
911 | int recursionLevel) | ||
912 | { | ||
913 | bool returnValue = false; | ||
914 | if (node) { | ||
915 | KMime::Content *curNode = node; | ||
916 | KMime::Content *decryptedNode = nullptr; | ||
917 | const QByteArray type = node->contentType(false) ? QByteArray(node->contentType()->mediaType()).toLower() : "text"; | ||
918 | const QByteArray subType = node->contentType(false) ? node->contentType()->subType().toLower() : "plain"; | ||
919 | const bool isMultipart = node->contentType(false) && node->contentType()->isMultipart(); | ||
920 | bool isSignature = false; | ||
921 | |||
922 | qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") Looking at" << type << "/" << subType; | ||
923 | |||
924 | if (isMultipart) { | ||
925 | if (subType == "signed") { | ||
926 | isSignature = true; | ||
927 | } else if (subType == "encrypted") { | ||
928 | decryptedNode = decryptedNodeForContent(curNode); | ||
929 | } | ||
930 | } else if (type == "application") { | ||
931 | if (subType == "octet-stream") { | ||
932 | decryptedNode = decryptedNodeForContent(curNode); | ||
933 | } else if (subType == "pkcs7-signature") { | ||
934 | isSignature = true; | ||
935 | } else if (subType == "pkcs7-mime") { | ||
936 | // note: subtype pkcs7-mime can also be signed | ||
937 | // and we do NOT want to remove the signature! | ||
938 | if (encryptionState(curNode) != KMMsgNotEncrypted) { | ||
939 | decryptedNode = decryptedNodeForContent(curNode); | ||
940 | } | ||
941 | } | ||
942 | } | ||
943 | |||
944 | if (decryptedNode) { | ||
945 | qCDebug(MIMETREEPARSER_LOG) << "Current node has an associated decrypted node, adding a modified header " | ||
946 | "and then processing the children."; | ||
947 | |||
948 | Q_ASSERT(addHeaders); | ||
949 | KMime::Content headers; | ||
950 | headers.setHead(curNode->head()); | ||
951 | headers.parse(); | ||
952 | if (decryptedNode->contentType(false)) { | ||
953 | headers.contentType()->from7BitString(decryptedNode->contentType()->as7BitString(false)); | ||
954 | } else { | ||
955 | headers.removeHeader<KMime::Headers::ContentType>(); | ||
956 | } | ||
957 | if (decryptedNode->contentTransferEncoding(false)) { | ||
958 | headers.contentTransferEncoding()->from7BitString(decryptedNode->contentTransferEncoding()->as7BitString(false)); | ||
959 | } else { | ||
960 | headers.removeHeader<KMime::Headers::ContentTransferEncoding>(); | ||
961 | } | ||
962 | if (decryptedNode->contentDisposition(false)) { | ||
963 | headers.contentDisposition()->from7BitString(decryptedNode->contentDisposition()->as7BitString(false)); | ||
964 | } else { | ||
965 | headers.removeHeader<KMime::Headers::ContentDisposition>(); | ||
966 | } | ||
967 | if (decryptedNode->contentDescription(false)) { | ||
968 | headers.contentDescription()->from7BitString(decryptedNode->contentDescription()->as7BitString(false)); | ||
969 | } else { | ||
970 | headers.removeHeader<KMime::Headers::ContentDescription>(); | ||
971 | } | ||
972 | headers.assemble(); | ||
973 | |||
974 | resultingData += headers.head() + '\n'; | ||
975 | unencryptedMessage_helper(decryptedNode, resultingData, false, recursionLevel + 1); | ||
976 | |||
977 | returnValue = true; | ||
978 | } | ||
979 | |||
980 | else if (isSignature) { | ||
981 | qCDebug(MIMETREEPARSER_LOG) << "Current node is a signature, adding it as-is."; | ||
982 | // We can't change the nodes under the signature, as that would invalidate it. Add the signature | ||
983 | // and its child as-is | ||
984 | if (addHeaders) { | ||
985 | resultingData += curNode->head() + '\n'; | ||
986 | } | ||
987 | resultingData += curNode->encodedBody(); | ||
988 | returnValue = false; | ||
989 | } | ||
990 | |||
991 | else if (isMultipart) { | ||
992 | qCDebug(MIMETREEPARSER_LOG) << "Current node is a multipart node, adding its header and then processing all children."; | ||
993 | // Normal multipart node, add the header and all of its children | ||
994 | bool somethingChanged = false; | ||
995 | if (addHeaders) { | ||
996 | resultingData += curNode->head() + '\n'; | ||
997 | } | ||
998 | const QByteArray boundary = curNode->contentType()->boundary(); | ||
999 | foreach (KMime::Content *child, curNode->contents()) { | ||
1000 | resultingData += "\n--" + boundary + '\n'; | ||
1001 | const bool changed = unencryptedMessage_helper(child, resultingData, true, recursionLevel + 1); | ||
1002 | if (changed) { | ||
1003 | somethingChanged = true; | ||
1004 | } | ||
1005 | } | ||
1006 | resultingData += "\n--" + boundary + "--\n\n"; | ||
1007 | returnValue = somethingChanged; | ||
1008 | } | ||
1009 | |||
1010 | else if (curNode->bodyIsMessage()) { | ||
1011 | qCDebug(MIMETREEPARSER_LOG) << "Current node is a message, adding the header and then processing the child."; | ||
1012 | if (addHeaders) { | ||
1013 | resultingData += curNode->head() + '\n'; | ||
1014 | } | ||
1015 | |||
1016 | returnValue = unencryptedMessage_helper(curNode->bodyAsMessage().data(), resultingData, true, recursionLevel + 1); | ||
1017 | } | ||
1018 | |||
1019 | else { | ||
1020 | qCDebug(MIMETREEPARSER_LOG) << "Current node is an ordinary leaf node, adding it as-is."; | ||
1021 | if (addHeaders) { | ||
1022 | resultingData += curNode->head() + '\n'; | ||
1023 | } | ||
1024 | resultingData += curNode->body(); | ||
1025 | returnValue = false; | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") done."; | ||
1030 | return returnValue; | ||
1031 | } | ||
1032 | |||
1033 | KMime::Message::Ptr NodeHelper::unencryptedMessage(const KMime::Message::Ptr &originalMessage) | ||
1034 | { | ||
1035 | QByteArray resultingData; | ||
1036 | const bool messageChanged = unencryptedMessage_helper(originalMessage.data(), resultingData, true); | ||
1037 | if (messageChanged) { | ||
1038 | #if 0 | ||
1039 | qCDebug(MIMETREEPARSER_LOG) << "Resulting data is:" << resultingData; | ||
1040 | QFile bla("stripped.mbox"); | ||
1041 | bla.open(QIODevice::WriteOnly); | ||
1042 | bla.write(resultingData); | ||
1043 | bla.close(); | ||
1044 | #endif | ||
1045 | KMime::Message::Ptr newMessage(new KMime::Message); | ||
1046 | newMessage->setContent(resultingData); | ||
1047 | newMessage->parse(); | ||
1048 | return newMessage; | ||
1049 | } else { | ||
1050 | return KMime::Message::Ptr(); | ||
1051 | } | ||
1052 | } | ||
1053 | |||
1054 | QVector<KMime::Content *> NodeHelper::attachmentsOfExtraContents() const | ||
1055 | { | ||
1056 | QVector<KMime::Content *> result; | ||
1057 | for (auto it = mExtraContents.begin(); it != mExtraContents.end(); ++it) { | ||
1058 | foreach (auto content, it.value()) { | ||
1059 | if (KMime::isAttachment(content)) { | ||
1060 | result.push_back(content); | ||
1061 | } else { | ||
1062 | result += content->attachments(); | ||
1063 | } | ||
1064 | } | ||
1065 | } | ||
1066 | return result; | ||
1067 | } | ||
1068 | |||
1069 | } | ||