File indexing completed on 2025-01-19 03:41:33
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"