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 <[email protected]>
6  SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
7  SPDX-FileCopyrightText: 2009 Andras Mantia <[email protected]>
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 
21 class QString;
22 
23 namespace KMime
24 {
25 class Content;
26 }
27 
28 namespace MimeTreeParser
29 {
30 class PartMetaData;
31 class ViewerPrivate;
32 class NodeHelper;
33 class MessagePart;
34 class MimeMessagePart;
35 
36 using MessagePartPtr = QSharedPointer<MessagePart>;
37 using MimeMessagePartPtr = QSharedPointer<MimeMessagePart>;
38 /**
39  * @brief The ProcessResult class
40  */
41 class MIMETREEPARSER_EXPORT ProcessResult
42 {
43 public:
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 
66 private:
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 
78 First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
79 to understand the broader picture.
80 
81 Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
82 KMime::Content.
83 
84 \par Basics
85 
86 The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only
87 extracting the plainTextContent() for situations where only the message text is needed, for example
88 when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the
89 constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is
90 used.
91 
92 Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on
93 those nodes. Operating here means creating the HTML code for the node or extracting the textual
94 content from it. This process is started with parseObjectTree(), where we loop over the subnodes
95 of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can
96 handle the type of the node. This can either be an internal function, such as
97 processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin.
98 More on external plugins later. When no matching formatter is found, defaultHandling() is called
99 for that node.
100 
101 \par Multipart Nodes
102 
103 Those nodes that are of type multipart have subnodes. If one of those children needs to be
104 processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that
105 should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone
106 of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all
107 children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on
108 one of the children, as the other one doesn't need to be displayed. Similarly,
109 processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the
110 signed node.
111 
112 \par Processed and Unprocessed Nodes
113 
114 When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being
115 not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a
116 list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other
117 related helper functions.
118 
119 It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the
120 related functions. This is important so that processing the same node twice can be prevented. The
121 check that prevents duplicate processing is in parseObjectTree().
122 
123 An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(),
124 which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to
125 prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call
126 stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call
127 parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the
128 siblings is the plain text node, which shouldn't be processed! Therefore
129 processMultiPartAlternativeSubtype() sets the plain text node as been processed already.
130 
131 \par Plain Text Output
132 
133 Various nodes have plain text that should be displayed. This plain text is usually processed though
134 writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts
135 it if necessary. It also pushes the text through quotedHTML(), which does a number of things like
136 coloring quoted lines or detecting links and creating real link tags for them.
137 
138 \par Modifying the Message
139 
140 The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it
141 before displaying. This is for example the case when displaying a decrypted message: The original
142 message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts
143 that blob. After decryption, the current node is replaced with the decrypted node, which happens
144 in insertAndParseNewChildNode().
145 
146 \par Crypto Operations
147 
148 For signature and decryption handling, there are functions which help with generating the HTML code
149 for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter()
150 and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause
151 the message to not be decrypted unless the user clicks a link. Whether the message should be
152 decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the
153 decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update
154 of the Viewer, which will cause parseObjectTree() to be called again.
155 
156 \par Async Crypto Operations
157 
158 The above case describes decryption the message in place. However, decryption and also verifying of
159 the signature can take a long time, so synchronous decryption and verifying would cause the Viewer to
160 block. Therefore it is possible to run these operations in async mode, see allowAsync().
161 In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the
162 verify job, and informing the user that the operation is in progress with
163 writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a
164 BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can
165 have multiple mementos associated with it, which are differeniated by name.
166 
167 NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and
168 retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the
169 job pointer, the job input data and the job result. Mementos can be used for any async situation,
170 not just for crypto jobs, but I'll describe crypto jobs here.
171 
172 So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the
173 crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the
174 operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the
175 message is displayed.
176 
177 At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento
178 to be called. slotResult() then saves the result to some member variable and calls
179 BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update
180 will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it.
181 This is where the second run begins.
182 
183 The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or
184 processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with
185 the current node. Now it finds that memento, since it was created in the first run. It checks if the
186 memento's job has finished, and if so, the result can be written out (either the decrypted data or
187 the verified signature).
188 
189 When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to
190 note that the original MIME tree is never modified, and remains the same as the original one. The method
191 createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to
192 store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise
193 some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the
194 regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function
195 NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent.
196 Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent
197 node to the newly created temporary node.
198 
199 When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the
200 function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for
201 the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence
202 of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the
203 given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node.
204 
205 To conclude: For async operations, parseObjectTree() is called twice: The first call starts the
206 crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is
207 there and can use its result for writing out the HTML.
208 
209 \par PartMetaData and ProcessResult
210 
211 For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the
212 crypto state of a node. A PartMetaData can also be associated with a node by using
213 NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of
214 the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message.
215 
216 You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify
217 the ProcessResult to tell the callers something about the state of the nodes that were processed.
218 One example for its use is to tell the caller about the crypto state of the node.
219 
220 \par BodyPartFormatter Plugins
221 
222 As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp
223 contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from
224 a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro.
225 
226 The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not
227 yet happened at the time of writing. Therefore the code can seem a bit chaotic.
228 
229 External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins
230 can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento,
231 BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler and URLHandler. Therefore
232 external plugins have powerful capabilities, which are needed for example in the iCal formatter or
233 in the vCard formatter.
234 
235 \par Special HTML tags
236 
237 As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special
238 links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs.
239 Also, some special HTML tags are created, which the Viewer later uses for post-processing. For
240 example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which
241 is used to mark an attachment in the body with a yellow border when the user clicks the attachment
242 in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in
243 the Viewer to scroll to the attachment.
244 */
245 class MIMETREEPARSER_EXPORT ObjectTreeParser
246 {
247  /**
248  * @internal
249  * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent.
250  */
251  ObjectTreeParser(const ObjectTreeParser &other);
252 
253 public:
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  bool allowAsync() const;
261 
262  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  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  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  QByteArray plainTextContentCharset() const;
286  QByteArray htmlContentCharset() const;
287 
288  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  MessagePartPtr parsedPart() const;
294 
295 private:
296  void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart);
297  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  MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
304  MessagePartPtr processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mimeType);
305 
306 private:
307  /** ctor helper */
308  void init();
309 
310  QByteArray codecNameFor(KMime::Content *node) const;
311 
312 private:
314  NodeHelper *mNodeHelper;
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 }
The ProcessResult class.
Parses messages and generates HTML display code out of them.
Interface for object tree sources.
an implementation of the BodyPart interface using KMime::Content's
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Dec 1 2023 03:57:06 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.