2 objecttreeparser.h
4 This file is part of KMail, the KDE mail client.
5 SPDX-FileCopyrightText: 2003 Marc Mutz <>
6 SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company,
7 SPDX-FileCopyrightText: 2009 Andras Mantia <>
9 SPDX-License-Identifier: GPL-2.0-or-later
12#pragma once
14#include "mimetreeparser_export.h"
16#include "mimetreeparser/nodehelper.h"
17#include "mimetreeparser/objecttreesource.h"
19#include <gpgme++/verificationresult.h>
21class QString;
23namespace KMime
25class Content;
28namespace MimeTreeParser
30class PartMetaData;
31class ViewerPrivate;
32class NodeHelper;
33class MessagePart;
34class MimeMessagePart;
36using MessagePartPtr = QSharedPointer<MessagePart>;
37using MimeMessagePartPtr = QSharedPointer<MimeMessagePart>;
39 * @brief The ProcessResult class
40 */
44 explicit ProcessResult(NodeHelper *nodeHelper,
45 KMMsgSignatureState inlineSignatureState = KMMsgNotSigned,
46 KMMsgEncryptionState inlineEncryptionState = KMMsgNotEncrypted,
47 bool neverDisplayInline = false)
48 : mInlineSignatureState(inlineSignatureState)
49 , mInlineEncryptionState(inlineEncryptionState)
50 , mNeverDisplayInline(neverDisplayInline)
51 , mNodeHelper(nodeHelper)
52 {
53 }
55 [[nodiscard]] KMMsgSignatureState inlineSignatureState() const;
56 void setInlineSignatureState(KMMsgSignatureState state);
58 [[nodiscard]] KMMsgEncryptionState inlineEncryptionState() const;
59 void setInlineEncryptionState(KMMsgEncryptionState state);
61 [[nodiscard]] bool neverDisplayInline() const;
62 void setNeverDisplayInline(bool display);
64 void adjustCryptoStatesOfNode(const KMime::Content *node) const;
67 KMMsgSignatureState mInlineSignatureState;
68 KMMsgEncryptionState mInlineEncryptionState;
69 bool mNeverDisplayInline : 1;
70 NodeHelper *mNodeHelper;
74\brief Parses messages and generates HTML display code out of them
76\par Introduction
78First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
79to understand the broader picture.
81Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
84\par Basics
86The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only
87extracting the plainTextContent() for situations where only the message text is needed, for example
88when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the
89constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is
92Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on
93those nodes. Operating here means creating the HTML code for the node or extracting the textual
94content from it. This process is started with parseObjectTree(), where we loop over the subnodes
95of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can
96handle the type of the node. This can either be an internal function, such as
97processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin.
98More on external plugins later. When no matching formatter is found, defaultHandling() is called
99for that node.
101\par Multipart Nodes
103Those nodes that are of type multipart have subnodes. If one of those children needs to be
104processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that
105should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone
106of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all
107children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on
108one of the children, as the other one doesn't need to be displayed. Similarly,
109processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the
110signed node.
112\par Processed and Unprocessed Nodes
114When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being
115not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a
116list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other
117related helper functions.
119It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the
120related functions. This is important so that processing the same node twice can be prevented. The
121check that prevents duplicate processing is in parseObjectTree().
123An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(),
124which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to
125prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call
126stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call
127parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the
128siblings is the plain text node, which shouldn't be processed! Therefore
129processMultiPartAlternativeSubtype() sets the plain text node as been processed already.
131\par Plain Text Output
133Various nodes have plain text that should be displayed. This plain text is usually processed though
134writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts
135it if necessary. It also pushes the text through quotedHTML(), which does a number of things like
136coloring quoted lines or detecting links and creating real link tags for them.
138\par Modifying the Message
140The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it
141before displaying. This is for example the case when displaying a decrypted message: The original
142message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts
143that blob. After decryption, the current node is replaced with the decrypted node, which happens
144in insertAndParseNewChildNode().
146\par Crypto Operations
148For signature and decryption handling, there are functions which help with generating the HTML code
149for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter()
150and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause
151the message to not be decrypted unless the user clicks a link. Whether the message should be
152decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the
153decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update
154of the Viewer, which will cause parseObjectTree() to be called again.
156\par Async Crypto Operations
158The above case describes decryption the message in place. However, decryption and also verifying of
159the signature can take a long time, so synchronous decryption and verifying would cause the Viewer to
160block. Therefore it is possible to run these operations in async mode, see allowAsync().
161In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the
162verify job, and informing the user that the operation is in progress with
163writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a
164BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can
165have multiple mementos associated with it, which are differeniated by name.
167NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and
168retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the
169job pointer, the job input data and the job result. Mementos can be used for any async situation,
170not just for crypto jobs, but I'll describe crypto jobs here.
172So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the
173crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the
174operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the
175message is displayed.
177At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento
178to be called. slotResult() then saves the result to some member variable and calls
179BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update
180will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it.
181This is where the second run begins.
183The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or
184processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with
185the current node. Now it finds that memento, since it was created in the first run. It checks if the
186memento's job has finished, and if so, the result can be written out (either the decrypted data or
187the verified signature).
189When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to
190note that the original MIME tree is never modified, and remains the same as the original one. The method
191createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to
192store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise
193some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the
194regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function
195NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent.
196Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent
197node to the newly created temporary node.
199When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the
200function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for
201the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence
202of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the
203given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node.
205To conclude: For async operations, parseObjectTree() is called twice: The first call starts the
206crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is
207there and can use its result for writing out the HTML.
209\par PartMetaData and ProcessResult
211For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the
212crypto state of a node. A PartMetaData can also be associated with a node by using
213NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of
214the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message.
216You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify
217the ProcessResult to tell the callers something about the state of the nodes that were processed.
218One example for its use is to tell the caller about the crypto state of the node.
220\par BodyPartFormatter Plugins
222As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp
223contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from
224a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro.
226The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not
227yet happened at the time of writing. Therefore the code can seem a bit chaotic.
229External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins
230can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento,
231BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler and URLHandler. Therefore
232external plugins have powerful capabilities, which are needed for example in the iCal formatter or
233in the vCard formatter.
235\par Special HTML tags
237As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special
238links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs.
239Also, some special HTML tags are created, which the Viewer later uses for post-processing. For
240example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which
241is used to mark an attachment in the body with a yellow border when the user clicks the attachment
242in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in
243the Viewer to scroll to the attachment.
245class MIMETREEPARSER_EXPORT ObjectTreeParser
247 /**
248 * @internal
249 * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent.
250 */
254 explicit ObjectTreeParser(Interface::ObjectTreeSource *source, NodeHelper *nodeHelper = nullptr);
256 explicit ObjectTreeParser(const ObjectTreeParser *topLevelParser);
257 virtual ~ObjectTreeParser();
259 void setAllowAsync(bool allow);
260 [[nodiscard]] bool allowAsync() const;
262 [[nodiscard]] bool hasPendingAsyncJobs() const;
264 /**
265 * The text of the message, ie. what would appear in the
266 * composer's text editor if this was edited or replied to.
267 * This is usually the content of the first text/plain MIME part.
268 */
269 [[nodiscard]] QString plainTextContent() const;
271 /**
272 * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
273 *
274 * Not to be confused with the HTML code that the message viewer widget displays, that HTML
275 * is written out by htmlWriter() and a totally different pair of shoes.
276 */
277 [[nodiscard]] QString htmlContent() const;
279 [[nodiscard]] NodeHelper *nodeHelper() const;
281 /** Parse beginning at a given node and recursively parsing
282 the children of that node and it's next sibling. */
283 void parseObjectTree(KMime::Content *node, bool parseOnlySingleNode = false);
284 [[nodiscard]] MessagePartPtr parsedPart() const;
287 MIMETREEPARSER_NO_EXPORT void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart);
288 MIMETREEPARSER_NO_EXPORT void setPlainTextContent(const QString &plainTextContent);
290 /**
291 * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
292 * top-level content.
293 */
294 MIMETREEPARSER_NO_EXPORT MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
295 MIMETREEPARSER_NO_EXPORT MessagePartPtr processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mimeType);
298 /** ctor helper */
301 MIMETREEPARSER_NO_EXPORT QByteArray codecNameFor(KMime::Content *node) const;
304 Interface::ObjectTreeSource *mSource = nullptr;
305 NodeHelper *mNodeHelper = nullptr;
306 QString mPlainTextContent;
307 QString mHtmlContent;
308 KMime::Content *mTopLevelContent = nullptr;
309 MessagePartPtr mParsedPart;
311 bool mHasPendingAsyncJobs = false;
312 bool mAllowAsync = false;
313 // DataUrl Icons cache
314 QString mCollapseIcon;
315 QString mExpandIcon;
316 bool mDeleteNodeHelper;
318 friend class PartNodeBodyPart;
319 friend class MessagePart;
320 friend class EncryptedMessagePart;
321 friend class SignedMessagePart;
322 friend class TextMessagePart;
323 friend class HtmlMessagePart;
324 friend class MultiPartSignedBodyPartFormatter;
325 friend class ApplicationPkcs7MimeBodyPartFormatter;
Interface for object tree sources.
Parses messages and generates HTML display code out of them.
an implementation of the BodyPart interface using KMime::Content's
The ProcessResult class.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 11:54:55 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.