File indexing completed on 2024-06-16 05:00:15
0001 /* 0002 objecttreeparser.cpp 0003 0004 This file is part of KMail, the KDE mail client. 0005 SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org> 0006 SPDX-FileCopyrightText: 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net 0007 SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net> 0008 SPDX-FileCopyrightText: 2015 Sandro Knauß <sknauss@kde.org> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 // MessageViewer includes 0014 0015 #include "objecttreeparser.h" 0016 0017 #include "bodypartformatterfactory.h" 0018 #include "messagepart.h" 0019 #include "nodehelper.h" 0020 #include "partnodebodypart.h" 0021 0022 #include "bodyformatter/utils.h" 0023 #include "interfaces/bodypartformatter.h" 0024 #include "utils/util.h" 0025 0026 #include <KMime/Headers> 0027 #include <KMime/Message> 0028 0029 // Qt includes 0030 #include <QByteArray> 0031 0032 using namespace MimeTreeParser; 0033 0034 ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser) 0035 : mSource(topLevelParser->mSource) 0036 , mNodeHelper(topLevelParser->mNodeHelper) 0037 , mTopLevelContent(topLevelParser->mTopLevelContent) 0038 , mAllowAsync(topLevelParser->mAllowAsync) 0039 { 0040 init(); 0041 } 0042 0043 ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source, MimeTreeParser::NodeHelper *nodeHelper) 0044 : mSource(source) 0045 , mNodeHelper(nodeHelper) 0046 , mTopLevelContent(nullptr) 0047 , mAllowAsync(false) 0048 { 0049 init(); 0050 } 0051 0052 void ObjectTreeParser::init() 0053 { 0054 Q_ASSERT(mSource); 0055 0056 if (!mNodeHelper) { 0057 mNodeHelper = new NodeHelper(); 0058 mDeleteNodeHelper = true; 0059 } else { 0060 mDeleteNodeHelper = false; 0061 } 0062 } 0063 0064 ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other) 0065 : mSource(other.mSource) 0066 , mNodeHelper(other.nodeHelper()) 0067 , mTopLevelContent(other.mTopLevelContent) 0068 , mHasPendingAsyncJobs(other.hasPendingAsyncJobs()) 0069 , mAllowAsync(other.allowAsync()) 0070 , mDeleteNodeHelper(false) 0071 { 0072 } 0073 0074 ObjectTreeParser::~ObjectTreeParser() 0075 { 0076 if (mDeleteNodeHelper) { 0077 delete mNodeHelper; 0078 mNodeHelper = nullptr; 0079 } 0080 } 0081 0082 void ObjectTreeParser::setAllowAsync(bool allow) 0083 { 0084 Q_ASSERT(!mHasPendingAsyncJobs); 0085 mAllowAsync = allow; 0086 } 0087 0088 bool ObjectTreeParser::allowAsync() const 0089 { 0090 return mAllowAsync; 0091 } 0092 0093 bool ObjectTreeParser::hasPendingAsyncJobs() const 0094 { 0095 return mHasPendingAsyncJobs; 0096 } 0097 0098 QString ObjectTreeParser::plainTextContent() const 0099 { 0100 return mPlainTextContent; 0101 } 0102 0103 QString ObjectTreeParser::htmlContent() const 0104 { 0105 return mHtmlContent; 0106 } 0107 0108 //----------------------------------------------------------------------------- 0109 0110 void ObjectTreeParser::parseObjectTree(KMime::Content *node, bool parseOnlySingleNode) 0111 { 0112 mTopLevelContent = node; 0113 mParsedPart = parseObjectTreeInternal(node, parseOnlySingleNode); 0114 0115 if (mParsedPart) { 0116 mParsedPart->fix(); 0117 if (auto mp = toplevelTextNode(mParsedPart)) { 0118 if (auto _mp = mp.dynamicCast<TextMessagePart>()) { 0119 extractNodeInfos(_mp->content(), true); 0120 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) { 0121 if (_mp->childParts().contains(Util::MultipartPlain)) { 0122 extractNodeInfos(_mp->childParts()[Util::MultipartPlain]->content(), true); 0123 } 0124 } 0125 setPlainTextContent(mp->text()); 0126 } 0127 } 0128 } 0129 0130 MessagePartPtr ObjectTreeParser::parsedPart() const 0131 { 0132 return mParsedPart; 0133 } 0134 0135 MessagePartPtr ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mimeType) 0136 { 0137 const auto formatters = mSource->bodyPartFormatterFactory()->formattersForType(QString::fromUtf8(mimeType)); 0138 Q_ASSERT(!formatters.empty()); 0139 for (auto formatter : formatters) { 0140 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper); 0141 mNodeHelper->setNodeDisplayedEmbedded(node, true); 0142 0143 const MessagePart::Ptr result = formatter->process(part); 0144 if (result.isNull()) { 0145 continue; 0146 } 0147 0148 result->setAttachmentContent(node); 0149 return result; 0150 } 0151 0152 Q_UNREACHABLE(); 0153 return {}; 0154 } 0155 0156 MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart) 0157 { 0158 if (!node) { 0159 return {}; 0160 } 0161 0162 // reset pending async jobs state (we'll rediscover pending jobs as we go) 0163 mHasPendingAsyncJobs = false; 0164 0165 mNodeHelper->clearOverrideHeaders(); 0166 0167 // reset "processed" flags for... 0168 if (onlyOneMimePart) { 0169 // ... this node and all descendants 0170 mNodeHelper->setNodeUnprocessed(node, false); 0171 if (!node->contents().isEmpty()) { 0172 mNodeHelper->setNodeUnprocessed(node, true); 0173 } 0174 } else if (!node->parent()) { 0175 // ...this node and all it's siblings and descendants 0176 mNodeHelper->setNodeUnprocessed(node, true); 0177 } 0178 0179 const bool isRoot = node->isTopLevel(); 0180 auto parsedPart = MessagePart::Ptr(new MessagePartList(this)); 0181 parsedPart->setIsRoot(isRoot); 0182 KMime::Content *parent = node->parent(); 0183 auto contents = parent ? parent->contents() : KMime::Content::List(); 0184 if (contents.isEmpty()) { 0185 contents.append(node); 0186 } 0187 int i = contents.indexOf(node); 0188 if (i < 0) { 0189 return parsedPart; 0190 } else { 0191 for (; i < contents.size(); ++i) { 0192 node = contents.at(i); 0193 if (mNodeHelper->nodeProcessed(node)) { 0194 continue; 0195 } 0196 0197 ProcessResult processResult(mNodeHelper); 0198 0199 QByteArray mimeType("text/plain"); 0200 if (auto ct = node->contentType(false)) { 0201 mimeType = ct->mimeType(); 0202 } 0203 // unfortunately there's many emails where we can't trust the attachment mimetype 0204 // so try to see if we can find something better 0205 if (mimeType == "application/octet-stream") { 0206 NodeHelper::magicSetType(node); 0207 mimeType = node->contentType(false)->mimeType(); 0208 } 0209 0210 const auto mp = processType(node, processResult, mimeType); 0211 Q_ASSERT(mp); 0212 parsedPart->appendSubPart(mp); 0213 mNodeHelper->setNodeProcessed(node, false); 0214 0215 // adjust signed/encrypted flags if inline PGP was found 0216 processResult.adjustCryptoStatesOfNode(node); 0217 0218 if (onlyOneMimePart) { 0219 break; 0220 } 0221 } 0222 } 0223 return parsedPart; 0224 } 0225 0226 KMMsgSignatureState ProcessResult::inlineSignatureState() const 0227 { 0228 return mInlineSignatureState; 0229 } 0230 0231 void ProcessResult::setInlineSignatureState(KMMsgSignatureState state) 0232 { 0233 mInlineSignatureState = state; 0234 } 0235 0236 KMMsgEncryptionState ProcessResult::inlineEncryptionState() const 0237 { 0238 return mInlineEncryptionState; 0239 } 0240 0241 void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state) 0242 { 0243 mInlineEncryptionState = state; 0244 } 0245 0246 bool ProcessResult::neverDisplayInline() const 0247 { 0248 return mNeverDisplayInline; 0249 } 0250 0251 void ProcessResult::setNeverDisplayInline(bool display) 0252 { 0253 mNeverDisplayInline = display; 0254 } 0255 0256 void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const 0257 { 0258 if ((inlineSignatureState() != KMMsgNotSigned) || (inlineEncryptionState() != KMMsgNotEncrypted)) { 0259 mNodeHelper->setSignatureState(node, inlineSignatureState()); 0260 mNodeHelper->setEncryptionState(node, inlineEncryptionState()); 0261 } 0262 } 0263 0264 void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart) 0265 { 0266 if (isFirstTextPart) { 0267 mPlainTextContent += curNode->decodedText(); 0268 mPlainTextContentCharset += NodeHelper::charset(curNode); 0269 } 0270 } 0271 0272 void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent) 0273 { 0274 mPlainTextContent = plainTextContent; 0275 } 0276 0277 QByteArray ObjectTreeParser::codecNameFor(KMime::Content *node) const 0278 { 0279 Q_ASSERT(node); 0280 if (!mSource->overrideCodecName().isEmpty()) { 0281 return mSource->overrideCodecName(); 0282 } 0283 return mNodeHelper->codecName(node); 0284 } 0285 0286 QByteArray ObjectTreeParser::plainTextContentCharset() const 0287 { 0288 return mPlainTextContentCharset; 0289 } 0290 0291 QByteArray ObjectTreeParser::htmlContentCharset() const 0292 { 0293 return mHtmlContentCharset; 0294 } 0295 0296 MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const 0297 { 0298 return mNodeHelper; 0299 }