Messagelib

objecttreeparser.h
1/*
2 objecttreeparser.h
3
4 This file is part of KMail, the KDE mail client.
5 SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
6 SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12#pragma once
13
14#include "mimetreeparser_export.h"
15
16#include "mimetreeparser/nodehelper.h"
17#include "mimetreeparser/objecttreesource.h"
18
19#include <gpgme++/verificationresult.h>
20
21class QString;
22
23namespace KMime
24{
25class Content;
26}
27
28namespace MimeTreeParser
29{
30class PartMetaData;
31class ViewerPrivate;
32class NodeHelper;
33class MessagePart;
34class MimeMessagePart;
35
36using MessagePartPtr = QSharedPointer<MessagePart>;
37using MimeMessagePartPtr = QSharedPointer<MimeMessagePart>;
38/**
39 * @brief The ProcessResult class
40 */
41class MIMETREEPARSER_EXPORT ProcessResult
42{
43public:
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 }
54
55 [[nodiscard]] KMMsgSignatureState inlineSignatureState() const;
56 void setInlineSignatureState(KMMsgSignatureState state);
57
58 [[nodiscard]] KMMsgEncryptionState inlineEncryptionState() const;
59 void setInlineEncryptionState(KMMsgEncryptionState state);
60
61 [[nodiscard]] bool neverDisplayInline() const;
62 void setNeverDisplayInline(bool display);
63
64 void adjustCryptoStatesOfNode(const KMime::Content *node) const;
65
66private:
67 KMMsgSignatureState mInlineSignatureState;
68 KMMsgEncryptionState mInlineEncryptionState;
69 bool mNeverDisplayInline : 1;
70 NodeHelper *mNodeHelper;
71};
72
73/**
74\brief Parses messages and generates HTML display code out of them
75
76\par Introduction
77
78First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
79to understand the broader picture.
80
81Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
82KMime::Content.
83
84\par Basics
85
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
90used.
91
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.
100
101\par Multipart Nodes
102
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.
111
112\par Processed and Unprocessed Nodes
113
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.
118
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().
122
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.
130
131\par Plain Text Output
132
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.
137
138\par Modifying the Message
139
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().
145
146\par Crypto Operations
147
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.
155
156\par Async Crypto Operations
157
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.
166
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.
171
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.
176
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.
182
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).
188
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.
198
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.
204
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.
208
209\par PartMetaData and ProcessResult
210
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.
215
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.
219
220\par BodyPartFormatter Plugins
221
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.
225
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.
228
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.
234
235\par Special HTML tags
236
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.
244*/
245class MIMETREEPARSER_EXPORT ObjectTreeParser
246{
247 /**
248 * @internal
249 * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent.
250 */
252
253public:
254 explicit ObjectTreeParser(Interface::ObjectTreeSource *source, NodeHelper *nodeHelper = nullptr);
255
256 explicit ObjectTreeParser(const ObjectTreeParser *topLevelParser);
257 virtual ~ObjectTreeParser();
258
259 void setAllowAsync(bool allow);
260 [[nodiscard]] bool allowAsync() const;
261
262 [[nodiscard]] bool hasPendingAsyncJobs() const;
263
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;
270
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;
278
279 /**
280 * The original charset of MIME part the plain text was extracted from.
281 *
282 * If there were more than one text/plain MIME parts in the mail, the this is the charset
283 * of the last MIME part processed.
284 */
285 [[nodiscard]] QByteArray plainTextContentCharset() const;
286 [[nodiscard]] QByteArray htmlContentCharset() const;
287
288 [[nodiscard]] NodeHelper *nodeHelper() const;
289
290 /** Parse beginning at a given node and recursively parsing
291 the children of that node and it's next sibling. */
292 void parseObjectTree(KMime::Content *node, bool parseOnlySingleNode = false);
293 [[nodiscard]] MessagePartPtr parsedPart() const;
294
295private:
296 MIMETREEPARSER_NO_EXPORT void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart);
297 MIMETREEPARSER_NO_EXPORT void setPlainTextContent(const QString &plainTextContent);
298
299 /**
300 * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
301 * top-level content.
302 */
303 MIMETREEPARSER_NO_EXPORT MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
304 MIMETREEPARSER_NO_EXPORT MessagePartPtr processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mimeType);
305
306private:
307 /** ctor helper */
308 MIMETREEPARSER_NO_EXPORT void init();
309
310 MIMETREEPARSER_NO_EXPORT QByteArray codecNameFor(KMime::Content *node) const;
311
312private:
313 Interface::ObjectTreeSource *mSource = nullptr;
314 NodeHelper *mNodeHelper = nullptr;
315 QByteArray mPlainTextContentCharset;
316 QByteArray mHtmlContentCharset;
317 QString mPlainTextContent;
318 QString mHtmlContent;
319 KMime::Content *mTopLevelContent = nullptr;
320 MessagePartPtr mParsedPart;
321
322 bool mHasPendingAsyncJobs = false;
323 bool mAllowAsync = false;
324 // DataUrl Icons cache
325 QString mCollapseIcon;
326 QString mExpandIcon;
327 bool mDeleteNodeHelper;
328
329 friend class PartNodeBodyPart;
330 friend class MessagePart;
331 friend class EncryptedMessagePart;
332 friend class SignedMessagePart;
333 friend class TextMessagePart;
334 friend class HtmlMessagePart;
335 friend class MultiPartSignedBodyPartFormatter;
336 friend class ApplicationPkcs7MimeBodyPartFormatter;
337};
338}
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 Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.