File indexing completed on 2024-12-15 04:52:24

0001 /*
0002     Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com>
0003 
0004     This library is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This library is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to the
0016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301, USA.
0018 */
0019 #include "extensionapi.h"
0020 
0021 #include <KMime/KMimeMessage>
0022 #include <QStandardPaths>
0023 #include <QDataStream>
0024 #include <QSettings>
0025 #include <QVariantMap>
0026 #include <QVariant>
0027 #include <QMap>
0028 
0029 #include <sink/store.h>
0030 #include <sink/log.h>
0031 
0032 #include <mailtemplates.h>
0033 #include <sink/crypto.h>
0034 
0035 static void send(const QByteArray &message, const QByteArray &accountId)
0036 {
0037     using namespace Sink;
0038     using namespace Sink::ApplicationDomain;
0039 
0040     Q_ASSERT(!accountId.isEmpty());
0041     Query query;
0042     query.containsFilter<SinkResource::Capabilities>(ResourceCapabilities::Mail::transport);
0043     query.filter<SinkResource::Account>(accountId);
0044     auto job = Store::fetchAll<SinkResource>(query)
0045         .then([=](const QList<SinkResource::Ptr> &resources) {
0046             if (!resources.isEmpty()) {
0047                 auto resourceId = resources[0]->identifier();
0048                 SinkLog() << "Sending message via resource: " << resourceId;
0049                 Mail mail(resourceId);
0050                 mail.setMimeMessage(message);
0051                 return Store::create(mail)
0052                     .then<void>([=] {
0053                         //Trigger a sync, but don't wait for it.
0054                         Store::synchronize(Sink::SyncScope{}.resourceFilter(resourceId)).exec();
0055                     });
0056             }
0057             SinkWarning() << "Failed to find a mailtransport resource";
0058             return KAsync::error("Failed to find a MailTransport resource.");
0059         })
0060         .then([&] (const KAsync::Error &) {
0061             SinkLog() << "Message was sent: ";
0062         });
0063     job.exec();
0064 }
0065 
0066 static QStringList toStringList(const QVariantList &list)
0067 {
0068     QStringList s;
0069     for (const auto &e : list) {
0070         s << e.toString();
0071     }
0072     return s;
0073 }
0074 
0075 Q_INVOKABLE void ExtensionApi::forwardMail(const QVariantMap &map)
0076 {
0077     SinkLog() << "Forwarding mail " << map;
0078     auto mailObject = map.value("mail").value<Sink::ApplicationDomain::Mail::Ptr>();
0079     Q_ASSERT(mailObject);
0080     KMime::Message::Ptr msg(new KMime::Message);
0081     msg->setContent(KMime::CRLFtoLF(mailObject->getMimeMessage()));
0082     msg->parse();
0083 
0084     MailTemplates::forward(msg, [map] (const KMime::Message::Ptr &fwdMessage) {
0085         auto msg = fwdMessage;
0086         msg->subject()->fromUnicodeString(map.value("subject").toString(), "utf8");
0087         auto list = toStringList(map.value("to").toList());
0088         for (const auto &address : list) {
0089             KMime::Types::Mailbox mb;
0090             mb.fromUnicodeString(address);
0091             msg->to()->addAddress(mb);
0092         }
0093         msg->assemble();
0094         send(msg->encodedContent(true), map.value("accountId").toByteArray());
0095     });
0096 }
0097 
0098 void ExtensionApi::storeSecret(const QByteArray &accountId, const QByteArray &keyId, const QVariantMap &secret)
0099 {
0100     QByteArray secretBA;
0101     QDataStream stream(&secretBA, QIODevice::WriteOnly);
0102     stream << secret;
0103     auto result = Crypto::signAndEncrypt(secretBA, Crypto::findKeys({{keyId}}, true), {});
0104     if (result) {
0105         QSettings settings(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString("/kube/secrets.ini"), QSettings::IniFormat);
0106         settings.setValue(accountId, result.value());
0107     } else {
0108         SinkWarning() << "Failed to encrypt account secret " << accountId << keyId;
0109     }
0110 }
0111 
0112 void ExtensionApi::loadSecret(const QByteArray &accountId)
0113 {
0114     QSettings settings(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString("/kube/secrets.ini"), QSettings::IniFormat);
0115 
0116     QByteArray secretBA;
0117     decryptAndVerify(Crypto::OpenPGP, settings.value(accountId).value<QByteArray>(), secretBA);
0118 
0119     QVariantMap map;
0120     QDataStream stream(&secretBA, QIODevice::ReadOnly);
0121     stream >> map;
0122 
0123     emit secretAvailable(accountId, map);
0124 }