summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mime/mimetreeparser/objecttreeparser.h')
-rw-r--r--framework/src/domain/mime/mimetreeparser/objecttreeparser.h351
1 files changed, 351 insertions, 0 deletions
diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
new file mode 100644
index 00000000..aebdae7b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
@@ -0,0 +1,351 @@
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 "messagepart.h"
39
40#include <gpgme++/verificationresult.h>
41
42class QString;
43
44namespace KMime
45{
46class Content;
47}
48
49namespace MimeTreeParser
50{
51
52class PartMetaData;
53class ViewerPrivate;
54class NodeHelper;
55class MimeMessagePart;
56
57typedef QSharedPointer<MessagePart> MessagePartPtr;
58typedef QSharedPointer<MimeMessagePart> MimeMessagePartPtr;
59
60/**
61\brief Parses messages and generates HTML display code out of them
62
63\par Introduction
64
65First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
66to understand the broader picture.
67
68Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
69KMime::Content.
70
71\par Basics
72
73The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only
74extracting the plainTextContent() for situations where only the message text is needed, for example
75when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the
76constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is
77used.
78
79Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on
80those nodes. Operating here means creating the HTML code for the node or extracting the textual
81content from it. This process is started with parseObjectTree(), where we loop over the subnodes
82of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can
83handle the type of the node. This can either be an internal function, such as
84processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin.
85More on external plugins later. When no matching formatter is found, defaultHandling() is called
86for that node.
87
88\par Multipart Nodes
89
90Those nodes that are of type multipart have subnodes. If one of those children needs to be
91processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that
92should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone
93of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all
94children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on
95one of the children, as the other one doesn't need to be displayed. Similary,
96processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the
97signed node.
98
99\par Processed and Unprocessed Nodes
100
101When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being
102not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a
103list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other
104related helper functions.
105
106It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the
107related functions. This is important so that processing the same node twice can be prevented. The
108check that prevents duplicate processing is in parseObjectTree().
109
110An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(),
111which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to
112prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call
113stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call
114parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the
115siblings is the plain text node, which shouldn't be processed! Therefore
116processMultiPartAlternativeSubtype() sets the plain text node as been processed already.
117
118\par Plain Text Output
119
120Various nodes have plain text that should be displayed. This plain text is usually processed though
121writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts
122it if necessary. It also pushes the text through quotedHTML(), which does a number of things like
123coloring quoted lines or detecting links and creating real link tags for them.
124
125\par Modifying the Message
126
127The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it
128before displaying. This is for example the case when displaying a decrypted message: The original
129message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts
130that blob. After decryption, the current node is replaced with the decrypted node, which happens
131in insertAndParseNewChildNode().
132
133\par Crypto Operations
134
135For signature and decryption handling, there are functions which help with generating the HTML code
136for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter()
137and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause
138the message to not be decrypted unless the user clicks a link. Whether the message should be
139decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the
140decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update
141of the Viewer, which will cause parseObjectTree() to be called again.
142
143\par Async Crypto Operations
144
145The above case describes decryption the message in place. However, decryption and also verifying of
146the signature can take a long time, so synchronous decryption and verifing would cause the Viewer to
147block. Therefore it is possible to run these operations in async mode, see allowAsync().
148In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the
149verify job, and informing the user that the operation is in progress with
150writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a
151BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can
152have multiple mementos associated with it, which are differeniated by name.
153
154NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and
155retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the
156job pointer, the job input data and the job result. Mementos can be used for any async situation,
157not just for crypto jobs, but I'll describe crypto jobs here.
158
159So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the
160crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the
161operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the
162message is displayed.
163
164At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento
165to be called. slotResult() then saves the result to some member variable and calls
166BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update
167will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it.
168This is where the second run begins.
169
170The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or
171processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with
172the current node. Now it finds that memento, since it was created in the first run. It checks if the
173memento's job has finished, and if so, the result can be written out (either the decrypted data or
174the verified signature).
175
176When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to
177note that the original MIME tree is never modified, and remains the same as the original one. The method
178createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to
179store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise
180some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the
181regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function
182NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent.
183Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent
184node to the newly created temporary node.
185
186When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the
187function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for
188the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence
189of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the
190given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node.
191
192To conclude: For async operations, parseObjectTree() is called twice: The first call starts the
193crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is
194there and can use its result for writing out the HTML.
195
196\par PartMetaData and ProcessResult
197
198For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the
199crypto state of a node. A PartMetaData can also be associated with a node by using
200NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of
201the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message.
202
203You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify
204the ProcessResult to tell the callers something about the state of the nodes that were processed.
205One example for its use is to tell the caller about the crypto state of the node.
206
207\par BodyPartFormatter Plugins
208
209As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp
210contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from
211a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro.
212
213The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not
214yet happened at the time of writing. Therefore the code can seem a bit chaotic.
215
216External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins
217can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento,
218BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler, HtmlWriter and URLHandler. Therefore
219external plugins have powerful capabilities, which are needed for example in the iCal formatter or
220in the vCard formatter.
221
222\par Special HTML tags
223
224As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special
225links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs.
226Also, some special HTML tags are created, which the Viewer later uses for post-processing. For
227example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which
228is used to mark an attachment in the body with a yellow border when the user clicks the attachment
229in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in
230the Viewer to scroll to the attachment.
231*/
232class ObjectTreeParser
233{
234 //Disable copy
235 ObjectTreeParser(const ObjectTreeParser &other);
236public:
237 explicit ObjectTreeParser();
238
239 explicit ObjectTreeParser(NodeHelper *nodeHelper,
240 bool showOneMimePart = false
241 );
242
243 virtual ~ObjectTreeParser();
244
245 void setAllowAsync(bool allow);
246 bool allowAsync() const;
247
248 bool hasPendingAsyncJobs() const;
249 void print();
250
251 /**
252 * The text of the message, ie. what would appear in the
253 * composer's text editor if this was edited or replied to.
254 * This is usually the content of the first text/plain MIME part.
255 */
256 QString plainTextContent() const;
257
258 /**
259 * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
260 *
261 * Not to be consfused with the HTML code that the message viewer widget displays, that HTML
262 * is written out by htmlWriter() and a totally different pair of shoes.
263 */
264 QString htmlContent() const;
265
266 /**
267 * The original charset of MIME part the plain text was extracted from.
268 *
269 * If there were more than one text/plain MIME parts in the mail, the this is the charset
270 * of the last MIME part processed.
271 */
272 QByteArray plainTextContentCharset() const;
273 QByteArray htmlContentCharset() const;
274
275 bool showOnlyOneMimePart() const;
276 void setShowOnlyOneMimePart(bool show);
277
278 NodeHelper *nodeHelper() const;
279
280 /** Parse beginning at a given node and recursively parsing
281 the children of that node and it's next sibling. */
282 void parseObjectTree(KMime::Content *node);
283 void parseObjectTree(const QByteArray &mimeMessage);
284 MessagePartPtr parsedPart() const;
285 KMime::Content *find(const std::function<bool(KMime::Content *)> &select);
286 QVector<MessagePartPtr> collectContentParts();
287 QVector<MessagePartPtr> collectAttachmentParts();
288 void decryptParts();
289 void importCertificates();
290
291 /** Embedd content referenced by cid by inlining */
292 QString resolveCidLinks(const QString &html);
293
294private:
295 /**
296 * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
297 * top-level content.
298 */
299 MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
300 MessagePartPtr processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType, bool onlyOneMimePart);
301
302 MessagePartPtr defaultHandling(KMime::Content *node, bool onlyOneMimePart);
303
304private:
305
306 /** ctor helper */
307 void init();
308
309 const QTextCodec *codecFor(KMime::Content *node) const;
310private:
311 NodeHelper *mNodeHelper;
312 QByteArray mPlainTextContentCharset;
313 QByteArray mHtmlContentCharset;
314 QString mPlainTextContent;
315 QString mHtmlContent;
316 KMime::Content *mTopLevelContent;
317 MessagePartPtr mParsedPart;
318
319 /// Show only one mime part means that the user has selected some node in the message structure
320 /// viewer that is not the root, which means the user wants to only see the selected node and its
321 /// children. If that is the case, this variable is set to true.
322 /// The code needs to behave differently if this is set. For example, it should not process the
323 /// siblings. Also, consider inline images: Normally, those nodes are completely hidden, as the
324 /// HTML node embedds them. However, when showing only the node of the image, one has to show them,
325 /// as their is no HTML node in which they are displayed. There are many more cases where this
326 /// variable needs to be obeyed.
327 /// This variable is set to false again when processing the children in stdChildHandling(), as
328 /// the children can be completely displayed again.
329 bool mShowOnlyOneMimePart;
330
331 bool mHasPendingAsyncJobs;
332 bool mAllowAsync;
333 bool mDeleteNodeHelper;
334 KMime::Message::Ptr mMsg;
335
336 friend class PartNodeBodyPart;
337 friend class MessagePart;
338 friend class EncryptedMessagePart;
339 friend class SignedMessagePart;
340 friend class EncapsulatedRfc822MessagePart;
341 friend class TextMessagePart;
342 friend class HtmlMessagePart;
343 friend class TextPlainBodyPartFormatter;
344 friend class MultiPartSignedBodyPartFormatter;
345 friend class ApplicationPkcs7MimeBodyPartFormatter;
346};
347
348}
349
350#endif // __MIMETREEPARSER_OBJECTTREEPARSER_H__
351