Messagelib

templateparserjob.h
1 /*
2  SPDX-FileCopyrightText: 2017-2021 Laurent Montel <[email protected]>
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 
17 namespace KIdentityManagement
18 {
19 class IdentityManager;
20 }
21 
22 namespace MessageCore
23 {
24 class ImageCollector;
25 }
26 
27 struct TemplateParserExtractHtmlInfoResult;
28 class TemplateParserJobTest;
29 
30 namespace TemplateParser
31 {
32 class 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. The addContent()
114  * method can be used for adding sub-content to content object in case of
115  * attachments. The addContent() method is not used for adding content of the
116  * above mentioned single-part, as addContent() will convert single-part to
117  * multipart-mixed before adding it to mMsg.
118  *
119  * \par Make multipart/alternative mails
120  * First of all a KMime::Content (content) is created with a content-type of
121  * multipart/alternative. Then in the same way as plain-part is created in above
122  * paragraph, a KMime::Content (sub-content) containing the plainBody is created
123  * and added as child to the content. Then a new KMime::Content (sub-content)
124  * with htmlBody as the body is created. The content-type is set as text/html.
125  * This new sub-content is then added to the parent content. Now, since the
126  * parent content (multipart/alternative) has two sub-content (text/plain and
127  * text/html) to it, it is added to the reply message (mMsg).
128  *
129  * TODO: What is the usecase of the commands that work on the message to be transformed?
130  * In general you only use the commands that work on the original message...
131  */
132 class TEMPLATEPARSER_EXPORT TemplateParserJob : public QObject
133 {
134  Q_OBJECT
135  friend class TemplateParserJobTest;
136 
137 public:
138  enum Mode { NewMessage, Reply, ReplyAll, Forward };
139 
140  enum AllowSelection { SelectionAllowed, NoSelectionAllowed };
141 
142  enum Quotes { ReplyAsOriginalMessage, ReplyAsPlain, ReplyAsHtml };
143 
144 public:
145  explicit TemplateParserJob(const KMime::Message::Ptr &amsg, const Mode amode, QObject *parent = nullptr);
146  ~TemplateParserJob() override;
147 
148  /**
149  * Sets the selection. If this is set, only the selection will be added to
150  * commands such as %QUOTE. Otherwise, the whole message is quoted.
151  * If this is not called at all, the whole message is quoted as well.
152  * Call this before calling process().
153  */
154  void setSelection(const QString &selection);
155 
156  /**
157  * Sets whether the template parser is allowed to decrypt the original
158  * message when needing its message text, for example for the %QUOTE command.
159  * If true, it will tell the ObjectTreeParser it uses internally to decrypt the
160  * message, and that will possibly show a password request dialog to the user.
161  *
162  * The default is false.
163  */
164  void setAllowDecryption(const bool allowDecryption);
165 
166  /**
167  * Tell template parser whether or not to wrap words, and at what column
168  * to wrap at.
169  *
170  * Default is true, wrapping at 80chars.
171  */
172  void setWordWrap(bool wrap, int wrapColWidth = 80);
173 
174  /**
175  * Set the identity manager to be used when creating the template.
176  */
177  void setIdentityManager(KIdentityManagement::IdentityManager *ident);
178 
179  /**
180  * Sets the list of charsets to try to use to encode the resulting text.
181  * They are tried in order until one matches, or utf-8 as a fallback.
182  */
183  void setCharsets(const QStringList &charsets);
184 
185  void process(const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
186  void process(const QString &tmplName, const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
187  void processWithIdentity(uint uoid, const KMime::Message::Ptr &aorig_msg, qint64 afolder = -1);
188 
189  void processWithTemplate(const QString &tmpl);
190 
191  void setReplyAsHtml(bool replyAsHtml);
192 
193 Q_SIGNALS:
194  void parsingDone(bool cursorPositionWasSet);
195  void parsingFailed();
196 
197 private:
198  void slotExtractInfoDone(const TemplateParserExtractHtmlInfoResult &result);
199  /// This finds the template to use. Either the one from the folder, identity or
200  /// finally the global template.
201  /// This also reads the To and CC address of the template
202  /// @return the contents of the template
203  Q_REQUIRED_RESULT QString findTemplate();
204 
205  /// Finds the template with the given name.
206  /// This also reads the To and CC address of the template
207  /// @return the contents of the template
208  Q_REQUIRED_RESULT QString findCustomTemplate(const QString &tmpl);
209 
210  Q_REQUIRED_RESULT QString pipe(const QString &cmd, const QString &buf);
211 
212  /**
213  * Called by processWithTemplate(). This adds the completely processed body to
214  * the message.
215  *
216  * This function creates plain text message or multipart/alternative message,
217  * depending on whether the processed body has @p htmlBody or not.
218  *
219  * In append mode, this will simply append the text to the body.
220  *
221  * Otherwise, the content of the old message is deleted and replaced with @p plainBody
222  * and @p htmlBody.
223  * Attachments of the original message are also added back to the new message.
224  */
225  void addProcessedBodyToMessage(const QString &plainBody, const QString &htmlBody) const;
226 
227  /**
228  * Determines whether the signature should be stripped when getting the text
229  * of the original message, e.g. for commands such as %QUOTE
230  */
231  bool shouldStripSignature() const;
232 
233  static int parseQuotes(const QString &prefix, const QString &str, QString &quote);
234 
235  /**
236  * Return the text signature used the by current identity.
237  */
238  Q_REQUIRED_RESULT QString getPlainSignature() const;
239 
240  /**
241  * Return the HTML signature used the by current identity.
242  */
243  Q_REQUIRED_RESULT QString getHtmlSignature() const;
244 
245  /**
246  * Returns message body indented by the
247  * given indentation string. This is suitable for including the message
248  * in another message of for replies, forwards.
249  *
250  * No attachments are handled if includeAttach is false.
251  * The signature is stripped if aStripSignature is true and
252  * smart quoting is turned on. Signed or encrypted texts
253  * get converted to plain text when allowDecryption is true.
254  */
255  Q_REQUIRED_RESULT QString quotedPlainText(const QString &selection = QString()) const;
256 
257  /**
258  * Returns HTML message body.
259  * This is suitable for including the message
260  * in another message of for replies, forwards.
261  *
262  * No attachments are handled if includeAttach is false.
263  * The signature is stripped if aStripSignature is true and
264  * smart quoting is turned on. Signed or encrypted texts
265  * get converted to plain text when allowDecryption is true.
266  */
267  Q_REQUIRED_RESULT QString quotedHtmlText(const QString &selection) const;
268 
269  /**
270  * This function return the plain text part from the OTP.
271  * For HTML only mails. It returns the converted plain text
272  * from the OTP.
273  * @param allowSelectionOnly takes care that if a reply/forward
274  * is made to a selected part of message, then the selection is
275  * returned as it is without going through th OTP
276  * @param aStripSignature strips the signature out of the message
277  *
278  */
279  Q_REQUIRED_RESULT QString plainMessageText(bool aStripSignature, AllowSelection isSelectionAllowed) const;
280 
281  /**
282  * Returns the HTML content of the message as plain text
283  */
284  Q_REQUIRED_RESULT QString htmlMessageText(bool aStripSignature, AllowSelection isSelectionAllowed);
285 
286  /** @return the UOID of the identity for this message.
287  * Searches the "x-kmail-identity" header and if that fails,
288  * searches with KIdentityManagement::IdentityManager::identityForAddress()
289  */
290  Q_REQUIRED_RESULT uint identityUoid(const KMime::Message::Ptr &msg) const;
291 
292  /**
293  * Returns KMime content of the plain text part of the message after setting
294  * its mime type, charset and CTE.
295  * This function is called by:-
296  * 1) TemplateParser::addProcessedBodyToMessage(), which uses this content
297  * to simply create the text/plain message
298  *
299  * 2) TemplateParser::createMultipartAlternativeContent() which adds this content
300  * to create the multipart/alternative message.
301  */
302  KMime::Content *createPlainPartContent(const QString &plainBody) const;
303 
304  /**
305  * Returns KMime content of the multipart/alternative part of the message
306  * after setting the mime type, charset and CTE of its respective text/plain
307  * part and text/html part.
308  */
309  KMime::Content *createMultipartAlternativeContent(const QString &plainBody, const QString &htmlBody) const;
310 
311  /**
312  * Returns a multipart/mixed KMime::Content that has textPart and all
313  * attachments as children.
314  * @param attachments the list of attachments to add
315  * @param textPart a KMime::Content that is to be added as a child.
316  * @since 4.8
317  */
318  KMime::Content *createMultipartMixed(const QVector<KMime::Content *> &attachments, KMime::Content *textPart) const;
319 
320  /**
321  * Returnsa multipart/related KMime::Content that has mainTextPart and all
322  * embedded images as children.
323  * @param ac a reference to an MessageCore::ImageCollector that has
324  * collected all attachments.
325  * @param mainTextPart a KMime::Content that is to be added as a child.
326  * @since 4.8
327  */
328  KMime::Content *createMultipartRelated(const MessageCore::ImageCollector &ic, KMime::Content *mainTextPart) const;
329 
330  /**
331  * Checks if the signature is HTML or not.
332  */
333  Q_REQUIRED_RESULT bool isHtmlSignature() const;
334 
335  /**
336  * Does the necessary conversions like escaping characters, changing "\n" to
337  * breakline tag before appending text to htmlBody.
338  */
339  static Q_REQUIRED_RESULT QString plainTextToHtml(const QString &body);
340 
341  /**
342  * Make a HTML content valid by adding missing html/head/body tag.
343  */
344  void makeValidHtml(QString &body);
345 
346  std::unique_ptr<TemplateParserJobPrivate> d;
347 };
348 }
349 
The TemplateParser transforms a message with a given template.
A helper class to collect the embedded images of a email.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Dec 4 2021 23:12:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.