Messagelib

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

KDE's Doxygen guidelines are available online.