Messagelib

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

KDE's Doxygen guidelines are available online.