1 /*
2   SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3   SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
4   SPDX-FileCopyrightText: 2017-2021 Laurent Montel <montel@kde.org>
5 
6   SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #pragma once
10 
11 #include "messagecomposer_export.h"
12 #include <KMime/KMimeMessage>
13 #include <KMime/MDN>
14 
15 #include <Akonadi/Collection>
16 #include <Akonadi/Item>
17 #include <Akonadi/KMime/MessageStatus>
18 #include <QVector>
19 
20 namespace KIdentityManagement
21 {
22 class IdentityManager;
23 }
24 
25 namespace MessageComposer
26 {
27 /**
28  * Enumeration that defines the available reply "modes"
29  */
30 enum ReplyStrategy {
31     ReplySmart = 0, //< Attempt to automatically guess the best recipient for the reply
32     ReplyAuthor, //< Reply to the author of the message (possibly NOT the mailing list, if any)
33     ReplyList, //< Reply to the mailing list (and not the author of the message)
34     ReplyAll, //< Reply to author and all the recipients in CC
35     ReplyNone //< Don't set reply addresses: they will be set manually
36 };
37 
38 enum MDNAdvice { MDNIgnore, MDNSendDenied, MDNSend };
39 /**
40  * Contains various factory methods for creating new messages such as replies, MDNs, forwards, etc.
41  */
42 class MESSAGECOMPOSER_EXPORT MessageFactoryNG : public QObject
43 {
44     Q_OBJECT
45 public:
46     /// Small helper structure which encapsulates the KMime::Message created when creating a reply, and
47     /// the reply mode
48     struct MessageReply {
49         KMime::Message::Ptr msg = nullptr; ///< The actual reply message
50         bool replyAll = false; ///< If true, the "reply all" template was used, otherwise the normal reply
51         ///  template
52     };
53 
54     explicit MessageFactoryNG(const KMime::Message::Ptr &origMsg,
55                               Akonadi::Item::Id id,
56                               const Akonadi::Collection &col = Akonadi::Collection(),
57                               QObject *parent = nullptr);
58     ~MessageFactoryNG() override;
59 
60     /**
61      * Create a new message that is a reply to this message, filling all
62      * required header fields with the proper values. The returned message
63      * is not stored in any folder. Marks this message as replied.
64      *
65      */
66     void createReplyAsync();
67 
68     /** Create a new message that is a forward of this message, filling all
69     required header fields with the proper values. The returned message
70     is not stored in any folder. Marks this message as forwarded. */
71     void createForwardAsync();
72 
73     /**
74      * Create a forward from the given list of messages, attaching each
75      *  message to be forwarded to the new forwarded message.
76      *
77      * If no list is passed, use the original message passed in the MessageFactoryNG
78      *  constructor.
79      */
80     Q_REQUIRED_RESULT QPair<KMime::Message::Ptr, QVector<KMime::Content *>> createAttachedForward(const Akonadi::Item::List &items = Akonadi::Item::List());
81 
82     /** Create a new message that is a redirect to this message, filling all
83     required header fields with the proper values. The returned message
84     is not stored in any folder. Marks this message as replied.
85     Redirects differ from forwards so they are forwarded to some other
86     user, mail is not changed and the reply-to field is set to
87     the email address of the original sender.
88     */
89     Q_REQUIRED_RESULT KMime::Message::Ptr createRedirect(const QString &toStr,
90                                                          const QString &ccStr = QString(),
91                                                          const QString &bccStr = QString(),
92                                                          int transportId = -1,
93                                                          const QString &fcc = QString(),
94                                                          int identity = -1);
95 
96     Q_REQUIRED_RESULT KMime::Message::Ptr createResend();
97 
98     /** Create a new message that is a delivery receipt of this message,
99       filling required header fields with the proper values. The
100       returned message is not stored in any folder. */
101     Q_REQUIRED_RESULT KMime::Message::Ptr createDeliveryReceipt();
102 
103     /** Create a new message that is a MDN for this message, filling all
104       required fields with proper values. The returned message is not
105       stored in any folder.
106 
107       @param a Use AutomaticAction for filtering and ManualAction for
108                user-induced events.
109       @param d See docs for KMime::MDN::DispositionType
110       @param s See docs for KMime::MDN::SendingMode (in KMail, use MDNAdvideDialog to ask the user for this parameter)
111       @param m See docs for KMime::MDN::DispositionModifier
112 
113       @return The notification message or 0, if none should be sent, as well as the state of the MDN operation.
114     **/
115     Q_REQUIRED_RESULT KMime::Message::Ptr createMDN(KMime::MDN::ActionMode a,
116                                                     KMime::MDN::DispositionType d,
117                                                     KMime::MDN::SendingMode s,
118                                                     int mdnQuoteOriginal = 0,
119                                                     const QVector<KMime::MDN::DispositionModifier> &m = QVector<KMime::MDN::DispositionModifier>());
120 
121     /**
122      * Create a new forwarded MIME digest. If the user is trying to forward multiple messages
123      *  at once all inline, they can choose to have them be compiled into a single digest
124      *  message.
125      *
126      * This will return a header message and individual message parts to be set as
127      *  attachments.
128      *
129      * @param msgs List of messages to be composed into a digest
130      */
131     Q_REQUIRED_RESULT QPair<KMime::Message::Ptr, KMime::Content *> createForwardDigestMIME(const Akonadi::Item::List &items);
132 
133     /**
134      * Set the identity manager to be used when creating messages.
135      * Required to be set before create* is called, otherwise the created messages
136      *  might have the wrong identity data.
137      */
138     void setIdentityManager(KIdentityManagement::IdentityManager *ident);
139 
140     /**
141      * Set the reply strategy to use. Default is ReplySmart.
142      */
143     void setReplyStrategy(MessageComposer::ReplyStrategy replyStrategy);
144 
145     /**
146      * Set the selection to be used to  base the reply on.
147      */
148     void setSelection(const QString &selection);
149 
150     /**
151      * Whether to quote the original message in the reply.
152      *  Default is to quote.
153      */
154     void setQuote(bool quote);
155 
156     /**
157      * Set the template to be used when creating the reply. Default is to not
158      *  use any template at all.
159      */
160     void setTemplate(const QString &templ);
161 
162     /**
163      * Set extra mailinglist addresses to send the created message to.
164      * Any mailing-list addresses specified in the original message
165      * itself will be added by MessageFactoryNG, so no need to add those manually.
166      */
167     void setMailingListAddresses(const KMime::Types::Mailbox::List &listAddresses);
168 
169     /**
170      *  Set the identity that is set for the folder in which the given message is.
171      *   It is used as a fallback when finding the identity if it can't be found in
172      *   any other way.
173      *   @param folderIdentityId an uoid of KIdentityManagement::Identity
174      */
175     void setFolderIdentity(uint folderIdentityId);
176 
177     /**
178      * Whether or not to put the reply to a message in the same folder as the message itself.
179      *  If so, specify the folder id in which to put them. Default is -1, which means to not put
180      *  replies in the same folder at all.
181      */
182     void putRepliesInSameFolder(Akonadi::Item::Id parentColId = -1);
183 
184     /**
185      * When creating MDNs, the user needs to be asked for confirmation in specific
186      *  cases according to RFC 2298.
187      */
188     Q_REQUIRED_RESULT static bool MDNRequested(const KMime::Message::Ptr &msg);
189 
190     /**
191      * If sending an MDN requires confirmation due to multiple addresses.
192      *
193      * RFC 2298: [ Confirmation from the user SHOULD be obtained (or no
194      * MDN sent) ] if there is more than one distinct address in the
195      * Disposition-Notification-To header.
196      */
197     Q_REQUIRED_RESULT static bool MDNConfirmMultipleRecipients(const KMime::Message::Ptr &msg);
198 
199     /**
200      *
201      * If sending an MDN requires confirmation due to discrepancy between MDN
202      *  header and Return-Path header.
203      *
204      * RFC 2298: MDNs SHOULD NOT be sent automatically if the address in
205      * the Disposition-Notification-To header differs from the address
206      * in the Return-Path header. [...] Confirmation from the user
207      * SHOULD be obtained (or no MDN sent) if there is no Return-Path
208      * header in the message [...]
209      */
210     Q_REQUIRED_RESULT static bool MDNReturnPathEmpty(const KMime::Message::Ptr &msg);
211     Q_REQUIRED_RESULT static bool MDNReturnPathNotInRecieptTo(const KMime::Message::Ptr &msg);
212 
213     /**
214      * If the MDN headers contain options that KMail can't parse
215      */
216     Q_REQUIRED_RESULT static bool MDNMDNUnknownOption(const KMime::Message::Ptr &msg);
217 
218     Q_REQUIRED_RESULT bool replyAsHtml() const;
219     void setReplyAsHtml(bool replyAsHtml);
220 
221 Q_SIGNALS:
222     void createReplyDone(const MessageComposer::MessageFactoryNG::MessageReply &reply);
223     void createForwardDone(const KMime::Message::Ptr &msg);
224 
225 private Q_SLOTS:
226     void slotCreateReplyDone(const KMime::Message::Ptr &msg, bool replyAll);
227     void slotCreateForwardDone(const KMime::Message::Ptr &msg);
228 
229 private:
230     /** @return the UOID of the identity for this message.
231       Searches the "x-kmail-identity" header and if that fails,
232       searches with KIdentityManagement::IdentityManager::identityForAddress()
233     **/
234     Q_REQUIRED_RESULT uint identityUoid(const KMime::Message::Ptr &msg);
235 
236     Q_REQUIRED_RESULT QString replaceHeadersInString(const KMime::Message::Ptr &msg, const QString &s);
237 
238     /*
239      * If force charset option is enabled, try to set the original charset
240      *  in the newly created message. If unable to encode, fall back to
241      *  preferred charsets, and if all fail, use UTF-8.
242      */
243     void applyCharset(const KMime::Message::Ptr msg);
244 
245     Q_REQUIRED_RESULT QByteArray getRefStr(const KMime::Message::Ptr &msg);
246     KMime::Content *createForwardAttachmentMessage(const KMime::Message::Ptr &fwdMsg);
247 
248     KIdentityManagement::IdentityManager *mIdentityManager = nullptr;
249     // Required parts to create messages
250     KMime::Message::Ptr mOrigMsg;
251     uint mFolderId;
252     Akonadi::Item::Id mParentFolderId;
253 
254     Akonadi::Collection mCollection;
255 
256     // Optional settings the calling class may set
257     MessageComposer::ReplyStrategy mReplyStrategy;
258     QString mSelection;
259     QString mTemplate;
260     bool mQuote = true;
261     bool mReplyAsHtml = false;
262     KMime::Types::Mailbox::List mMailingListAddresses;
263     Akonadi::Item::Id mId;
264 };
265 }
266 
267 Q_DECLARE_METATYPE(MessageComposer::ReplyStrategy)
268 Q_DECLARE_METATYPE(MessageComposer::MessageFactoryNG::MessageReply)
269 
270