Messagelib

akonadisender.cpp
1 /*
2  * This file is part of KMail.
3  * SPDX-FileCopyrightText: 2009 Constantin Berzan <[email protected]>
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  */
7 
8 #include "akonadisender.h"
9 
10 #include "messagecomposer_debug.h"
11 
12 #include "MessageComposer/Util"
13 #include "helper/messagehelper.h"
14 #include "settings/messagecomposersettings.h"
15 #include "utils/util_p.h"
16 
17 #include <Libkdepim/ProgressManager>
18 
19 #include <KEmailAddress>
20 #include <KIdentityManagement/Identity>
21 #include <KIdentityManagement/IdentityManager>
22 #include <MailTransport/Transport>
23 #include <MailTransport/TransportManager>
24 #include <MailTransportAkonadi/DispatcherInterface>
25 #include <MailTransportAkonadi/MessageQueueJob>
26 #include <MessageCore/StringUtil>
27 using namespace KMime::Types;
28 using namespace KPIM;
29 using namespace MailTransport;
30 using namespace MessageComposer;
31 
32 static QStringList addrSpecListToStringList(const AddrSpecList &l, bool allowEmpty = false)
33 {
34  QStringList result;
35  for (AddrSpecList::const_iterator it = l.constBegin(), end = l.constEnd(); it != end; ++it) {
36  const QString s = (*it).asString();
37  if (allowEmpty || !s.isEmpty()) {
38  result.push_back(s);
39  }
40  }
41  return result;
42 }
43 
44 static void extractSenderToCCAndBcc(const KMime::Message::Ptr &aMsg, QString &sender, QStringList &to, QStringList &cc, QStringList &bcc)
45 {
46  sender = aMsg->sender()->asUnicodeString();
47  if (aMsg->headerByType("X-KMail-Recipients")) {
48  // extended BCC handling to prevent TOs and CCs from seeing
49  // BBC information by looking at source of an OpenPGP encrypted mail
50  to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "X-KMail-Recipients"));
51  aMsg->removeHeader("X-KMail-Recipients");
52  } else if (aMsg->headerByType("Resent-To")) {
53  to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-To"));
54  cc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-Cc"));
55  bcc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-Bcc"));
56  } else {
57  to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "To"));
58  cc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Cc"));
59  bcc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Bcc"));
60  }
61 }
62 
63 class MessageComposer::AkonadiSenderPrivate
64 {
65 public:
66  AkonadiSenderPrivate() = default;
67 
68  QSet<KJob *> mPendingJobs;
69  int mCustomTransportId = -1;
70 };
71 
72 AkonadiSender::AkonadiSender(QObject *parent)
73  : QObject(parent)
74  , d(new MessageComposer::AkonadiSenderPrivate)
75 {
76 }
77 
78 AkonadiSender::~AkonadiSender() = default;
79 
80 bool AkonadiSender::doSend(const KMime::Message::Ptr &aMsg, short sendNow)
81 {
82  if (sendNow == -1) {
83  sendNow = MessageComposer::MessageComposerSettings::self()->sendImmediate(); // -1 == use default setting
84  }
85  if (sendNow) {
86  sendOrQueueMessage(aMsg, MessageComposer::MessageSender::SendImmediate);
87  } else {
88  sendOrQueueMessage(aMsg, MessageComposer::MessageSender::SendLater);
89  }
90  return true;
91 }
92 
93 bool AkonadiSender::doSendQueued(int customTransportId)
94 {
95  qCDebug(MESSAGECOMPOSER_LOG) << "Sending queued message with custom transport:" << customTransportId;
96  if (!MessageComposer::Util::sendMailDispatcherIsOnline()) {
97  return false;
98  }
99 
100  d->mCustomTransportId = customTransportId;
101 
102  auto dispatcher = new DispatcherInterface();
103  if (d->mCustomTransportId == -1) {
104  dispatcher->dispatchManually();
105  } else {
106  dispatcher->dispatchManualTransport(d->mCustomTransportId);
107  }
108  delete dispatcher;
109  return true;
110 }
111 
112 void AkonadiSender::sendOrQueueMessage(const KMime::Message::Ptr &message, MessageComposer::MessageSender::SendMethod method)
113 {
114  Q_ASSERT(message);
115  qCDebug(MESSAGECOMPOSER_LOG) << "KMime::Message: \n[\n" << message->encodedContent().left(1000) << "\n]\n";
116 
117  auto qjob = new MessageQueueJob(this);
118  if (message->hasHeader("X-KMail-FccDisabled")) {
119  qjob->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::Delete);
120  } else if (auto hrd = message->headerByType("X-KMail-Fcc")) {
121  qjob->sentBehaviourAttribute().setSentBehaviour(SentBehaviourAttribute::MoveToCollection);
122  const int sentCollectionId = hrd->asUnicodeString().toInt();
123  qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId));
124  } else if (auto hrd = message->headerByType("X-KMail-Identity")) {
126  const QString identityStrId = hrd->asUnicodeString();
127  const KIdentityManagement::Identity id = im->modifyIdentityForUoid(identityStrId.toUInt());
128  const QString fccId = id.fcc();
129  qjob->sentBehaviourAttribute().setSentBehaviour(SentBehaviourAttribute::MoveToCollection);
130  const int sentCollectionId = fccId.toInt();
131  qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId));
132  } else if (auto hrd = message->headerByType("X-KMail-Identity-Name")) {
134  const QString identityStrName = hrd->asUnicodeString();
135  const KIdentityManagement::Identity id = im->modifyIdentityForName(identityStrName);
136  const QString fccId = id.fcc();
137  qjob->sentBehaviourAttribute().setSentBehaviour(SentBehaviourAttribute::MoveToCollection);
138  const int sentCollectionId = fccId.toInt();
139  qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId));
140  } else {
141  qjob->sentBehaviourAttribute().setSentBehaviour(MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection);
142  }
143  qjob->setMessage(message);
144 
145  // Get transport.
146  int transportId = -1;
147  if (d->mCustomTransportId != -1) {
148  transportId = d->mCustomTransportId;
149  } else {
150  if (auto hrd = message->headerByType("X-KMail-Transport")) {
151  transportId = hrd->asUnicodeString().toInt();
152  }
153  }
154  const Transport *transport = TransportManager::self()->transportById(transportId);
155  if (!transport) {
156  qCDebug(MESSAGECOMPOSER_LOG) << " No transport defined. Need to create it";
157  qjob->deleteLater();
158  return;
159  }
160 
161  if ((method == MessageComposer::MessageSender::SendImmediate) && !MessageComposer::Util::sendMailDispatcherIsOnline()) {
162  qjob->deleteLater();
163  return;
164  }
165 
166  qCDebug(MESSAGECOMPOSER_LOG) << "Using transport (" << transport->name() << "," << transport->id() << ")";
167  qjob->transportAttribute().setTransportId(transport->id());
168 
169  // if we want to manually queue it for sending later, then do it
170  if (method == MessageComposer::MessageSender::SendLater) {
171  qjob->dispatchModeAttribute().setDispatchMode(MailTransport::DispatchModeAttribute::Manual);
172  }
173 
174  // Get addresses.
175  QStringList to;
176  QStringList cc;
177  QStringList bcc;
178  QString from;
179  extractSenderToCCAndBcc(message, from, to, cc, bcc);
180  qjob->addressAttribute().setFrom(from);
181  qjob->addressAttribute().setTo(to);
182  qjob->addressAttribute().setCc(cc);
183  qjob->addressAttribute().setBcc(bcc);
184 
185  if (qjob->addressAttribute().to().isEmpty()) {
186  qCWarning(MESSAGECOMPOSER_LOG) << " Impossible to specify TO! It's a bug";
187  qjob->deleteLater();
188  return;
189  }
190 
191  if (transport && transport->specifySenderOverwriteAddress()) {
192  qjob->addressAttribute().setFrom(
194  } else {
195  qjob->addressAttribute().setFrom(KEmailAddress::extractEmailAddress(KEmailAddress::normalizeAddressesAndEncodeIdn(message->from()->asUnicodeString())));
196  }
197 
198  MessageComposer::Util::addSendReplyForwardAction(message, qjob);
199 
201  message->assemble();
202 
203  // Queue the message.
204  connect(qjob, &MessageQueueJob::result, this, &AkonadiSender::queueJobResult);
205  d->mPendingJobs.insert(qjob);
206  qjob->start();
207  qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob started.";
208 
209  // TODO potential problem:
210  // The MDA finishes sending a message before I queue the next one, and
211  // thinking it is finished, the progress item deletes itself.
212  // Turn the MDA offline until everything is queued?
213 }
214 
215 void AkonadiSender::queueJobResult(KJob *job)
216 {
217  Q_ASSERT(d->mPendingJobs.contains(job));
218  d->mPendingJobs.remove(job);
219 
220  if (job->error()) {
221  qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob failed with error" << job->errorString();
222  } else {
223  qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob success.";
224  }
225 }
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
void removePrivateHeaderFields(const KMime::Message::Ptr &message, bool cleanUpHeader)
Removes all private header fields (e.g.
Definition: stringutil.cpp:354
void push_back(const T &value)
Identity & modifyIdentityForUoid(uint uoid)
Identity & modifyIdentityForName(const QString &identityName)
bool isEmpty() const const
int toInt(bool *ok, int base) const const
static IdentityManager * self()
uint toUInt(bool *ok, int base) const const
QString left(int n) const const
virtual QString errorString() const
int error() const
QString message
KCODECS_EXPORT QString normalizeAddressesAndEncodeIdn(const QString &str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Mar 22 2023 04:07:13 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.