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: