diff options
Diffstat (limited to 'framework/src/domain/mimetreeparser/otp/objecttreeparser.h')
-rw-r--r-- | framework/src/domain/mimetreeparser/otp/objecttreeparser.h | 406 |
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 | |||
42 | class QString; | ||
43 | |||
44 | namespace KMime | ||
45 | { | ||
46 | class Content; | ||
47 | } | ||
48 | |||
49 | namespace MimeTreeParser | ||
50 | { | ||
51 | |||
52 | namespace Interface | ||
53 | { | ||
54 | class MessagePart; | ||
55 | typedef QSharedPointer<MessagePart> MessagePartPtr; | ||
56 | } | ||
57 | |||
58 | class PartMetaData; | ||
59 | class ViewerPrivate; | ||
60 | class HtmlWriter; | ||
61 | class AttachmentStrategy; | ||
62 | class NodeHelper; | ||
63 | class MessagePart; | ||
64 | class MimeMessagePart; | ||
65 | |||
66 | typedef QSharedPointer<MessagePart> MessagePartPtr; | ||
67 | typedef QSharedPointer<MimeMessagePart> MimeMessagePartPtr; | ||
68 | |||
69 | class ProcessResult | ||
70 | { | ||
71 | public: | ||
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 | |||
96 | private: | ||
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 | |||
109 | First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate | ||
110 | to understand the broader picture. | ||
111 | |||
112 | Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a | ||
113 | KMime::Content. | ||
114 | |||
115 | \par Basics | ||
116 | |||
117 | The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only | ||
118 | extracting the plainTextContent() for situations where only the message text is needed, for example | ||
119 | when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the | ||
120 | constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is | ||
121 | used. | ||
122 | |||
123 | Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on | ||
124 | those nodes. Operating here means creating the HTML code for the node or extracting the textual | ||
125 | content from it. This process is started with parseObjectTree(), where we loop over the subnodes | ||
126 | of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can | ||
127 | handle the type of the node. This can either be an internal function, such as | ||
128 | processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin. | ||
129 | More on external plugins later. When no matching formatter is found, defaultHandling() is called | ||
130 | for that node. | ||
131 | |||
132 | \par Multipart Nodes | ||
133 | |||
134 | Those nodes that are of type multipart have subnodes. If one of those children needs to be | ||
135 | processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that | ||
136 | should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone | ||
137 | of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all | ||
138 | children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on | ||
139 | one of the children, as the other one doesn't need to be displayed. Similary, | ||
140 | processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the | ||
141 | signed node. | ||
142 | |||
143 | \par Processed and Unprocessed Nodes | ||
144 | |||
145 | When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being | ||
146 | not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a | ||
147 | list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other | ||
148 | related helper functions. | ||
149 | |||
150 | It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the | ||
151 | related functions. This is important so that processing the same node twice can be prevented. The | ||
152 | check that prevents duplicate processing is in parseObjectTree(). | ||
153 | |||
154 | An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(), | ||
155 | which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to | ||
156 | prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call | ||
157 | stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call | ||
158 | parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the | ||
159 | siblings is the plain text node, which shouldn't be processed! Therefore | ||
160 | processMultiPartAlternativeSubtype() sets the plain text node as been processed already. | ||
161 | |||
162 | \par Plain Text Output | ||
163 | |||
164 | Various nodes have plain text that should be displayed. This plain text is usually processed though | ||
165 | writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts | ||
166 | it if necessary. It also pushes the text through quotedHTML(), which does a number of things like | ||
167 | coloring quoted lines or detecting links and creating real link tags for them. | ||
168 | |||
169 | \par Modifying the Message | ||
170 | |||
171 | The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it | ||
172 | before displaying. This is for example the case when displaying a decrypted message: The original | ||
173 | message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts | ||
174 | that blob. After decryption, the current node is replaced with the decrypted node, which happens | ||
175 | in insertAndParseNewChildNode(). | ||
176 | |||
177 | \par Crypto Operations | ||
178 | |||
179 | For signature and decryption handling, there are functions which help with generating the HTML code | ||
180 | for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter() | ||
181 | and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause | ||
182 | the message to not be decrypted unless the user clicks a link. Whether the message should be | ||
183 | decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the | ||
184 | decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update | ||
185 | of the Viewer, which will cause parseObjectTree() to be called again. | ||
186 | |||
187 | \par Async Crypto Operations | ||
188 | |||
189 | The above case describes decryption the message in place. However, decryption and also verifying of | ||
190 | the signature can take a long time, so synchronous decryption and verifing would cause the Viewer to | ||
191 | block. Therefore it is possible to run these operations in async mode, see allowAsync(). | ||
192 | In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the | ||
193 | verify job, and informing the user that the operation is in progress with | ||
194 | writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a | ||
195 | BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can | ||
196 | have multiple mementos associated with it, which are differeniated by name. | ||
197 | |||
198 | NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and | ||
199 | retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the | ||
200 | job pointer, the job input data and the job result. Mementos can be used for any async situation, | ||
201 | not just for crypto jobs, but I'll describe crypto jobs here. | ||
202 | |||
203 | So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the | ||
204 | crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the | ||
205 | operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the | ||
206 | message is displayed. | ||
207 | |||
208 | At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento | ||
209 | to be called. slotResult() then saves the result to some member variable and calls | ||
210 | BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update | ||
211 | will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it. | ||
212 | This is where the second run begins. | ||
213 | |||
214 | The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or | ||
215 | processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with | ||
216 | the current node. Now it finds that memento, since it was created in the first run. It checks if the | ||
217 | memento's job has finished, and if so, the result can be written out (either the decrypted data or | ||
218 | the verified signature). | ||
219 | |||
220 | When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to | ||
221 | note that the original MIME tree is never modified, and remains the same as the original one. The method | ||
222 | createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to | ||
223 | store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise | ||
224 | some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the | ||
225 | regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function | ||
226 | NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent. | ||
227 | Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent | ||
228 | node to the newly created temporary node. | ||
229 | |||
230 | When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the | ||
231 | function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for | ||
232 | the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence | ||
233 | of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the | ||
234 | given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node. | ||
235 | |||
236 | To conclude: For async operations, parseObjectTree() is called twice: The first call starts the | ||
237 | crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is | ||
238 | there and can use its result for writing out the HTML. | ||
239 | |||
240 | \par PartMetaData and ProcessResult | ||
241 | |||
242 | For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the | ||
243 | crypto state of a node. A PartMetaData can also be associated with a node by using | ||
244 | NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of | ||
245 | the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message. | ||
246 | |||
247 | You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify | ||
248 | the ProcessResult to tell the callers something about the state of the nodes that were processed. | ||
249 | One example for its use is to tell the caller about the crypto state of the node. | ||
250 | |||
251 | \par BodyPartFormatter Plugins | ||
252 | |||
253 | As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp | ||
254 | contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from | ||
255 | a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro. | ||
256 | |||
257 | The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not | ||
258 | yet happened at the time of writing. Therefore the code can seem a bit chaotic. | ||
259 | |||
260 | External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins | ||
261 | can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento, | ||
262 | BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler, HtmlWriter and URLHandler. Therefore | ||
263 | external plugins have powerful capabilities, which are needed for example in the iCal formatter or | ||
264 | in the vCard formatter. | ||
265 | |||
266 | \par Special HTML tags | ||
267 | |||
268 | As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special | ||
269 | links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs. | ||
270 | Also, some special HTML tags are created, which the Viewer later uses for post-processing. For | ||
271 | example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which | ||
272 | is used to mark an attachment in the body with a yellow border when the user clicks the attachment | ||
273 | in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in | ||
274 | the Viewer to scroll to the attachment. | ||
275 | */ | ||
276 | class 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 | |||
284 | public: | ||
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 | |||
338 | private: | ||
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 | |||
351 | private: | ||
352 | |||
353 | /** ctor helper */ | ||
354 | void init(); | ||
355 | |||
356 | const QTextCodec *codecFor(KMime::Content *node) const; | ||
357 | |||
358 | void copyContentFrom(const ObjectTreeParser *other); | ||
359 | |||
360 | private: | ||
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 | |||