Messagelib

mdnadvicehelper.cpp
1 /*
2  SPDX-FileCopyrightText: 2020-2023 Laurent Montel <[email protected]>
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>
18 using namespace MessageComposer;
19 
20 static 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 
64 static const int numMdnMessageBoxes = sizeof mdnMessageBoxes / sizeof *mdnMessageBoxes;
65 
66 MDNAdviceHelper *MDNAdviceHelper::s_instance = nullptr;
67 MessageComposer::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 
79 QPair<bool, KMime::MDN::SendingMode> MDNAdviceHelper::checkAndSetMDNInfo(const Akonadi::Item &item, KMime::MDN::DispositionType d, bool forceSend)
80 {
82 
83  // RFC 2298: At most one MDN may be issued on behalf of each
84  // particular recipient by their user agent. That is, once an MDN
85  // has been issued on behalf of a recipient, no further MDNs may be
86  // issued on behalf of that recipient, even if another disposition
87  // is performed on the message.
90  // if already dealt with, don't do it again.
91  return QPair<bool, KMime::MDN::SendingMode>(false, KMime::MDN::SentAutomatically);
92  }
94 
95  KMime::MDN::SendingMode s = KMime::MDN::SentAutomatically; // set to manual if asked user
96  bool doSend = false;
97  // default:
98  int mode = MessageViewer::MessageViewerSettings::self()->defaultPolicy();
99  if (forceSend) { // We must send it
100  mode = 3;
101  } else {
102  if (!mode || (mode < 0) || (mode > 3)) {
103  // early out for ignore:
104  mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
105  s = KMime::MDN::SentManually;
106  } else {
108  mode = requestAdviceOnMDN("mdnUnknownOption");
109  s = KMime::MDN::SentManually;
110  // TODO set type to Failed as well
111  // and clear modifiers
112  }
113 
115  mode = requestAdviceOnMDN("mdnMultipleAddressesInReceiptTo");
116  s = KMime::MDN::SentManually;
117  }
118 
120  mode = requestAdviceOnMDN("mdnReturnPathEmpty");
121  s = KMime::MDN::SentManually;
122  }
123 
124  if (MessageFactoryNG::MDNReturnPathNotInRecieptTo(msg)) {
125  mode = requestAdviceOnMDN("mdnReturnPathNotInReceiptTo");
126  s = KMime::MDN::SentManually;
127  }
128 
130  if (s != KMime::MDN::SentManually) {
131  // don't ask again if user has already been asked. use the users' decision
132  mode = requestAdviceOnMDN("mdnNormalAsk");
133  s = KMime::MDN::SentManually; // asked user
134  }
135  } else { // if message doesn't have a disposition header, never send anything.
136  mode = 0;
137  }
138  }
139  }
140 
141  // RFC 2298: An MDN MUST NOT be generated in response to an MDN.
142  if (MessageComposer::Util::findTypeInMessage(msg.data(), "message", "disposition-notification")) {
143  mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
144  } else if (mode == 0) { // ignore
145  doSend = false;
146  mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNIgnore);
147  } else if (mode == 2) { // denied
148  doSend = true;
149  mdnStateAttr->setMDNState(Akonadi::MDNStateAttribute::MDNDenied);
150  } else if (mode == 3) { // the user wants to send. let's make sure we can, according to the RFC.
151  doSend = true;
152  mdnStateAttr->setMDNState(dispositionToSentState(d));
153  }
154 
155  // create a minimal version of item with just the attribute we want to change
156  Akonadi::Item i(item.id());
157  i.setRevision(item.revision());
158  i.setMimeType(item.mimeType());
159  i.addAttribute(mdnStateAttr);
160  auto modify = new Akonadi::ItemModifyJob(i);
161  modify->setIgnorePayload(true);
162  modify->disableRevisionCheck();
163  return QPair<bool, KMime::MDN::SendingMode>(doSend, s);
164 }
165 
166 Akonadi::MDNStateAttribute::MDNSentState MDNAdviceHelper::dispositionToSentState(KMime::MDN::DispositionType d)
167 {
168  switch (d) {
169  case KMime::MDN::Displayed:
171  case KMime::MDN::Deleted:
173  case KMime::MDN::Dispatched:
175  case KMime::MDN::Processed:
177  case KMime::MDN::Denied:
179  case KMime::MDN::Failed:
181  default:
183  }
184 }
185 
186 QPair<QString, bool> MDNAdviceHelper::mdnMessageText(const char *what)
187 {
188  for (int i = 0; i < numMdnMessageBoxes; ++i) {
189  if (!qstrcmp(what, mdnMessageBoxes[i].dontAskAgainID)) {
190  return {mdnMessageBoxes[i].text.toString(), mdnMessageBoxes[i].canDeny};
191  }
192  }
193  return {};
194 }
195 
196 int MDNAdviceHelper::requestAdviceOnMDN(const char *what)
197 {
198  const QPair<QString, bool> mdnInfo = mdnMessageText(what);
199  if (mdnInfo.first.isEmpty()) {
200  qCWarning(MESSAGECOMPOSER_LOG) << "didn't find data for message box \"" << what << "\"";
201  return MessageComposer::MDNIgnore;
202  } else {
204  const MessageComposer::MDNAdvice answer = questionIgnoreSend(mdnInfo.first, mdnInfo.second);
205  switch (answer) {
206  case MessageComposer::MDNSend:
207  return 3;
208 
209  case MessageComposer::MDNSendDenied:
210  return 2;
211 
212  // don't use 1, as that's used for 'default ask" in checkMDNHeaders
213  default:
214  case MessageComposer::MDNIgnore:
215  return 0;
216  }
217  }
218 }
T * data() const const
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
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...
int revision() const
QString mimeType() const
static bool MDNReturnPathEmpty(const KMime::Message::Ptr &msg)
If sending an MDN requires confirmation due to discrepancy between MDN header and Return-Path header.
Contains various factory methods for creating new messages such as replies, MDNs, forwards,...
ArrowCursor
static bool MDNMDNUnknownOption(const KMime::Message::Ptr &msg)
If the MDN headers contain options that KMail can't parse.
static bool MDNConfirmMultipleRecipients(const KMime::Message::Ptr &msg)
If sending an MDN requires confirmation due to multiple addresses.
bool hasAttribute() const
Id id() const
MDNStateAttribute::MDNSentState mdnState() const
const T * attribute() const
QString message
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Apr 1 2023 04:01:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.