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