File indexing completed on 2024-12-22 04:55:36
0001 /* 0002 * sendakonadimail.cpp - sends email using MailTransport library 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2010-2022 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "sendakonadimail.h" 0010 0011 #include "akonadiplugin_debug.h" 0012 0013 #include <KIdentityManagementCore/Identity> 0014 #include <MailTransport/TransportManager> 0015 #include <MailTransport/Transport> 0016 #include <Akonadi/MessageQueueJob> 0017 0018 #include <KEmailAddress> 0019 #include <KLocalizedString> 0020 0021 using namespace MailSend; 0022 0023 namespace 0024 { 0025 QStringList extractEmailsAndNormalize(const QString& emailAddresses); 0026 } 0027 0028 SendAkonadiMail* SendAkonadiMail::mInstance = nullptr; // used only to enable signals/slots to work 0029 QQueue<Akonadi::MessageQueueJob*> SendAkonadiMail::mJobs; 0030 QQueue<JobData> SendAkonadiMail::mJobData; 0031 0032 SendAkonadiMail* SendAkonadiMail::instance() 0033 { 0034 if (!mInstance) 0035 mInstance = new SendAkonadiMail(); 0036 return mInstance; 0037 } 0038 0039 /****************************************************************************** 0040 * Send an email message. 0041 * Reply = empty string if the message is queued for sending, 0042 * = error message if the message was not sent. 0043 */ 0044 QString SendAkonadiMail::send(KMime::Message::Ptr message, const KIdentityManagementCore::Identity& identity, 0045 const QString& normalizedFrom, bool keepSentMail, JobData& jobdata) 0046 { 0047 qCDebug(AKONADIPLUGIN_LOG) << "SendAkonadiMail::send: Sending via KDE"; 0048 MailTransport::TransportManager* manager = MailTransport::TransportManager::self(); 0049 const int transportId = identity.transport().isEmpty() ? -1 : identity.transport().toInt(); 0050 MailTransport::Transport* transport = manager->transportById(transportId, true); 0051 if (!transport) 0052 { 0053 qCCritical(AKONADIPLUGIN_LOG) << "SendAkonadiMail::send: No mail transport found for identity" << identity.identityName() << "uoid" << identity.uoid(); 0054 return xi18nc("@info", "No mail transport configured for email identity <resource>%1</resource>", identity.identityName()); 0055 } 0056 qCDebug(AKONADIPLUGIN_LOG) << "SendAkonadiMail::send: Using transport" << transport->name() << ", id=" << transport->id(); 0057 0058 auto mailjob = new Akonadi::MessageQueueJob(qApp); 0059 mailjob->setMessage(message); 0060 mailjob->transportAttribute().setTransportId(transport->id()); 0061 // MessageQueueJob email addresses must be pure, i.e. without display name. Note 0062 // that display names are included in the actual headers set up in 'message'. 0063 mailjob->addressAttribute().setFrom(normalizedFrom); 0064 mailjob->addressAttribute().setTo(extractEmailsAndNormalize(jobdata.event.emailAddresses(QStringLiteral(",")))); 0065 if (!jobdata.bcc.isEmpty()) 0066 mailjob->addressAttribute().setBcc(extractEmailsAndNormalize(jobdata.bcc)); 0067 Akonadi::SentBehaviourAttribute::SentBehaviour sentAction = 0068 keepSentMail ? Akonadi::SentBehaviourAttribute::MoveToDefaultSentCollection 0069 : Akonadi::SentBehaviourAttribute::Delete; 0070 mailjob->sentBehaviourAttribute().setSentBehaviour(sentAction); 0071 mJobs.enqueue(mailjob); 0072 mJobData.enqueue(jobdata); 0073 if (mJobs.count() == 1) 0074 { 0075 // There are no jobs already active or queued, so send now 0076 connect(mailjob, &KJob::result, instance(), &SendAkonadiMail::slotEmailSent); 0077 mailjob->start(); 0078 } 0079 return {}; 0080 } 0081 0082 /****************************************************************************** 0083 * Called when sending an email is complete. 0084 */ 0085 void SendAkonadiMail::slotEmailSent(KJob* job) 0086 { 0087 bool sendError = false; 0088 QStringList errmsgs; 0089 if (job->error()) 0090 { 0091 qCCritical(AKONADIPLUGIN_LOG) << "SendAkonadiMail::slotEmailSent: Failed:" << job->errorString(); 0092 errmsgs += job->errorString(); 0093 sendError = true; 0094 } 0095 JobData jobdata; 0096 if (mJobs.isEmpty() || mJobData.isEmpty() || job != std::as_const(mJobs).head()) 0097 { 0098 // The queue has been corrupted, so we can't locate the job's data 0099 qCCritical(AKONADIPLUGIN_LOG) << "SendAkonadiMail::slotEmailSent: Wrong job at head of queue: wiping queue"; 0100 mJobs.clear(); 0101 mJobData.clear(); 0102 if (!errmsgs.isEmpty()) 0103 Q_EMIT sent(jobdata, errmsgs, sendError); 0104 errmsgs.clear(); 0105 errmsgs += i18nc("@info", "Emails may not have been sent"); 0106 errmsgs += i18nc("@info", "Program error"); 0107 Q_EMIT sent(jobdata, errmsgs, false); 0108 return; 0109 } 0110 mJobs.dequeue(); 0111 jobdata = mJobData.dequeue(); 0112 if (jobdata.allowNotify) 0113 Q_EMIT queued(jobdata.event); 0114 Q_EMIT sent(jobdata, errmsgs, sendError); 0115 if (!mJobs.isEmpty()) 0116 { 0117 // Send the next queued email 0118 auto job1 = mJobs.head(); 0119 connect(job1, &KJob::result, instance(), &SendAkonadiMail::slotEmailSent); 0120 job1->start(); 0121 } 0122 } 0123 0124 namespace 0125 { 0126 0127 QStringList extractEmailsAndNormalize(const QString& emailAddresses) 0128 { 0129 const QStringList splitEmails(KEmailAddress::splitAddressList(emailAddresses)); 0130 QStringList normalizedEmail; 0131 normalizedEmail.reserve(splitEmails.count()); 0132 for (const QString& email : splitEmails) 0133 { 0134 normalizedEmail << KEmailAddress::extractEmailAddress(KEmailAddress::normalizeAddressesAndEncodeIdn(email)); 0135 } 0136 return normalizedEmail; 0137 } 0138 0139 } // namespace 0140 0141 #include "moc_sendakonadimail.cpp" 0142 0143 // vim: et sw=4: