File indexing completed on 2024-06-23 05:18:37
0001 /* 0002 * This file is part of KMail. 0003 * SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "akonadisender.h" 0009 0010 #include "messagecomposer_debug.h" 0011 0012 #include "MessageComposer/Util" 0013 #include "helper/messagehelper.h" 0014 #include "settings/messagecomposersettings.h" 0015 #include "utils/util_p.h" 0016 0017 #include <Libkdepim/ProgressManager> 0018 0019 #include <Akonadi/DispatcherInterface> 0020 #include <Akonadi/MessageQueueJob> 0021 #include <KEmailAddress> 0022 #include <KIdentityManagementCore/Identity> 0023 #include <KIdentityManagementCore/IdentityManager> 0024 #include <MailTransport/Transport> 0025 #include <MailTransport/TransportManager> 0026 #include <MessageCore/StringUtil> 0027 using namespace KMime::Types; 0028 using namespace KPIM; 0029 using namespace MessageComposer; 0030 0031 static QStringList addrSpecListToStringList(const AddrSpecList &l, bool allowEmpty = false) 0032 { 0033 QStringList result; 0034 for (AddrSpecList::const_iterator it = l.constBegin(), end = l.constEnd(); it != end; ++it) { 0035 const QString s = (*it).asString(); 0036 if (allowEmpty || !s.isEmpty()) { 0037 result.push_back(s); 0038 } 0039 } 0040 return result; 0041 } 0042 0043 static void extractSenderToCCAndBcc(const KMime::Message::Ptr &aMsg, QString &sender, QStringList &to, QStringList &cc, QStringList &bcc) 0044 { 0045 sender = aMsg->sender()->asUnicodeString(); 0046 if (aMsg->headerByType("X-KMail-Recipients")) { 0047 // extended BCC handling to prevent TOs and CCs from seeing 0048 // BBC information by looking at source of an OpenPGP encrypted mail 0049 to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "X-KMail-Recipients")); 0050 aMsg->removeHeader("X-KMail-Recipients"); 0051 } else if (aMsg->headerByType("Resent-To")) { 0052 to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-To")); 0053 cc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-Cc")); 0054 bcc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Resent-Bcc")); 0055 } else { 0056 to = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "To")); 0057 cc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Cc")); 0058 bcc = addrSpecListToStringList(MessageHelper::extractAddrSpecs(aMsg, "Bcc")); 0059 } 0060 } 0061 0062 class MessageComposer::AkonadiSenderPrivate 0063 { 0064 public: 0065 AkonadiSenderPrivate() = default; 0066 0067 QSet<KJob *> mPendingJobs; 0068 int mCustomTransportId = -1; 0069 }; 0070 0071 AkonadiSender::AkonadiSender(QObject *parent) 0072 : QObject(parent) 0073 , d(new MessageComposer::AkonadiSenderPrivate) 0074 { 0075 } 0076 0077 AkonadiSender::~AkonadiSender() = default; 0078 0079 bool AkonadiSender::doSend(const KMime::Message::Ptr &aMsg, short sendNow) 0080 { 0081 if (sendNow == -1) { 0082 sendNow = MessageComposer::MessageComposerSettings::self()->sendImmediate(); // -1 == use default setting 0083 } 0084 if (sendNow) { 0085 sendOrQueueMessage(aMsg, MessageComposer::MessageSender::SendImmediate); 0086 } else { 0087 sendOrQueueMessage(aMsg, MessageComposer::MessageSender::SendLater); 0088 } 0089 return true; 0090 } 0091 0092 bool AkonadiSender::doSendQueued(int customTransportId) 0093 { 0094 qCDebug(MESSAGECOMPOSER_LOG) << "Sending queued message with custom transport:" << customTransportId; 0095 if (!MessageComposer::Util::sendMailDispatcherIsOnline()) { 0096 return false; 0097 } 0098 0099 d->mCustomTransportId = customTransportId; 0100 0101 auto dispatcher = new Akonadi::DispatcherInterface(); 0102 if (d->mCustomTransportId == -1) { 0103 dispatcher->dispatchManually(); 0104 } else { 0105 dispatcher->dispatchManualTransport(d->mCustomTransportId); 0106 } 0107 delete dispatcher; 0108 return true; 0109 } 0110 0111 void AkonadiSender::sendOrQueueMessage(const KMime::Message::Ptr &message, MessageComposer::MessageSender::SendMethod method) 0112 { 0113 Q_ASSERT(message); 0114 qCDebug(MESSAGECOMPOSER_LOG) << "KMime::Message: \n[\n" << message->encodedContent().left(1000) << "\n]\n"; 0115 0116 auto qjob = new Akonadi::MessageQueueJob(this); 0117 if (message->hasHeader("X-KMail-FccDisabled")) { 0118 qjob->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::Delete); 0119 } else if (auto hrd = message->headerByType("X-KMail-Fcc")) { 0120 qjob->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::MoveToCollection); 0121 const int sentCollectionId = hrd->asUnicodeString().toInt(); 0122 qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId)); 0123 } else if (auto hrd = message->headerByType("X-KMail-Identity")) { 0124 KIdentityManagementCore::IdentityManager *im = KIdentityManagementCore::IdentityManager::self(); 0125 const QString identityStrId = hrd->asUnicodeString(); 0126 const KIdentityManagementCore::Identity id = im->modifyIdentityForUoid(identityStrId.toUInt()); 0127 const QString fccId = id.fcc(); 0128 qjob->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::MoveToCollection); 0129 const int sentCollectionId = fccId.toInt(); 0130 qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId)); 0131 } else if (auto hrd = message->headerByType("X-KMail-Identity-Name")) { 0132 KIdentityManagementCore::IdentityManager *im = KIdentityManagementCore::IdentityManager::self(); 0133 const QString identityStrName = hrd->asUnicodeString(); 0134 const KIdentityManagementCore::Identity id = im->modifyIdentityForName(identityStrName); 0135 const QString fccId = id.fcc(); 0136 qjob->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::MoveToCollection); 0137 const int sentCollectionId = fccId.toInt(); 0138 qjob->sentBehaviourAttribute().setMoveToCollection(Akonadi::Collection(sentCollectionId)); 0139 } else { 0140 qjob->sentBehaviourAttribute().setSentBehaviour(Akonadi::SentBehaviourAttribute::MoveToDefaultSentCollection); 0141 } 0142 qjob->setMessage(message); 0143 0144 // Get transport. 0145 int transportId = -1; 0146 if (d->mCustomTransportId != -1) { 0147 transportId = d->mCustomTransportId; 0148 } else { 0149 if (auto hrd = message->headerByType("X-KMail-Transport")) { 0150 transportId = hrd->asUnicodeString().toInt(); 0151 } 0152 } 0153 const auto *transport = MailTransport::TransportManager::self()->transportById(transportId); 0154 if (!transport) { 0155 qCDebug(MESSAGECOMPOSER_LOG) << " No transport defined. Need to create it"; 0156 qjob->deleteLater(); 0157 return; 0158 } 0159 0160 if ((method == MessageComposer::MessageSender::SendImmediate) && !MessageComposer::Util::sendMailDispatcherIsOnline()) { 0161 qjob->deleteLater(); 0162 return; 0163 } 0164 0165 qCDebug(MESSAGECOMPOSER_LOG) << "Using transport (" << transport->name() << "," << transport->id() << ")"; 0166 qjob->transportAttribute().setTransportId(transport->id()); 0167 0168 // if we want to manually queue it for sending later, then do it 0169 if (method == MessageComposer::MessageSender::SendLater) { 0170 qjob->dispatchModeAttribute().setDispatchMode(Akonadi::DispatchModeAttribute::Manual); 0171 } 0172 0173 // Get addresses. 0174 QStringList to; 0175 QStringList cc; 0176 QStringList bcc; 0177 QString from; 0178 extractSenderToCCAndBcc(message, from, to, cc, bcc); 0179 qjob->addressAttribute().setFrom(from); 0180 qjob->addressAttribute().setTo(to); 0181 qjob->addressAttribute().setCc(cc); 0182 qjob->addressAttribute().setBcc(bcc); 0183 0184 if (qjob->addressAttribute().to().isEmpty()) { 0185 qCWarning(MESSAGECOMPOSER_LOG) << " Impossible to specify TO! It's a bug"; 0186 qjob->deleteLater(); 0187 return; 0188 } 0189 0190 if (transport && transport->specifySenderOverwriteAddress()) { 0191 qjob->addressAttribute().setFrom( 0192 KEmailAddress::extractEmailAddress(KEmailAddress::normalizeAddressesAndEncodeIdn(transport->senderOverwriteAddress()))); 0193 } else { 0194 qjob->addressAttribute().setFrom(KEmailAddress::extractEmailAddress(KEmailAddress::normalizeAddressesAndEncodeIdn(message->from()->asUnicodeString()))); 0195 } 0196 0197 MessageComposer::Util::addSendReplyForwardAction(message, qjob); 0198 0199 MessageCore::StringUtil::removePrivateHeaderFields(message, false); 0200 message->assemble(); 0201 0202 // Queue the message. 0203 connect(qjob, &Akonadi::MessageQueueJob::result, this, &AkonadiSender::queueJobResult); 0204 d->mPendingJobs.insert(qjob); 0205 qjob->start(); 0206 qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob started."; 0207 0208 // TODO potential problem: 0209 // The MDA finishes sending a message before I queue the next one, and 0210 // thinking it is finished, the progress item deletes itself. 0211 // Turn the MDA offline until everything is queued? 0212 } 0213 0214 void AkonadiSender::queueJobResult(KJob *job) 0215 { 0216 Q_ASSERT(d->mPendingJobs.contains(job)); 0217 d->mPendingJobs.remove(job); 0218 0219 if (job->error()) { 0220 qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob failed with error" << job->errorString(); 0221 } else { 0222 qCDebug(MESSAGECOMPOSER_LOG) << "QueueJob success."; 0223 } 0224 } 0225 0226 #include "moc_akonadisender.cpp"