File indexing completed on 2024-04-14 03:53:16

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2021 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "kemailclientlauncherjob.h"
0009 
0010 #include <KApplicationTrader>
0011 #include <KConfigGroup>
0012 #include <KLocalizedString>
0013 #include <KMacroExpander>
0014 #include <KService>
0015 #include <KSharedConfig>
0016 #include <KShell>
0017 #include <QProcessEnvironment>
0018 #include <QUrlQuery>
0019 
0020 #include "desktopexecparser.h"
0021 #include <KIO/ApplicationLauncherJob>
0022 #include <KIO/CommandLauncherJob>
0023 
0024 #ifdef Q_OS_WIN
0025 #include <windows.h> // Must be included before shellapi.h
0026 
0027 #include <shellapi.h>
0028 #endif
0029 
0030 class KEMailClientLauncherJobPrivate
0031 {
0032 public:
0033     QStringList m_to;
0034     QStringList m_cc;
0035     QStringList m_bcc;
0036     QString m_subject;
0037     QString m_body;
0038     QList<QUrl> m_attachments;
0039 
0040     QByteArray m_startupId;
0041 };
0042 
0043 KEMailClientLauncherJob::KEMailClientLauncherJob(QObject *parent)
0044     : KJob(parent)
0045     , d(new KEMailClientLauncherJobPrivate)
0046 {
0047 }
0048 
0049 KEMailClientLauncherJob::~KEMailClientLauncherJob() = default;
0050 
0051 void KEMailClientLauncherJob::setTo(const QStringList &to)
0052 {
0053     d->m_to = to;
0054 }
0055 
0056 void KEMailClientLauncherJob::setCc(const QStringList &cc)
0057 {
0058     d->m_cc = cc;
0059 }
0060 
0061 void KEMailClientLauncherJob::setBcc(const QStringList &bcc)
0062 {
0063     d->m_bcc = bcc;
0064 }
0065 
0066 void KEMailClientLauncherJob::setSubject(const QString &subject)
0067 {
0068     d->m_subject = subject;
0069 }
0070 
0071 void KEMailClientLauncherJob::setBody(const QString &body)
0072 {
0073     d->m_body = body;
0074 }
0075 
0076 void KEMailClientLauncherJob::setAttachments(const QList<QUrl> &urls)
0077 {
0078     d->m_attachments = urls;
0079 }
0080 
0081 void KEMailClientLauncherJob::setStartupId(const QByteArray &startupId)
0082 {
0083     d->m_startupId = startupId;
0084 }
0085 
0086 void KEMailClientLauncherJob::start()
0087 {
0088 #ifndef Q_OS_WIN
0089     KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("x-scheme-handler/mailto"));
0090     if (!service) {
0091         setError(KJob::UserDefinedError);
0092         setErrorText(i18n("No mail client found"));
0093         emitDelayedResult();
0094         return;
0095     }
0096     const QString entryPath = service->entryPath().toLower();
0097     if (entryPath.contains(QLatin1String("thunderbird")) || entryPath.contains(QLatin1String("dovecot"))) {
0098         const QString exec = KIO::DesktopExecParser::executableName(service->exec());
0099         auto *subjob = new KIO::CommandLauncherJob(exec, thunderbirdArguments(), this);
0100         subjob->setStartupId(d->m_startupId);
0101         connect(subjob, &KJob::result, this, &KEMailClientLauncherJob::emitResult);
0102         subjob->start();
0103     } else {
0104         auto *subjob = new KIO::ApplicationLauncherJob(service, this);
0105         subjob->setUrls({mailToUrl()});
0106         subjob->setStartupId(d->m_startupId);
0107         connect(subjob, &KJob::result, this, &KEMailClientLauncherJob::emitResult);
0108         subjob->start();
0109     }
0110 #else
0111     const QString url = mailToUrl().toString();
0112     const QString sOpen = QStringLiteral("open");
0113     ShellExecuteW(0, (LPCWSTR)sOpen.utf16(), (LPCWSTR)url.utf16(), 0, 0, SW_NORMAL);
0114     emitDelayedResult();
0115 #endif
0116 }
0117 
0118 void KEMailClientLauncherJob::emitDelayedResult()
0119 {
0120     // Use delayed invocation so the caller has time to connect to the signal
0121     QMetaObject::invokeMethod(this, &KEMailClientLauncherJob::emitResult, Qt::QueuedConnection);
0122 }
0123 
0124 QUrl KEMailClientLauncherJob::mailToUrl() const
0125 {
0126     QUrl url;
0127     QUrlQuery query;
0128     for (const QString &to : std::as_const(d->m_to)) {
0129         if (url.path().isEmpty()) {
0130             url.setPath(to);
0131         } else {
0132             query.addQueryItem(QStringLiteral("to"), to);
0133         }
0134     }
0135     for (const QString &cc : std::as_const(d->m_cc)) {
0136         query.addQueryItem(QStringLiteral("cc"), cc);
0137     }
0138     for (const QString &bcc : std::as_const(d->m_bcc)) {
0139         query.addQueryItem(QStringLiteral("bcc"), bcc);
0140     }
0141     for (const QUrl &url : std::as_const(d->m_attachments)) {
0142         query.addQueryItem(QStringLiteral("attach"), url.toString());
0143     }
0144     if (!d->m_subject.isEmpty()) {
0145         query.addQueryItem(QStringLiteral("subject"), d->m_subject);
0146     }
0147     if (!d->m_body.isEmpty()) {
0148         query.addQueryItem(QStringLiteral("body"), d->m_body);
0149     }
0150     url.setQuery(query);
0151     if (!url.path().isEmpty() || url.hasQuery()) {
0152         url.setScheme(QStringLiteral("mailto"));
0153     }
0154     return url;
0155 }
0156 
0157 QStringList KEMailClientLauncherJob::thunderbirdArguments() const
0158 {
0159     // Thunderbird supports mailto URLs, but refuses attachments for security reasons
0160     // (https://bugzilla.mozilla.org/show_bug.cgi?id=1613425)
0161     // It however supports a "command-line" syntax (also used by xdg-email)
0162     // which includes attachments.
0163     QString arg;
0164     const QChar quote = QLatin1Char('\'');
0165     auto addString = [&](const char *token, const QString &str) {
0166         if (!str.isEmpty()) {
0167             arg += QLatin1String(token) + quote + str + quote;
0168         }
0169     };
0170     auto addList = [&](const char *token, const QStringList &list) {
0171         if (!list.isEmpty()) {
0172             arg += QLatin1String(token) + quote + list.join(QLatin1Char(',')) + quote;
0173         }
0174     };
0175     addList(",to=", d->m_to);
0176     addList(",cc=", d->m_cc);
0177     addList(",bcc=", d->m_bcc);
0178     addList(",attachment=", QUrl::toStringList(d->m_attachments));
0179     addString(",subject=", d->m_subject);
0180     addString(",body=", d->m_body);
0181 
0182     QStringList resultArgs{QLatin1String("-compose")};
0183     if (!arg.isEmpty()) {
0184         resultArgs.push_back(arg.mid(1)); // remove first comma
0185     }
0186     return resultArgs;
0187 }
0188 
0189 #include "moc_kemailclientlauncherjob.cpp"