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>
27using namespace KMime::Types;
28using namespace KPIM;
29using namespace MessageComposer;
30
31static 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
43static 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
62class MessageComposer::AkonadiSenderPrivate
63{
64public:
65 AkonadiSenderPrivate() = default;
66
67 QSet<KJob *> mPendingJobs;
68 int mCustomTransportId = -1;
69};
70
71AkonadiSender::AkonadiSender(QObject *parent)
72 : QObject(parent)
73 , d(new MessageComposer::AkonadiSenderPrivate)
74{
75}
76
77AkonadiSender::~AkonadiSender() = default;
78
79bool 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
92bool 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
111void 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
214void 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"
static IdentityManager * self()
Identity & modifyIdentityForName(const QString &identityName)
Identity & modifyIdentityForUoid(uint uoid)
virtual QString errorString() const
int error() const
void result(KJob *job)
Transport * transportById(Transport::Id id, bool def=true) const
static TransportManager * self()
bool doSendQueued(int transportId=-1) override
Send queued messages, using the specified transport or the default, if none is given.
bool doSend(const KMime::Message::Ptr &msg, short sendNow) override
Send given message.
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
KCODECS_EXPORT QString normalizeAddressesAndEncodeIdn(const QString &str)
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.
void push_back(parameter_type value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
int toInt(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:25 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.