Messagelib

mdnadvicehelper.cpp
1/*
2 SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "mdnadvicehelper.h"
8#include "mdnadvicedialog.h"
9#include "messagecomposer_debug.h"
10#include <Akonadi/ItemModifyJob>
11#include <KCursorSaver>
12#include <KLazyLocalizedString>
13#include <KLocalizedString>
14#include <MessageComposer/Util>
15#include <MessageViewer/MessageViewerSettings>
16#include <QPointer>
18using namespace MessageComposer;
19
20static const struct {
21 const char *dontAskAgainID;
22 bool canDeny;
23 const KLazyLocalizedString text;
24} mdnMessageBoxes[] = {
25 {"mdnNormalAsk",
26 true,
27 kli18n("This message contains a request to return a notification "
28 "about your reception of the message.\n"
29 "You can either ignore the request or let the mail program "
30 "send a \"denied\" or normal response.")},
31 {"mdnUnknownOption",
32 false,
33 kli18n("This message contains a request to send a notification "
34 "about your reception of the message.\n"
35 "It contains a processing instruction that is marked as "
36 "\"required\", but which is unknown to the mail program.\n"
37 "You can either ignore the request or let the mail program "
38 "send a \"failed\" response.")},
39 {"mdnMultipleAddressesInReceiptTo",
40 true,
41 kli18n("This message contains a request to send a notification "
42 "about your reception of the message,\n"
43 "but it is requested to send the notification to more "
44 "than one address.\n"
45 "You can either ignore the request or let the mail program "
46 "send a \"denied\" or normal response.")},
47 {"mdnReturnPathEmpty",
48 true,
49 kli18n("This message contains a request to send a notification "
50 "about your reception of the message,\n"
51 "but there is no return-path set.\n"
52 "You can either ignore the request or let the mail program "
53 "send a \"denied\" or normal response.")},
54 {"mdnReturnPathNotInReceiptTo",
55 true,
56 kli18n("This message contains a request to send a notification "
57 "about your reception of the message,\n"
58 "but the return-path address differs from the address "
59 "the notification was requested to be sent to.\n"
60 "You can either ignore the request or let the mail program "
61 "send a \"denied\" or normal response.")},
62};
63
64static const int numMdnMessageBoxes = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
65
66MDNAdviceHelper *MDNAdviceHelper::s_instance = nullptr;
67MessageComposer::MDNAdvice MDNAdviceHelper::questionIgnoreSend(const QString &text, bool canDeny)
68{
69 MessageComposer::MDNAdvice rc = MessageComposer::MDNIgnore;
70 QPointer<MDNAdviceDialog> dlg(new MDNAdviceDialog(text, canDeny));
71 dlg->exec();
72 if (dlg) {
73 rc = dlg->result();
74 }
75 delete dlg;
76 return rc;
77}
78
79MDNAdviceHelper *MDNAdviceHelper::instance()
80{
81 if (!s_instance) {
82 s_instance = new MDNAdviceHelper;
83 }
84
85 return s_instance;
86}
87
88QPair<bool, KMime::MDN::SendingMode> MDNAdviceHelper::checkAndSetMDNInfo(const Akonadi::Item &item, KMime::MDN::DispositionType d, bool forceSend)
89{
90 KMime::Message::Ptr msg = MessageComposer::Util::message(item);
91
92 // RFC 2298: At most one MDN may be issued on behalf of each
93 // particular recipient by their user agent. That is, once an MDN
94 // has been issued on behalf of a recipient, no further MDNs may be
95 // issued on behalf of that recipient, even if another disposition
96 // is performed on the message.
99 // if already dealt with, don't do it again.
100 return QPair<bool, KMime::MDN::SendingMode>(false, KMime::MDN::SentAutomatically);
101 }
103
104 KMime::MDN::SendingMode s = KMime::MDN::SentAutomatically; // set to manual if asked user
105 bool doSend = false;
106 // default:
107 int mode = MessageViewer::MessageViewerSettings::self()->defaultPolicy();
108 if (forceSend) { // We must send it
109 mode = 3;
110 } else {
111 if (!mode || (mode < 0) || (mode > 3)) {
112 // early out for ignore:
113 mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
114 s = KMime::MDN::SentManually;
115 } else {
117 mode = requestAdviceOnMDN("mdnUnknownOption");
118 s = KMime::MDN::SentManually;
119 // TODO set type to Failed as well
120 // and clear modifiers
121 }
122
124 mode = requestAdviceOnMDN("mdnMultipleAddressesInReceiptTo");
125 s = KMime::MDN::SentManually;
126 }
127
129 mode = requestAdviceOnMDN("mdnReturnPathEmpty");
130 s = KMime::MDN::SentManually;
131 }
132
133 if (MessageFactoryNG::MDNReturnPathNotInRecieptTo(msg)) {
134 mode = requestAdviceOnMDN("mdnReturnPathNotInReceiptTo");
135 s = KMime::MDN::SentManually;
136 }
137
139 if (s != KMime::MDN::SentManually) {
140 // don't ask again if user has already been asked. use the users' decision
141 mode = requestAdviceOnMDN("mdnNormalAsk");
142 s = KMime::MDN::SentManually; // asked user
143 }
144 } else { // if message doesn't have a disposition header, never send anything.
145 mode = 0;
146 }
147 }
148 }
149
150 // RFC 2298: An MDN MUST NOT be generated in response to an MDN.
151 if (MessageComposer::Util::findTypeInMessage(msg.data(), "message", "disposition-notification")) {
152 mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
153 } else if (mode == 0) { // ignore
154 doSend = false;
155 mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
156 } else if (mode == 2) { // denied
157 doSend = true;
158 mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNDenied);
159 } else if (mode == 3) { // the user wants to send. let's make sure we can, according to the RFC.
160 doSend = true;
161 mdnStateAttr->setMDNState(dispositionToSentState(d));
162 }
163
164 // create a minimal version of item with just the attribute we want to change
165 Akonadi::Item i(item.id());
166 i.setRevision(item.revision());
167 i.setMimeType(item.mimeType());
168 i.addAttribute(mdnStateAttr);
169 auto modify = new Akonadi::ItemModifyJob(i);
170 modify->setIgnorePayload(true);
171 modify->disableRevisionCheck();
172 return QPair<bool, KMime::MDN::SendingMode>(doSend, s);
173}
174
175Akonadi::MDNStateAttribute::MDNSentState MDNAdviceHelper::dispositionToSentState(KMime::MDN::DispositionType d)
176{
177 switch (d) {
178 case KMime::MDN::Displayed:
180 case KMime::MDN::Deleted:
182 case KMime::MDN::Dispatched:
184 case KMime::MDN::Processed:
186 case KMime::MDN::Denied:
188 case KMime::MDN::Failed:
190 default:
192 }
193}
194
195QPair<QString, bool> MDNAdviceHelper::mdnMessageText(const char *what)
196{
197 for (int i = 0; i < numMdnMessageBoxes; ++i) {
198 if (!qstrcmp(what, mdnMessageBoxes[i].dontAskAgainID)) {
199 return {mdnMessageBoxes[i].text.toString(), mdnMessageBoxes[i].canDeny};
200 }
201 }
202 return {};
203}
204
205int MDNAdviceHelper::requestAdviceOnMDN(const char *what)
206{
207 const QPair<QString, bool> mdnInfo = mdnMessageText(what);
208 if (mdnInfo.first.isEmpty()) {
209 qCWarning(MESSAGECOMPOSER_LOG) << "didn't find data for message box \"" << what << "\"";
210 return MessageComposer::MDNIgnore;
211 } else {
213 const MessageComposer::MDNAdvice answer = questionIgnoreSend(mdnInfo.first, mdnInfo.second);
214 switch (answer) {
215 case MessageComposer::MDNSend:
216 return 3;
217
218 case MessageComposer::MDNSendDenied:
219 return 2;
220
221 // don't use 1, as that's used for 'default ask" in checkMDNHeaders
222 default:
223 case MessageComposer::MDNIgnore:
224 return 0;
225 }
226 }
227}
228
229#include "moc_mdnadvicehelper.cpp"
QString mimeType() const
bool hasAttribute() const
Id id() const
int revision() const
const T * attribute() const
MDNStateAttribute::MDNSentState mdnState() const
Contains various factory methods for creating new messages such as replies, MDNs, forwards,...
static bool MDNRequested(const KMime::Message::Ptr &msg)
When creating MDNs, the user needs to be asked for confirmation in specific cases according to RFC 22...
static bool MDNMDNUnknownOption(const KMime::Message::Ptr &msg)
If the MDN headers contain options that KMail can't parse.
static bool MDNReturnPathEmpty(const KMime::Message::Ptr &msg)
If sending an MDN requires confirmation due to discrepancy between MDN header and Return-Path header.
static bool MDNConfirmMultipleRecipients(const KMime::Message::Ptr &msg)
If sending an MDN requires confirmation due to multiple addresses.
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
T * data() const const
ArrowCursor
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:08:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.