Messagelib

templateparserjob.h
1/*
2 SPDX-FileCopyrightText: 2017-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#pragma once
8
9#include "templateparser_export.h"
10
11#include <KMime/Message>
12
13#include <QObject>
14
15#include <memory>
16
17namespace KIdentityManagementCore
18{
19class IdentityManager;
20}
21
22namespace MessageCore
23{
24class ImageCollector;
25}
26
27struct TemplateParserExtractHtmlInfoResult;
28class TemplateParserJobTest;
29
30namespace TemplateParser
31{
32class TemplateParserJobPrivate;
33/**
34 * \brief The TemplateParser transforms a message with a given template.
35 *
36 * \par Introduction
37 * The TemplateParser transforms a message with a given template.
38 * A template contains text and commands, such as %QUOTE or %ODATE, which will be
39 * replaced with the real values in process().
40 *
41 * \par Basics
42 * The message given in the templateparser constructor amsg, is the message that
43 * is being transformed.
44 * aorig_msg is the original message to which actions are performed.
45 * The message text in amsg will be replaced by the processed text of the template,
46 * but other properties, such as the attachments or the subject, are preserved.
47 *
48 * There are two different kind of commands: Those that work on the message that is
49 * to be transformed and those that work on an 'original message'.
50 * Those that work on the message that is to be transformed have no special prefix, e.g.
51 * '%DATE'. Those that work on the original message have an 'O' prefix, for example
52 * '%ODATE'.
53 * This means that the %DATE command will take the date of the message passed in the
54 * constructor, the message which is to be transformed, whereas the %ODATE command will
55 * take the date of the message that is being passed in process(), the original message.
56 *
57 * \par The process()
58 * The process() function takes aorig_msg as parameter. This aorig_msg is the original
59 * message from which various commands in templates with prefix 'O' extract the data and adds
60 * to the processed message.
61 * This function finds the template and passes them to processWithTemplate(), where templates
62 * are processed and its value is added to the processed message.
63 *
64 * \par Reply To/Forward Plain Text Mails
65 * Plain Text mails are the mails with only text part and no html part. While creating
66 * reply/forward to mails, processWithTemplate() processes all the commands and then
67 * appends its values to plainBody and htmlBody. This function then on the
68 * basis of whether the user wants to use plain mails or HTML mails, clears the htmlBody,
69 * or just passes both the plainBody and htmlBody unaltered.
70 *
71 * \par Reply To/Forward HTML Mails
72 * By HTML mails here, we mean multipart/alternative mails. As mentioned above, all
73 * commands in the TemplateParser appends text, i.e. plain text to plainBody
74 * and html text to htmlBody in the function processWithTemplate().
75 * This function also takes a decision of clearing the htmlBody on the basis of fact
76 * whether the user wants to reply/forward using plain mails or multipart/alternative
77 * mails.
78 *
79 * \par When "TO" and when "NOT TO" make multipart/alternative Mails
80 * User is the master to decide when to and when not to make multipart/alternative mails.
81 * <b>For user who <u>don't prefer</u> using HTML mails</b>
82 * There is a TemplateParserSettings::self()->replyUsingHtml() (in GUI as Settings->Configure KMail->
83 * Composer->General->"Reply using HTML if present"), which when not true (checkbox disabled
84 * in UI), will clear the htmlBody.
85 * An another option within the standard templates, %FORCEDPLAIN command raises the flag,
86 * ReplyAsPlain. This flag when raised in processWithTemplate() takes care that the
87 * processed message will contain text/plain part by clearing the htmlBody.
88 *
89 * Once the htmlBody is cleared, plainBody and an empty htmlBody is passed to
90 * addProcessedBodyToMessage(). Here since the htmlBody is empty, text/plain messages are
91 * assembled and thus user is not dealing with any kind of HTML part.
92 *
93 * <b>For user who <u>do prefer</u> using HTML mails</b>
94 * The setting discussed above as "Reply using HTML if present" (when checked to true),
95 * passes the htmlBody to addProcessedBodyToMessage() without doing any changes.
96 * An another option %FORCEDHTML within standard templates command raises the flag ReplyAsHtml.
97 * This flag when raised in processWithTemplate() takes care that the htmlBody is passed to
98 * addProcessedBodyToMessage() unaltered.
99 *
100 * Since htmlBody received by addProcessedBodyToMessage() is not empty, multipart/alternative
101 * messages are assembled.
102 *
103 * @NOTE Resolving conflict between TemplateParserSettings "replyUsingHtml" and FORCEDXXXX command.
104 * The conflict is resolved by simply giving preference to the commands over TemplateParserSettings.
105 *
106 * \par Make plain part
107 * mMsg is the reply message in which the message text will be replaced by the
108 * processed value from templates.
109 *
110 * In case of no attachments, the message will be a single-part message.
111 * A KMime::Content containing the plainBody from processWithTemplate() is
112 * created. Then the encodedBody(), contentType (text/plain) of this
113 * KMime::Content is set in the body and the header of mMsg.
114 *
115 * \par Make multipart/alternative mails
116 * First of all a KMime::Content (content) is created with a content-type of
117 * multipart/alternative. Then in the same way as plain-part is created in above
118 * paragraph, a KMime::Content (sub-content) containing the plainBody is created
119 * and added as child to the content. Then a new KMime::Content (sub-content)
120 * with htmlBody as the body is created. The content-type is set as text/html.
121 * This new sub-content is then added to the parent content. Now, since the
122 * parent content (multipart/alternative) has two sub-content (text/plain and
123 * text/html) to it, it is added to the reply message (mMsg).
124 *
125 * TODO: What is the usecase of the commands that work on the message to be transformed?
126 * In general you only use the commands that work on the original message...
127 */
128class TEMPLATEPARSER_EXPORT TemplateParserJob : public QObject
129{
130 Q_OBJECT
131 friend class TemplateParserJobTest;
132
133public:
134 enum Mode {
135 NewMessage,
136 Reply,
137 ReplyAll,
138 Forward,
139 };
140
141 enum AllowSelection {
142 SelectionAllowed,
143 NoSelectionAllowed,
144 };
145
146 enum Quotes {
147 ReplyAsOriginalMessage,
148 ReplyAsPlain,
149 ReplyAsHtml,
150 };
151
152public:
153 explicit TemplateParserJob(const KMime::Message::Ptr &amsg, const Mode amode, QObject *parent = nullptr);
154 ~TemplateParserJob() override;
155
156 /**
157 * Sets the selection. If this is set, only the selection will be added to
158 * commands such as %QUOTE. Otherwise, the whole message is quoted.
159 * If this is not called at all, the whole message is quoted as well.
160 * Call this before calling process().
161 */
162 void setSelection(const QString &selection);
163
164 /**
165 * Sets whether the template parser is allowed to decrypt the original
166 * message when needing its message text, for example for the %QUOTE command.
167 * If true, it will tell the ObjectTreeParser it uses internally to decrypt the
168 * message, and that will possibly show a password request dialog to the user.
169 *
170 * The default is false.
171 */
172 void setAllowDecryption(const bool allowDecryption);
173
174 /**
175 * Tell template parser whether or not to wrap words, and at what column
176 * to wrap at.
177 *
178 * Default is true, wrapping at 80chars.
179 */
180 void setWordWrap(bool wrap, int wrapColWidth = 80);
181
182 /**
183 * Set the identity manager to be used when creating the template.
184 */
185 void setIdentityManager(KIdentityManagementCore::IdentityManager *ident);
186
187 void process(const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
188 void process(const QString &tmplName, const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
189 void processWithIdentity(uint uoid, const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
190
191 void processWithTemplate(const QString &tmpl);
192
193 void setReplyAsHtml(bool replyAsHtml);
194
195Q_SIGNALS:
196 void parsingDone(bool cursorPositionWasSet);
197 void parsingFailed();
198
199private:
200 void slotExtractInfoDone(const TemplateParserExtractHtmlInfoResult &result);
201 /// This finds the template to use. Either the one from the folder, identity or
202 /// finally the global template.
203 /// This also reads the To and CC address of the template
204 /// @return the contents of the template
205 [[nodiscard]] QString findTemplate();
206
207 /// Finds the template with the given name.
208 /// This also reads the To and CC address of the template
209 /// @return the contents of the template
210 [[nodiscard]] QString findCustomTemplate(const QString &tmpl);
211
212 [[nodiscard]] QString pipe(const QString &cmd, const QString &buf);
213
214 /**
215 * Called by processWithTemplate(). This adds the completely processed body to
216 * the message.
217 *
218 * This function creates plain text message or multipart/alternative message,
219 * depending on whether the processed body has @p htmlBody or not.
220 *
221 * In append mode, this will simply append the text to the body.
222 *
223 * Otherwise, the content of the old message is deleted and replaced with @p plainBody
224 * and @p htmlBody.
225 * Attachments of the original message are also added back to the new message.
226 */
227 void addProcessedBodyToMessage(const QString &plainBody, const QString &htmlBody) const;
228
229 /**
230 * Determines whether the signature should be stripped when getting the text
231 * of the original message, e.g. for commands such as %QUOTE
232 */
233 bool shouldStripSignature() const;
234
235 static int parseQuotes(const QString &prefix, const QString &str, QString &quote);
236
237 /**
238 * Return the text signature used the by current identity.
239 */
240 [[nodiscard]] QString getPlainSignature() const;
241
242 /**
243 * Return the HTML signature used the by current identity.
244 */
245 [[nodiscard]] QString getHtmlSignature() const;
246
247 /**
248 * Returns message body indented by the
249 * given indentation string. This is suitable for including the message
250 * in another message of for replies, forwards.
251 *
252 * No attachments are handled if includeAttach is false.
253 * The signature is stripped if aStripSignature is true and
254 * smart quoting is turned on. Signed or encrypted texts
255 * get converted to plain text when allowDecryption is true.
256 */
257 [[nodiscard]] QString quotedPlainText(const QString &selection = QString()) const;
258
259 /**
260 * Returns HTML message body.
261 * This is suitable for including the message
262 * in another message of for replies, forwards.
263 *
264 * No attachments are handled if includeAttach is false.
265 * The signature is stripped if aStripSignature is true and
266 * smart quoting is turned on. Signed or encrypted texts
267 * get converted to plain text when allowDecryption is true.
268 */
269 [[nodiscard]] QString quotedHtmlText(const QString &selection) const;
270
271 /**
272 * This function return the plain text part from the OTP.
273 * For HTML only mails. It returns the converted plain text
274 * from the OTP.
275 * @param allowSelectionOnly takes care that if a reply/forward
276 * is made to a selected part of message, then the selection is
277 * returned as it is without going through th OTP
278 * @param aStripSignature strips the signature out of the message
279 *
280 */
281 [[nodiscard]] QString plainMessageText(bool aStripSignature, AllowSelection isSelectionAllowed) const;
282
283 /**
284 * Returns the HTML content of the message as plain text
285 */
286 [[nodiscard]] QString htmlMessageText(bool aStripSignature, AllowSelection isSelectionAllowed);
287
288 /** @return the UOID of the identity for this message.
289 * Searches the "x-kmail-identity" header and if that fails,
290 * searches with KIdentityManagementCore::IdentityManager::identityForAddress()
291 */
292 [[nodiscard]] uint identityUoid(const KMime::Message::Ptr &msg) const;
293
294 /**
295 * Returns KMime content of the plain text part of the message after setting
296 * its mime type, charset and CTE.
297 * This function is called by:-
298 * 1) TemplateParser::addProcessedBodyToMessage(), which uses this content
299 * to simply create the text/plain message
300 *
301 * 2) TemplateParser::createMultipartAlternativeContent() which adds this content
302 * to create the multipart/alternative message.
303 */
304 KMime::Content *createPlainPartContent(const QString &plainBody) const;
305
306 /**
307 * Returns KMime content of the multipart/alternative part of the message
308 * after setting the mime type, charset and CTE of its respective text/plain
309 * part and text/html part.
310 */
311 KMime::Content *createMultipartAlternativeContent(const QString &plainBody, const QString &htmlBody) const;
312
313 /**
314 * Returns a multipart/mixed KMime::Content that has textPart and all
315 * attachments as children.
316 * @param attachments the list of attachments to add
317 * @param textPart a KMime::Content that is to be added as a child.
318 * @since 4.8
319 */
320 KMime::Content *createMultipartMixed(const QList<KMime::Content *> &attachments, KMime::Content *textPart) const;
321
322 /**
323 * Returnsa multipart/related KMime::Content that has mainTextPart and all
324 * embedded images as children.
325 * @param ac a reference to an MessageCore::ImageCollector that has
326 * collected all attachments.
327 * @param mainTextPart a KMime::Content that is to be added as a child.
328 * @since 4.8
329 */
330 KMime::Content *createMultipartRelated(const MessageCore::ImageCollector &ic, KMime::Content *mainTextPart) const;
331
332 /**
333 * Checks if the signature is HTML or not.
334 */
335 [[nodiscard]] bool isHtmlSignature() const;
336
337 /**
338 * Does the necessary conversions like escaping characters, changing "\n" to
339 * breakline tag before appending text to htmlBody.
340 */
341 [[nodiscard]] static QString plainTextToHtml(const QString &body);
342
343 /**
344 * Make a HTML content valid by adding missing html/head/body tag.
345 */
346 void makeValidHtml(QString &body);
347
348 friend class ::TemplateParserJobTest;
349 std::unique_ptr<TemplateParserJobPrivate> d;
350};
351}
A helper class to collect the embedded images of a email.
The TemplateParser transforms a message with a given template.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:07:25 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.