summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mimetreeparser/otp/objecttreeparser.h
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mimetreeparser/otp/objecttreeparser.h')
-rw-r--r--framework/src/domain/mimetreeparser/otp/objecttreeparser.h406
1 files changed, 406 insertions, 0 deletions
diff --git a/framework/src/domain/mimetreeparser/otp/objecttreeparser.h b/framework/src/domain/mimetreeparser/otp/objecttreeparser.h
new file mode 100644
index 00000000..3f29a673
--- /dev/null
+++ b/framework/src/domain/mimetreeparser/otp/objecttreeparser.h
@@ -0,0 +1,406 @@
1/*
2 objecttreeparser.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8
9 KMail is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License, version 2, as
11 published by the Free Software Foundation.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_OBJECTTREEPARSER_H__
35#define __MIMETREEPARSER_OBJECTTREEPARSER_H__
36
37#include "nodehelper.h"
38#include "objecttreesource.h"
39
40#include <gpgme++/verificationresult.h>
41
42class QString;
43
44namespace KMime
45{
46class Content;
47}
48
49namespace MimeTreeParser
50{
51
52namespace Interface
53{
54class MessagePart;
55typedef QSharedPointer<MessagePart> MessagePartPtr;
56}
57
58class PartMetaData;
59class ViewerPrivate;
60class HtmlWriter;
61class AttachmentStrategy;
62class NodeHelper;
63class MessagePart;
64class MimeMessagePart;
65
66typedef QSharedPointer<MessagePart> MessagePartPtr;
67typedef QSharedPointer<MimeMessagePart> MimeMessagePartPtr;
68
69class ProcessResult
70{
71public:
72 explicit ProcessResult(NodeHelper *nodeHelper, KMMsgSignatureState inlineSignatureState = KMMsgNotSigned,
73 KMMsgEncryptionState inlineEncryptionState = KMMsgNotEncrypted,
74 bool neverDisplayInline = false,
75 bool isImage = false)
76 : mInlineSignatureState(inlineSignatureState),
77 mInlineEncryptionState(inlineEncryptionState),
78 mNeverDisplayInline(neverDisplayInline),
79 mIsImage(isImage),
80 mNodeHelper(nodeHelper) {}
81
82 KMMsgSignatureState inlineSignatureState() const;
83 void setInlineSignatureState(KMMsgSignatureState state);
84
85 KMMsgEncryptionState inlineEncryptionState() const;
86 void setInlineEncryptionState(KMMsgEncryptionState state);
87
88 bool neverDisplayInline() const;
89 void setNeverDisplayInline(bool display);
90
91 bool isImage() const;
92 void setIsImage(bool image);
93
94 void adjustCryptoStatesOfNode(const KMime::Content *node) const;
95
96private:
97 KMMsgSignatureState mInlineSignatureState;
98 KMMsgEncryptionState mInlineEncryptionState;
99 bool mNeverDisplayInline : 1;
100 bool mIsImage : 1;
101 NodeHelper *mNodeHelper;
102};
103
104/**
105\brief Parses messages and generates HTML display code out of them
106
107\par Introduction
108
109First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
110to understand the broader picture.
111
112Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
113KMime::Content.
114
115\par Basics
116
117The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only
118extracting the plainTextContent() for situations where only the message text is needed, for example
119when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the
120constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is
121used.
122
123Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on
124those nodes. Operating here means creating the HTML code for the node or extracting the textual
125content from it. This process is started with parseObjectTree(), where we loop over the subnodes
126of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can
127handle the type of the node. This can either be an internal function, such as
128processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin.
129More on external plugins later. When no matching formatter is found, defaultHandling() is called
130for that node.
131
132\par Multipart Nodes
133
134Those nodes that are of type multipart have subnodes. If one of those children needs to be
135processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that
136should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone
137of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all
138children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on
139one of the children, as the other one doesn't need to be displayed. Similary,
140processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the
141signed node.
142
143\par Processed and Unprocessed Nodes
144
145When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being
146not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a
147list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other
148related helper functions.
149
150It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the
151related functions. This is important so that processing the same node twice can be prevented. The
152check that prevents duplicate processing is in parseObjectTree().
153
154An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(),
155which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to
156prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call
157stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call
158parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the
159siblings is the plain text node, which shouldn't be processed! Therefore
160processMultiPartAlternativeSubtype() sets the plain text node as been processed already.
161
162\par Plain Text Output
163
164Various nodes have plain text that should be displayed. This plain text is usually processed though
165writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts
166it if necessary. It also pushes the text through quotedHTML(), which does a number of things like
167coloring quoted lines or detecting links and creating real link tags for them.
168
169\par Modifying the Message
170
171The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it
172before displaying. This is for example the case when displaying a decrypted message: The original
173message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts
174that blob. After decryption, the current node is replaced with the decrypted node, which happens
175in insertAndParseNewChildNode().
176
177\par Crypto Operations
178
179For signature and decryption handling, there are functions which help with generating the HTML code
180for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter()
181and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause
182the message to not be decrypted unless the user clicks a link. Whether the message should be
183decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the
184decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update
185of the Viewer, which will cause parseObjectTree() to be called again.
186
187\par Async Crypto Operations
188
189The above case describes decryption the message in place. However, decryption and also verifying of
190the signature can take a long time, so synchronous decryption and verifing would cause the Viewer to
191block. Therefore it is possible to run these operations in async mode, see allowAsync().
192In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the
193verify job, and informing the user that the operation is in progress with
194writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a
195BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can
196have multiple mementos associated with it, which are differeniated by name.
197
198NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and
199retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the
200job pointer, the job input data and the job result. Mementos can be used for any async situation,
201not just for crypto jobs, but I'll describe crypto jobs here.
202
203So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the
204crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the
205operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the
206message is displayed.
207
208At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento
209to be called. slotResult() then saves the result to some member variable and calls
210BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update
211will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it.
212This is where the second run begins.
213
214The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or
215processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with
216the current node. Now it finds that memento, since it was created in the first run. It checks if the
217memento's job has finished, and if so, the result can be written out (either the decrypted data or
218the verified signature).
219
220When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to
221note that the original MIME tree is never modified, and remains the same as the original one. The method
222createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to
223store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise
224some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the
225regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function
226NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent.
227Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent
228node to the newly created temporary node.
229
230When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the
231function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for
232the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence
233of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the
234given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node.
235
236To conclude: For async operations, parseObjectTree() is called twice: The first call starts the
237crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is
238there and can use its result for writing out the HTML.
239
240\par PartMetaData and ProcessResult
241
242For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the
243crypto state of a node. A PartMetaData can also be associated with a node by using
244NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of
245the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message.
246
247You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify
248the ProcessResult to tell the callers something about the state of the nodes that were processed.
249One example for its use is to tell the caller about the crypto state of the node.
250
251\par BodyPartFormatter Plugins
252
253As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp
254contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from
255a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro.
256
257The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not
258yet happened at the time of writing. Therefore the code can seem a bit chaotic.
259
260External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins
261can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento,
262BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler, HtmlWriter and URLHandler. Therefore
263external plugins have powerful capabilities, which are needed for example in the iCal formatter or
264in the vCard formatter.
265
266\par Special HTML tags
267
268As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special
269links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs.
270Also, some special HTML tags are created, which the Viewer later uses for post-processing. For
271example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which
272is used to mark an attachment in the body with a yellow border when the user clicks the attachment
273in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in
274the Viewer to scroll to the attachment.
275*/
276class ObjectTreeParser
277{
278 /**
279 * @internal
280 * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent.
281 */
282 ObjectTreeParser(const ObjectTreeParser &other);
283
284public:
285 explicit ObjectTreeParser(Interface::ObjectTreeSource *source,
286 NodeHelper *nodeHelper = nullptr,
287 bool showOneMimePart = false,
288 const AttachmentStrategy *attachmentStrategy = nullptr);
289
290 explicit ObjectTreeParser(const ObjectTreeParser *topLevelParser,
291 bool showOneMimePart = false,
292 const AttachmentStrategy *attachmentStrategy = nullptr);
293 virtual ~ObjectTreeParser();
294
295 void setAllowAsync(bool allow);
296 bool allowAsync() const;
297
298 bool hasPendingAsyncJobs() const;
299
300 /**
301 * The text of the message, ie. what would appear in the
302 * composer's text editor if this was edited or replied to.
303 * This is usually the content of the first text/plain MIME part.
304 */
305 QString plainTextContent() const;
306
307 /**
308 * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
309 *
310 * Not to be consfused with the HTML code that the message viewer widget displays, that HTML
311 * is written out by htmlWriter() and a totally different pair of shoes.
312 */
313 QString htmlContent() const;
314
315 /**
316 * The original charset of MIME part the plain text was extracted from.
317 *
318 * If there were more than one text/plain MIME parts in the mail, the this is the charset
319 * of the last MIME part processed.
320 */
321 QByteArray plainTextContentCharset() const;
322 QByteArray htmlContentCharset() const;
323
324 bool showOnlyOneMimePart() const;
325 void setShowOnlyOneMimePart(bool show);
326
327 const AttachmentStrategy *attachmentStrategy() const;
328
329 HtmlWriter *htmlWriter() const;
330
331 NodeHelper *nodeHelper() const;
332
333 /** Parse beginning at a given node and recursively parsing
334 the children of that node and it's next sibling. */
335 void parseObjectTree(KMime::Content *node);
336 MessagePartPtr parsedPart() const;
337
338private:
339 void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart);
340 void setPlainTextContent(const QString &plainTextContent);
341
342 /**
343 * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
344 * top-level content.
345 */
346 MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
347 bool processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart);
348
349 Interface::MessagePartPtr defaultHandling(KMime::Content *node, MimeTreeParser::ProcessResult &result, bool onlyOneMimePart);
350
351private:
352
353 /** ctor helper */
354 void init();
355
356 const QTextCodec *codecFor(KMime::Content *node) const;
357
358 void copyContentFrom(const ObjectTreeParser *other);
359
360private:
361 Interface::ObjectTreeSource *mSource;
362 NodeHelper *mNodeHelper;
363 HtmlWriter *mHtmlWriter;
364 QByteArray mPlainTextContentCharset;
365 QByteArray mHtmlContentCharset;
366 QString mPlainTextContent;
367 QString mHtmlContent;
368 KMime::Content *mTopLevelContent;
369 MessagePartPtr mParsedPart;
370
371 /// Show only one mime part means that the user has selected some node in the message structure
372 /// viewer that is not the root, which means the user wants to only see the selected node and its
373 /// children. If that is the case, this variable is set to true.
374 /// The code needs to behave differently if this is set. For example, it should not process the
375 /// siblings. Also, consider inline images: Normally, those nodes are completely hidden, as the
376 /// HTML node embedds them. However, when showing only the node of the image, one has to show them,
377 /// as their is no HTML node in which they are displayed. There are many more cases where this
378 /// variable needs to be obeyed.
379 /// This variable is set to false again when processing the children in stdChildHandling(), as
380 /// the children can be completely displayed again.
381 bool mShowOnlyOneMimePart;
382
383 bool mHasPendingAsyncJobs;
384 bool mAllowAsync;
385 const AttachmentStrategy *mAttachmentStrategy;
386 // DataUrl Icons cache
387 QString mCollapseIcon;
388 QString mExpandIcon;
389 bool mDeleteNodeHelper;
390
391 friend class PartNodeBodyPart;
392 friend class MessagePart;
393 friend class EncryptedMessagePart;
394 friend class SignedMessagePart;
395 friend class EncapsulatedRfc822MessagePart;
396 friend class TextMessagePart;
397 friend class HtmlMessagePart;
398 friend class TextPlainBodyPartFormatter;
399 friend class MultiPartSignedBodyPartFormatter;
400 friend class ApplicationPkcs7MimeBodyPartFormatter;
401};
402
403}
404
405#endif // __MIMETREEPARSER_OBJECTTREEPARSER_H__
406